diff options
Diffstat (limited to 'kmymoney2/widgets')
105 files changed, 28777 insertions, 0 deletions
diff --git a/kmymoney2/widgets/Makefile.am b/kmymoney2/widgets/Makefile.am new file mode 100644 index 0000000..51fa159 --- /dev/null +++ b/kmymoney2/widgets/Makefile.am @@ -0,0 +1,95 @@ +KDE_OPTIONS = noautodist + +INCLUDES = $(all_includes) -I$(top_srcdir) -I. + +noinst_LIBRARIES = libwidgets.a + +libwidgets_a_METASOURCES = AUTO + +libwidgets_a_SOURCES = kbudgetvalues.cpp kbudgetvaluesdecl.ui kguiutils.cpp klistviewsearchline.cpp kmymoneyaccountcombo.cpp kmymoneyaccountcompletion.cpp kmymoneyaccountselector.cpp kmymoneyaccounttreebase.cpp kmymoneyaccounttree.cpp kmymoneyaccounttreebudget.cpp kmymoneyaccounttreeforecast.cpp kmymoneybriefschedule.cpp kmymoneycalculator.cpp kmymoneycalendar.cpp kmymoneycategory.cpp kmymoneychecklistitem.cpp kmymoneycombo.cpp kmymoneycompletion.cpp kmymoneycurrencyselector.cpp kmymoneydateinput.cpp kmymoneydatetbl.cpp kmymoneyedit.cpp kmymoneyforecastlistviewitem.cpp kmymoneygpgconfig.cpp kmymoneygpgconfigdecl.ui kmymoneylineedit.cpp kmymoneylistviewitem.cpp kmymoneyonlinequoteconfig.cpp kmymoneyonlinequoteconfigdecl.ui kmymoneypriceview.cpp kmymoneyreportconfigtab1decl.ui kmymoneyreportconfigtab2decl.ui kmymoneyreportconfigtab3decl.ui kmymoneyreportconfigtabchartdecl.ui kmymoneyreportcontroldecl.ui kmymoneyscheduledcalendar.cpp kmymoneyscheduleddatetbl.cpp kmymoneyselector.cpp kmymoneytitlelabel.cpp kmymoneywizard.cpp kschedulebriefwidget.ui register.cpp registeritem.cpp registersearchline.cpp transaction.cpp scheduledtransaction.cpp stdtransactiondownloaded.cpp stdtransactionmatched.cpp transactioneditorcontainer.cpp transactionform.cpp kaccounttemplateselectordecl.ui kaccounttemplateselector.cpp \ +transactionsortoption.cpp transactionsortoption.ui \ +selectedtransaction.cpp + +#libwidgets_a_SOURCES = kmymoneyregisterinvestment.cpp kmymoneyequity.cpp kmymoneyequitycompletion.cpp kmymoneycompletion.cpp kmymoneyequityselector.cpp kmymoneyaccountcombo.cpp kmymoneyaccountcompletion.cpp kmymoneycurrencyselector.cpp kmymoneypriceview.cpp kmymoneypriceviewdecl.ui kschedulebriefwidget.ui kmymoneyaccountselector.cpp kmymoneyregisterloan.cpp kmymoneyregistersearch.cpp kmymoneybriefschedule.cpp kmymoneyscheduleddatetbl.cpp kmymoneydatetbl.cpp kmymoneyscheduledcalendar.cpp kmymoneycalendar.cpp kmymoneycalculator.cpp kmymoneycategory.cpp kmymoneypayee.cpp kmymoneytransactionform.cpp kmymoneyregistercheckings.cpp kmymoneyregister.cpp kmymoneycombo.cpp kmymoneyhlayout.cpp kmymoneylineedit.cpp kmymoneyedit.cpp kmymoneydateinput.cpp kmymoneyreportcontroldecl.ui kmymoneyreportconfigtab1decl.ui kmymoneyreportconfigtab2decl.ui kmymoneyreportconfigtab3decl.ui kmymoneyreportconfigtabchartdecl.ui kmymoneyonlinequoteconfig.cpp kmymoneyonlinequoteconfigdecl.ui kmymoneyaccounttree.cpp kmymoneygpgconfigdecl.ui kmymoneygpgconfig.cpp kmymoneytitlelabel.cpp kguiutils.cpp kmymoneywizard.cpp kmymoneyaccounttreebudget.cpp kmymoneyaccounttreeforecast.cpp \ +#register.cpp registeritem.cpp transaction.cpp transactionform.cpp transactioneditor.cpp transactioneditorcontainer.cpp kmymoneychecklistitem.cpp kmymoneylistviewitem.cpp kmymoneyforecastlistviewitem.cpp kmymoneyselector.cpp \ +#transactionsortoption.ui + +EXTRA_DIST = kmymoney.widgets kmymoneygpgconfigdecl.ui kmymoneyonlinequoteconfigdecl.ui kmymoneyreportconfigtab1decl.ui kmymoneyreportconfigtab2decl.ui kmymoneyreportconfigtab3decl.ui kmymoneyreportcontroldecl.ui kschedulebriefwidget.ui kmymoneyreportconfigtabchartdecl.ui transactionsortoption.ui transactionsortoption.ui.h sortoptionlistitem.h makekdewidgets.in kbudgetvaluesdecl.ui kaccounttemplateselectordecl.ui + +# include the widgets that are available to 3rd party sw (e.g. plugins or +# Qt designer) here +# Note: The autogenerated file(s) are kept on a separate line so that no +# symbolic link is created during configure time (see configure.in.in) +# but the file is included in the tar-ball. See also the separate target +# $(top_builddir)/kmymoney/xxx.h further down and the BUILT_SOURCES target. +instdir=$(includedir)/kmymoney +inst_HEADERS = kmymoneydateinput.h kmymoneyedit.h kmymoneytitlelabel.h kmymoneyaccountselector.h kmymoneycategory.h kmymoneyaccounttreebase.h kmymoneyaccounttree.h kmymoneycurrencyselector.h kguiutils.h kmymoneywizard.h kmymoneyaccounttreebudget.h kmymoneyaccounttreeforecast.h kmymoneyaccountcombo.h register.h registeritem.h transaction.h scheduledtransaction.h stdtransactiondownloaded.h stdtransactionmatched.h selectedtransaction.h transactionform.h transactioneditorcontainer.h kmymoneylineedit.h kmymoneychecklistitem.h kmymoneylistviewitem.h kmymoneyforecastlistviewitem.h kmymoneyselector.h kmymoneyaccountcompletion.h kmymoneycompletion.h kmymoneycombo.h kbudgetvalues.h kaccounttemplateselector.h \ + transactionsortoption.h + +# include the widgets that are only available to KMyMoney here +noinst_HEADERS = kbudgetvalues.h klistviewsearchline.h kmymoneyaccounttree.h \ + kmymoneyaccounttreebudget.h kmymoneyaccounttreeforecast.h kmymoneybriefschedule.h kmymoneycalculator.h kmymoneycalendar.h \ + kmymoneycategory.h kmymoneydatetbl.h kmymoneygpgconfig.h kmymoneyonlinequoteconfig.h \ + kmymoneypriceview.h kmymoneyscheduledcalendar.h kmymoneyscheduleddatetbl.h \ + kmymoneywizard_p.h registersearchline.h + +# make sure, automatically generated files exist +BUILT_SOURCES = settings $(top_builddir)/kmymoney/transactionsortoption.h mocs dialogs transactionsortoption.cpp kbudgetvaluesdecl.h kaccounttemplateselectordecl.h libkmymoney.la kmmwidgets.cpp + +CLEANFILES = kmmwidgets.cpp + +.PHONY: dialogs settings +dialogs: + $(MAKE) -C ../dialogs kcurrencycalculatordecl.h + +settings: + $(MAKE) -C .. kmymoneysettings.h + +# we have to make sure, that the symbolic link exists +$(top_builddir)/kmymoney/transactionsortoption.h: transactionsortoption.h + if test -h $@; then rm $@; fi + ln -s `pwd`/transactionsortoption.h $@ + +PICS_DIR=kmymoney2/pics +WIDGET_PNGS = kmymoneytitlelabel.png + +libkmymoney_la_SOURCES = kmmwidgets.cpp ../kmymoneysettings.cpp kmymoneycompletion.cpp kmymoneyaccountcombo.cpp kmymoneyaccountcompletion.cpp kmymoneycurrencyselector.cpp kmymoneyaccountselector.cpp kmymoneydatetbl.cpp kmymoneycalculator.cpp kmymoneycategory.cpp kmymoneycombo.cpp kmymoneylineedit.cpp kmymoneyedit.cpp kmymoneydateinput.cpp kmymoneyaccounttree.cpp kmymoneytitlelabel.cpp kguiutils.cpp kmymoneyaccounttreebase.cpp kmymoneyaccounttreebudget.cpp kmymoneyaccounttreeforecast.cpp register.cpp registeritem.cpp transaction.cpp scheduledtransaction.cpp stdtransactiondownloaded.cpp stdtransactionmatched.cpp transactionform.cpp kmymoneychecklistitem.cpp kmymoneylistviewitem.cpp kmymoneyselector.cpp transactionsortoption.cpp kbudgetvalues.cpp kbudgetvaluesdecl.cpp kaccounttemplateselector.cpp kaccounttemplateselectordecl.cpp + +noinst_LTLIBRARIES = libkmymoney.la + +kmmwidgets.cpp: $(srcdir)/kmymoney.widgets + chmod +x makekdewidgets + ./makekdewidgets -g KMyMoney -n CustomWidgetPlugin -o kmmwidgets.cpp -i kmymoney $(abs_srcdir)/kmymoney.widgets + +# make sure to compile the Qt designer version w/o the memory leak checker +# also we need a copy of the mymoneymoney.lo file which has been compiled +# already. This is a quick hack around a bootstrap problem. We could link +# the libkmymoney.so against libkmm_mymoney.so but this is not yet installed +# but required to let UIC generate the right code for the dialogs and views. +# Hence the quick hack to include the required objects from the +# libkmm_mymoney.so directly into libkmymoney.so +libkmymoney_la_CXXFLAGS = -U_CHECK_MEMORY -DKMM_DESIGNER +libkmymoney_la_LDFLAGS = `ls ../mymoney/*.lo` $(KDE_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) -rpath $(DESTDIR)$(qt_libraries)/../plugins/designer $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) + +if INSTALL_QTDESIGNER_SUPPORT +install-exec-local: libkmymoney.la + $(mkinstalldirs) $(DESTDIR)$(qt_libraries)/../plugins/designer + $(INSTALL_PROGRAM) .libs/libkmymoney.so $(DESTDIR)$(qt_libraries)/../plugins/designer + $(mkinstalldirs) $(DESTDIR)$(kde_datadir)/$(PICS_DIR) + for file in $(WIDGET_PNGS); do \ + $(INSTALL_DATA) $(abs_srcdir)/$$file $(DESTDIR)$(kde_datadir)/$(PICS_DIR); \ + done + +uninstall-local: + -rm $(DESTDIR)$(qt_libraries)/../plugins/designer/libkmymoney.so + for file in $(WIDGET_PNGS); do \ + rm -rf $(DESTDIR)$(kde_datadir)/$(PICS_DIR)/$$file; \ + done +endif + +dist-hook: + -rm -rf $(distdir)/transactionsortoption.h + -rm -rf $(distdir)/transactionsortoption.cpp + -rm -rf $(distdir)/kmmwidgets.cpp + +messages: rc.cpp diff --git a/kmymoney2/widgets/kaccounttemplateselector.cpp b/kmymoney2/widgets/kaccounttemplateselector.cpp new file mode 100644 index 0000000..8ba6f0a --- /dev/null +++ b/kmymoney2/widgets/kaccounttemplateselector.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + kaccounttemplateselector.cpp - description + ------------------- + begin : Tue Feb 5 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdir.h> +#include <qheader.h> +#include <qtimer.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <klistview.h> +#include <ktextedit.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneytemplate.h> + +#include "kaccounttemplateselector.h" + +class KTemplateListItem : public KListViewItem +{ + public: + KTemplateListItem(QListViewItem* parent, const QString& text); + void setAvailable(void); + public: + bool m_isAvailable; +}; + +KTemplateListItem::KTemplateListItem(QListViewItem* parent, const QString& text) : + KListViewItem(parent, text), + m_isAvailable(false) +{ +} + +void KTemplateListItem::setAvailable(void) +{ + m_isAvailable = true; +} + +class KAccountTemplateSelector::Private +{ + public: + Private(KAccountTemplateSelector* p) { m_parent = p; } +#ifndef KMM_DESIGNER + QValueList<MyMoneyTemplate> selectedTemplates(void) const; + QListViewItem* hierarchyItem(const QString& parent, const QString& name); + void loadHierarchy(void); +#endif + + public: + KAccountTemplateSelector* m_parent; + QMap<QString, QListViewItem*> m_templateHierarchy; +#ifndef KMM_DESIGNER + QMap<QString, MyMoneyTemplate> m_templates; + QMap<QString, QString> countries; + QMap<QString, QString>::iterator it_m; + QStringList dirlist; + int id; +#endif +}; + + +#ifndef KMM_DESIGNER +QListViewItem* KAccountTemplateSelector::Private::hierarchyItem(const QString& parent, const QString& name) +{ + if(!m_templateHierarchy.contains(parent) + || m_templateHierarchy[parent] == 0) { + QRegExp exp("(.*):(.*)"); + if(exp.search(parent) != -1) + m_templateHierarchy[parent] = hierarchyItem(exp.cap(1), exp.cap(2)); + } + return new KTemplateListItem(m_templateHierarchy[parent], name); +} + +void KAccountTemplateSelector::Private::loadHierarchy(void) +{ + m_templateHierarchy.clear(); + QListViewItemIterator it(m_parent->m_groupList, QListViewItemIterator::Selected); + QListViewItem* it_v; + while((it_v = it.current()) != 0) { + m_templates[it_v->text(2)].hierarchy(m_templateHierarchy); + ++it; + } + + // I need to think about this some more. The code works and shows + // the current account hierarchy. It might be usefull, to show + // existing accounts dimmed and the new ones in bold or so. +#if 0 + + // add the hierarchy from the MyMoneyFile object + QValueList<MyMoneyAccount> aList; + QValueList<MyMoneyAccount>::const_iterator it_a; + MyMoneyFile* file = MyMoneyFile::instance(); + file->accountList(aList); + if(aList.count() > 0) { + m_templateHierarchy[file->accountToCategory(file->asset().id(), true)] = 0; + m_templateHierarchy[file->accountToCategory(file->liability().id(), true)] = 0; + m_templateHierarchy[file->accountToCategory(file->income().id(), true)] = 0; + m_templateHierarchy[file->accountToCategory(file->expense().id(), true)] = 0; + m_templateHierarchy[file->accountToCategory(file->equity().id(), true)] = 0; + } + + for(it_a = aList.begin(); it_a != aList.end(); ++it_a) { + m_templateHierarchy[file->accountToCategory((*it_a).id(), true)] = 0; + } +#endif + + m_parent->m_accountList->clear(); + QMap<QString, QListViewItem*>::iterator it_m; + + QRegExp exp("(.*):(.*)"); + for(it_m = m_templateHierarchy.begin(); it_m != m_templateHierarchy.end(); ++it_m) { + if(exp.search(it_m.key()) == -1) { + (*it_m) = new KListViewItem(m_parent->m_accountList, it_m.key()); + } else { + (*it_m) = hierarchyItem(exp.cap(1), exp.cap(2)); + } + (*it_m)->setOpen(true); + } + + m_parent->m_description->clear(); + if(m_parent->m_groupList->currentItem()) { + m_parent->m_description->setText(m_templates[m_parent->m_groupList->currentItem()->text(2)].longDescription()); + } +} + +QValueList<MyMoneyTemplate> KAccountTemplateSelector::Private::selectedTemplates(void) const +{ + QValueList<MyMoneyTemplate> list; + QListViewItemIterator it(m_parent->m_groupList, QListViewItemIterator::Selected); + QListViewItem* it_v; + while((it_v = it.current()) != 0) { + list << m_templates[it_v->text(2)]; + ++it; + } + return list; +} +#endif + + +KAccountTemplateSelector::KAccountTemplateSelector(QWidget* parent, const char* name) : + KAccountTemplateSelectorDecl(parent, name), + d(new Private(this)) +{ + m_accountList->header()->hide(); + connect(m_groupList, SIGNAL(selectionChanged()), this, SLOT(slotLoadHierarchy())); + + // kick off loading of account template data + QTimer::singleShot(0, this, SLOT(slotLoadTemplateList())); +} + +KAccountTemplateSelector::~KAccountTemplateSelector() +{ + delete d; +} + +void KAccountTemplateSelector::slotLoadTemplateList(void) +{ +#ifndef KMM_DESIGNER + QStringList dirs; + // get list of template subdirs and scan them for the list of subdirs + d->dirlist = KGlobal::dirs()->findDirs("appdata", "templates"); + QStringList::iterator it; + for(it = d->dirlist.begin(); it != d->dirlist.end(); ++it) { + QDir dir(*it); + // qDebug("Reading dir '%s' with %d entries", (*it).data(), dir.count()); + dirs = dir.entryList("*", QDir::Dirs); + QStringList::iterator it_d; + for(it_d= dirs.begin(); it_d != dirs.end(); ++it_d) { + // we don't care about . and .. + if((*it_d) == ".." || (*it_d) == "." || (*it_d) == "C") + continue; + QRegExp exp("(..)_(..)"); + if(exp.search(*it_d) != -1) { + QString country = KGlobal::locale()->twoAlphaToCountryName(exp.cap(2)); + if(country.isEmpty()) + country = exp.cap(2); + QString lang = KGlobal::locale()->twoAlphaToLanguageName(exp.cap(1)); + if(d->countries.contains(country)) { + if(d->countries[country] != *it_d) { + QString oName = d->countries[country]; + exp.search(oName); + QString oCountry = KGlobal::locale()->twoAlphaToCountryName(exp.cap(2)); + QString oLang = KGlobal::locale()->twoAlphaToLanguageName(exp.cap(1)); + d->countries.remove(country); + d->countries[QString("%1 (%2)").arg(oCountry).arg(oLang)] = oName; + d->countries[QString("%1 (%2)").arg(country).arg(lang)] = *it_d; + } + } else { + d->countries[country] = *it_d; + } + } else if((*it_d).length() == 2) { + QString country = KGlobal::locale()->twoAlphaToCountryName((*it_d).upper()); + d->countries[country] = *it_d; + } else { + qDebug("'%s/%s' not scanned", (*it).data(), (*it_d).data()); + } + } + } + + // now that we know, what we can get at max, we scan everything + // and parse the templates into memory + m_groupList->clear(); + d->m_templates.clear(); + d->it_m = d->countries.begin(); + d->id = 1; + QTimer::singleShot(0, this, SLOT(slotLoadCountry())); +#endif +} + +void KAccountTemplateSelector::slotLoadCountry(void) +{ +#ifndef KMM_DESIGNER + + KListViewItem* parent = new KListViewItem(m_groupList, d->it_m.key()); + parent->setSelectable(false); + QStringList::iterator it; + for(it = d->dirlist.begin(); it != d->dirlist.end(); ++it) { + QStringList::iterator it_f; + QDir dir(QString("%1%2").arg(*it).arg(*(d->it_m))); + if(dir.exists()) { + QStringList files = dir.entryList("*", QDir::Files); + for(it_f = files.begin(); it_f != files.end(); ++it_f) { + MyMoneyTemplate templ(QString("%1/%2").arg(dir.canonicalPath()).arg(*it_f)); + d->m_templates[QString("%1").arg(d->id)] = templ; + new KListViewItem(parent, templ.title(), templ.shortDescription(), QString("%1").arg(d->id)); + ++d->id; + } + } + } + + ++d->it_m; + if(d->it_m != d->countries.end()) + QTimer::singleShot(0, this, SLOT(slotLoadCountry())); + else { + d->loadHierarchy(); + } +#endif + +} + +void KAccountTemplateSelector::slotLoadHierarchy(void) +{ +#ifndef KMM_DESIGNER + d->loadHierarchy(); +#endif +} + +QValueList<MyMoneyTemplate> KAccountTemplateSelector::selectedTemplates(void) const +{ +#ifndef KMM_DESIGNER + return d->selectedTemplates(); +#else + return QValueList<MyMoneyTemplate>(); +#endif +} + +#include "kaccounttemplateselector.moc" diff --git a/kmymoney2/widgets/kaccounttemplateselector.h b/kmymoney2/widgets/kaccounttemplateselector.h new file mode 100644 index 0000000..df2c3fd --- /dev/null +++ b/kmymoney2/widgets/kaccounttemplateselector.h @@ -0,0 +1,57 @@ +/*************************************************************************** + kaccounttemplateselector.h - description + ------------------- + begin : Tue Feb 5 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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 KACCOUNTTEMPLATESELECTOR_H +#define KACCOUNTTEMPLATESELECTOR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney2/widgets/kaccounttemplateselectordecl.h> +class MyMoneyTemplate; + +/** + * @author Thomas Baumgart <ipwizard@users.sourceforge.net> + */ +class KAccountTemplateSelector : public KAccountTemplateSelectorDecl +{ + Q_OBJECT + public: + KAccountTemplateSelector(QWidget* parent = 0, const char* name = 0); + ~KAccountTemplateSelector(); + + QValueList<MyMoneyTemplate> selectedTemplates(void) const; + + private slots: + void slotLoadHierarchy(void); + void slotLoadCountry(void); + void slotLoadTemplateList(void); + + private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + +#endif diff --git a/kmymoney2/widgets/kaccounttemplateselectordecl.cpp b/kmymoney2/widgets/kaccounttemplateselectordecl.cpp new file mode 100644 index 0000000..8fed754 --- /dev/null +++ b/kmymoney2/widgets/kaccounttemplateselectordecl.cpp @@ -0,0 +1,111 @@ +#include <kdialog.h> +#include <klocale.h> +#ifndef KMM_I18N +#define KMM_I18N +inline QString kmm_i18n(const char* msg, const char* ctx) { return i18n(ctx, msg); } +inline QString kmm_i18n(const char* msg) { return i18n(msg); } +#endif +/**************************************************************************** +** Form implementation generated from reading ui file '../../../kmymoney2/widgets/kaccounttemplateselectordecl.ui' +** +** Created: Fri Feb 12 15:05:39 2010 +** +** WARNING! All changes made in this file will be lost! +****************************************************************************/ + +#include "kaccounttemplateselectordecl.h" + +#include <qvariant.h> +#include <qpushbutton.h> +#include <qheader.h> +#include <klistview.h> +#include <qgroupbox.h> +#include <ktextedit.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +/* + * Constructs a KAccountTemplateSelectorDecl as a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + */ +KAccountTemplateSelectorDecl::KAccountTemplateSelectorDecl( QWidget* parent, const char* name, WFlags fl ) + : QWidget( parent, name, fl ) +{ + if ( !name ) + setName( "KAccountTemplateSelectorDecl" ); + KAccountTemplateSelectorDeclLayout = new QVBoxLayout( this, 0, 6, "KAccountTemplateSelectorDeclLayout"); + + m_groupList = new KListView( this, "m_groupList" ); + m_groupList->addColumn( kmm_i18n( "Account Types" ) ); + m_groupList->addColumn( kmm_i18n( "Description" ) ); + m_groupList->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)7, 0, 0, m_groupList->sizePolicy().hasHeightForWidth() ) ); + m_groupList->setMinimumSize( QSize( 0, 150 ) ); + m_groupList->setProperty( "selectionMode", "Extended" ); + m_groupList->setAllColumnsShowFocus( TRUE ); + m_groupList->setRootIsDecorated( TRUE ); + m_groupList->setFullWidth( TRUE ); + KAccountTemplateSelectorDeclLayout->addWidget( m_groupList ); + + layout3 = new QHBoxLayout( 0, 0, 6, "layout3"); + + groupBox1 = new QGroupBox( this, "groupBox1" ); + groupBox1->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)5, 0, 2, groupBox1->sizePolicy().hasHeightForWidth() ) ); + groupBox1->setColumnLayout(0, Qt::Vertical ); + groupBox1->layout()->setSpacing( 6 ); + groupBox1->layout()->setMargin( 11 ); + groupBox1Layout = new QVBoxLayout( groupBox1->layout() ); + groupBox1Layout->setAlignment( Qt::AlignTop ); + + m_description = new KTextEdit( groupBox1, "m_description" ); + m_description->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)7, 0, 3, m_description->sizePolicy().hasHeightForWidth() ) ); + m_description->setReadOnly( TRUE ); + groupBox1Layout->addWidget( m_description ); + layout3->addWidget( groupBox1 ); + + groupBox2 = new QGroupBox( this, "groupBox2" ); + groupBox2->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)5, 1, 2, groupBox2->sizePolicy().hasHeightForWidth() ) ); + groupBox2->setColumnLayout(0, Qt::Vertical ); + groupBox2->layout()->setSpacing( 6 ); + groupBox2->layout()->setMargin( 11 ); + groupBox2Layout = new QVBoxLayout( groupBox2->layout() ); + groupBox2Layout->setAlignment( Qt::AlignTop ); + + m_accountList = new KListView( groupBox2, "m_accountList" ); + m_accountList->addColumn( kmm_i18n( "Name" ) ); + m_accountList->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)7, 0, 3, m_accountList->sizePolicy().hasHeightForWidth() ) ); + m_accountList->setMinimumSize( QSize( 0, 150 ) ); + m_accountList->setProperty( "selectionMode", "NoSelection" ); + m_accountList->setAllColumnsShowFocus( TRUE ); + m_accountList->setRootIsDecorated( TRUE ); + m_accountList->setFullWidth( TRUE ); + groupBox2Layout->addWidget( m_accountList ); + layout3->addWidget( groupBox2 ); + KAccountTemplateSelectorDeclLayout->addLayout( layout3 ); + languageChange(); + resize( QSize(546, 346).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); +} + +/* + * Destroys the object and frees any allocated resources + */ +KAccountTemplateSelectorDecl::~KAccountTemplateSelectorDecl() +{ + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void KAccountTemplateSelectorDecl::languageChange() +{ + m_groupList->header()->setLabel( 0, kmm_i18n( "Account Types" ) ); + m_groupList->header()->setLabel( 1, kmm_i18n( "Description" ) ); + groupBox1->setTitle( kmm_i18n( "Detailed description" ) ); + groupBox2->setTitle( kmm_i18n( "Accounts" ) ); + m_accountList->header()->setLabel( 0, kmm_i18n( "Name" ) ); +} + +#include "kaccounttemplateselectordecl.moc" diff --git a/kmymoney2/widgets/kaccounttemplateselectordecl.ui b/kmymoney2/widgets/kaccounttemplateselectordecl.ui new file mode 100644 index 0000000..6ed4b6d --- /dev/null +++ b/kmymoney2/widgets/kaccounttemplateselectordecl.ui @@ -0,0 +1,188 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KAccountTemplateSelectorDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KAccountTemplateSelectorDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>546</width> + <height>346</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Account Types</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Description</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_groupList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>150</height> + </size> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Detailed description</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KTextEdit"> + <property name="name"> + <cstring>m_description</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>3</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>1</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Accounts</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_accountList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>3</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>150</height> + </size> + </property> + <property name="selectionMode" stdset="0"> + <enum>NoSelection</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kbudgetvalues.cpp b/kmymoney2/widgets/kbudgetvalues.cpp new file mode 100644 index 0000000..2d6c2d7 --- /dev/null +++ b/kmymoney2/widgets/kbudgetvalues.cpp @@ -0,0 +1,341 @@ +/*************************************************************************** + kbudgetvalues - description + ------------------- + begin : Wed Nov 28 2007 + copyright : (C) 2007 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtabwidget.h> +#include <qlabel.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qwidgetstack.h> +#include <qtimer.h> +#include <qtooltip.h> +#include <qapplication.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> +#include <kcalendarsystem.h> +#include <kmessagebox.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kbudgetvalues.h" +#include <kmymoney/kmymoneyedit.h> + +KBudgetValues::KBudgetValues(QWidget* parent, const char* name) : + KBudgetValuesDecl(parent, name), + m_currentTab(m_monthlyButton) +{ + m_budgetDate = QDate(2007,1,1); + + m_field[0] = m_amount1; + m_field[1] = m_amount2; + m_field[2] = m_amount3; + m_field[3] = m_amount4; + m_field[4] = m_amount5; + m_field[5] = m_amount6; + m_field[6] = m_amount7; + m_field[7] = m_amount8; + m_field[8] = m_amount9; + m_field[9] = m_amount10; + m_field[10] = m_amount11; + m_field[11] = m_amount12; + + m_label[0] = m_label1; + m_label[1] = m_label2; + m_label[2] = m_label3; + m_label[3] = m_label4; + m_label[4] = m_label5; + m_label[5] = m_label6; + m_label[6] = m_label7; + m_label[7] = m_label8; + m_label[8] = m_label9; + m_label[9] = m_label10; + m_label[10] = m_label11; + m_label[11] = m_label12; + + // fill with standard labels + m_monthlyButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_monthlyButton)); + + // connect(m_budgetLevel, SIGNAL(currentChanged(QWidget*)), this, SIGNAL(valuesChanged())); + connect(m_amountMonthly, SIGNAL(valueChanged(const QString&)), this, SLOT(slotNeedUpdate())); + connect(m_amountYearly, SIGNAL(valueChanged(const QString&)), this, SLOT(slotNeedUpdate())); + m_amountMonthly->installEventFilter(this); + m_amountYearly->installEventFilter(this); + + for(int i=0; i < 12; ++i) { + connect(m_field[i], SIGNAL(valueChanged(const QString&)), this, SLOT(slotNeedUpdate())); + m_field[i]->installEventFilter(this); + } + + connect(m_clearButton, SIGNAL(clicked()), this, SLOT(slotClearAllValues())); + connect(m_periodGroup, SIGNAL(clicked(int)), this, SLOT(slotChangePeriod(int))); + connect(this, SIGNAL(valuesChanged()), this, SLOT(slotUpdateClearButton())); + + KGuiItem clearItem(KStdGuiItem::clear()); + + m_clearButton->setGuiItem(clearItem); + m_clearButton->setText(""); + QToolTip::add(m_clearButton, clearItem.toolTip()); +} + + +KBudgetValues::~KBudgetValues() +{ +} + +bool KBudgetValues::eventFilter(QObject* o, QEvent* e) +{ + bool rc = false; + + if(o->isWidgetType() + && (e->type() == QEvent::KeyPress)) { + QKeyEvent* k = dynamic_cast<QKeyEvent*>(e); + if((k->state() & Qt::KeyButtonMask) == 0) { + QKeyEvent evt(e->type(), + Qt::Key_Tab, 0, k->state(), QString::null, + k->isAutoRepeat(), k->count()); + switch(k->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + // send out a TAB key event + QApplication::sendEvent( o, &evt ); + // don't process this one any further + rc = true; + break; + default: + break; + } + } + } + return rc; +} + +void KBudgetValues::clear(void) +{ + blockSignals(true); + for(int i=0; i < 12; ++i) + m_field[i]->setValue(MyMoneyMoney()); + m_amountMonthly->setValue(MyMoneyMoney()); + m_amountYearly->setValue(MyMoneyMoney()); + blockSignals(false); +} + +void KBudgetValues::slotClearAllValues(void) +{ + QWidget* tab = m_periodGroup->selected(); + if(tab == m_monthlyButton) { + m_amountMonthly->setValue(MyMoneyMoney()); + } else if(tab == m_yearlyButton) { + m_amountYearly->setValue(MyMoneyMoney()); + } else if(tab == m_individualButton) { + for(int i=0; i < 12; ++i) + m_field[i]->setValue(MyMoneyMoney()); + } + emit valuesChanged(); +} + +void KBudgetValues::slotChangePeriod(int id) +{ + // Prevent a recursive entry of this method due to widget changes + // performed during execution of this method + static bool inside = false; + if(inside) + return; + inside = true; + + QWidget *tab = m_periodGroup->find(id); + fillMonthLabels(); + + MyMoneyMoney newValue; + if(tab == m_monthlyButton) { + m_firstItemStack->raiseWidget(m_monthlyPage); + enableMonths(false); + m_label[0]->setText(" "); + if(m_amountMonthly->value().isZero()) { + if(m_currentTab == m_yearlyButton) { + newValue = (m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); + + } else if(m_currentTab == m_individualButton) { + for(int i=0; i < 12; ++i) + newValue += m_field[i]->value(); + newValue = (newValue / MyMoneyMoney(12, 1)).convert(); + } + if(!newValue.isZero()) { + if(KMessageBox::questionYesNo(this, QString("<qt>")+i18n("You have entered budget values using a different base which would result in a monthly budget of <b>%1</b>. Should this value be used to fill the monthly budget?").arg(newValue.formatMoney("", 2))+QString("</qt>"), i18n("Auto assignment (caption)", "Auto assignment"), KStdGuiItem::yes(), KStdGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + m_amountMonthly->setValue(newValue); + } + } + } + + } else if(tab == m_yearlyButton) { + m_firstItemStack->raiseWidget(m_yearlyPage); + enableMonths(false); + m_label[0]->setText(" "); + if(m_amountYearly->value().isZero()) { + if(m_currentTab == m_monthlyButton) { + newValue = (m_amountMonthly->value() * MyMoneyMoney(12, 1)).convert(); + + } else if(m_currentTab == m_individualButton) { + for(int i=0; i < 12; ++i) + newValue += m_field[i]->value(); + } + if(!newValue.isZero()) { + if(KMessageBox::questionYesNo(this, QString("<qt>")+i18n("You have entered budget values using a different base which would result in a yearly budget of <b>%1</b>. Should this value be used to fill the monthly budget?").arg(newValue.formatMoney("", 2))+QString("</qt>"), i18n("Auto assignment (caption)", "Auto assignment"), KStdGuiItem::yes(), KStdGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + m_amountYearly->setValue(newValue); + } + } + } + + } else if(tab == m_individualButton) { + m_firstItemStack->raiseWidget(m_individualPage); + enableMonths(true); + for(int i=0; i < 12; ++i) + newValue += m_field[i]->value(); + if(newValue.isZero()) { + if(m_currentTab == m_monthlyButton) { + newValue = m_amountMonthly->value(); + } else if(m_currentTab == m_yearlyButton) { + newValue = (m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); + } + + if(!newValue.isZero()) { + if(KMessageBox::questionYesNo(this, QString("<qt>")+i18n("You have entered budget values using a different base which would result in an individual monthly budget of <b>%1</b>. Should this value be used to fill the monthly budgets?").arg(newValue.formatMoney("", 2))+QString("</qt>"), i18n("Auto assignment (caption)", "Auto assignment"), KStdGuiItem::yes(), KStdGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + for(int i=0; i < 12; ++i) + m_field[i]->setValue(newValue); + } + } + } + } + + slotNeedUpdate(); + m_currentTab = tab; + inside = false; +} + +void KBudgetValues::slotNeedUpdate(void) +{ + if(!signalsBlocked()) + QTimer::singleShot(0, this, SIGNAL(valuesChanged())); +} + +void KBudgetValues::enableMonths(bool enabled) +{ + for(int i = 1; i < 12; ++i) { + m_label[i]->setEnabled(enabled); + m_field[i]->setEnabled(enabled); + } +} + +void KBudgetValues::fillMonthLabels(void) +{ + QDate date(m_budgetDate); + for(int i = 0; i < 12; ++i) { + m_label[i]->setText(KGlobal::locale()->calendar()->monthName(date, true)); + date = date.addMonths(1); + } +} + +void KBudgetValues::setBudgetValues(const MyMoneyBudget& budget, const MyMoneyBudget::AccountGroup& budgetAccount) +{ + MyMoneyBudget::PeriodGroup period; + m_budgetDate = budget.budgetStart(); + QDate date; + + // make sure all values are zero so that slotChangePeriod() + // doesn't check for anything. + clear(); + + blockSignals(true); + switch(budgetAccount.budgetLevel()) { + case MyMoneyBudget::AccountGroup::eMonthly: + default: + m_monthlyButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_monthlyButton)); + m_amountMonthly->setValue(budgetAccount.period(m_budgetDate).amount()); + break; + case MyMoneyBudget::AccountGroup::eYearly: + m_yearlyButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_yearlyButton)); + m_amountYearly->setValue(budgetAccount.period(m_budgetDate).amount()); + break; + case MyMoneyBudget::AccountGroup::eMonthByMonth: + m_individualButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_individualButton)); + date.setYMD(m_budgetDate.year(), 1, 1); + for(int i = 0; i < 12; ++i) { + m_field[i]->setValue(budgetAccount.period(date).amount()); + date = date.addMonths(1); + } + break; + } + slotUpdateClearButton(); + blockSignals(false); +} + +void KBudgetValues::budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount) +{ + MyMoneyBudget::PeriodGroup period; + m_budgetDate = budget.budgetStart(); + period.setStartDate(m_budgetDate); + QDate date; + + budgetAccount.clearPeriods(); + if(m_periodGroup->selected() == m_monthlyButton) { + budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthly); + period.setAmount(m_amountMonthly->value()); + budgetAccount.addPeriod(m_budgetDate, period); + } else if(m_periodGroup->selected() == m_yearlyButton) { + budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eYearly); + period.setAmount(m_amountYearly->value()); + budgetAccount.addPeriod(m_budgetDate, period); + } else if(m_periodGroup->selected() == m_individualButton) { + budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthByMonth); + date.setYMD(m_budgetDate.year(), 1, 1); + for(int i = 0; i < 12; ++i) { + period.setStartDate(date); + period.setAmount(m_field[i]->value()); + budgetAccount.addPeriod(date, period); + date = date.addMonths(1); + } + } +} + +void KBudgetValues::slotUpdateClearButton(void) +{ + bool rc = false; + if(m_periodGroup->selected() == m_monthlyButton) { + rc = !m_amountMonthly->value().isZero(); + } else if(m_periodGroup->selected() == m_yearlyButton) { + rc = !m_amountYearly->value().isZero(); + } else if(m_periodGroup->selected() == m_individualButton) { + for(int i = 0; (i < 12) && (rc == false); ++i) { + rc |= !m_field[i]->value().isZero(); + } + } + m_clearButton->setEnabled(rc); +} + +#include "kbudgetvalues.moc" diff --git a/kmymoney2/widgets/kbudgetvalues.h b/kmymoney2/widgets/kbudgetvalues.h new file mode 100644 index 0000000..5b8c2cc --- /dev/null +++ b/kmymoney2/widgets/kbudgetvalues.h @@ -0,0 +1,84 @@ +/*************************************************************************** + kbudgetvalues - description + ------------------- + begin : Wed Nov 28 2007 + copyright : (C) 2007 by Thomas Baumgart + email : Thomas Baumgart <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 KBUDGETVALUES_H +#define KBUDGETVALUES_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdatetime.h> +class QLabel; + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../widgets/kbudgetvaluesdecl.h" +#include <kmymoney/mymoneybudget.h> +class kMyMoneyEdit; + +/** + * @author Thomas Baumgart <ipwizard@users.sourceforge.net> + */ +class KBudgetValues : public KBudgetValuesDecl +{ + Q_OBJECT + public: + KBudgetValues(QWidget* parent = 0, const char* name = 0); + ~KBudgetValues(); + + void setBudgetValues(const MyMoneyBudget& budget, const MyMoneyBudget::AccountGroup& budgetAccount); + void budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount); + void clear(void); + + private: + void enableMonths(bool enabled); + void fillMonthLabels(void); + + protected slots: + void slotChangePeriod(int id); + + /** + * This slot clears the value in the value widgets of the selected budget type. + * Values of the other types are unaffected. + */ + void slotClearAllValues(void); + + /** + * Helper slot used to postpone sending the valuesChanged() signal. + */ + void slotNeedUpdate(void); + + void slotUpdateClearButton(void); + + protected: + bool eventFilter(QObject* o, QEvent* e); + + private: + kMyMoneyEdit* m_field[12]; + QLabel* m_label[12]; + QWidget* m_currentTab; + QDate m_budgetDate; + + signals: + void valuesChanged(void); +}; + +#endif diff --git a/kmymoney2/widgets/kbudgetvaluesdecl.cpp b/kmymoney2/widgets/kbudgetvaluesdecl.cpp new file mode 100644 index 0000000..43d71f5 --- /dev/null +++ b/kmymoney2/widgets/kbudgetvaluesdecl.cpp @@ -0,0 +1,257 @@ +#include <kdialog.h> +#include <klocale.h> +#ifndef KMM_I18N +#define KMM_I18N +inline QString kmm_i18n(const char* msg, const char* ctx) { return i18n(ctx, msg); } +inline QString kmm_i18n(const char* msg) { return i18n(msg); } +#endif +/**************************************************************************** +** Form implementation generated from reading ui file '../../../kmymoney2/widgets/kbudgetvaluesdecl.ui' +** +** Created: Fri Feb 12 15:05:38 2010 +** +** WARNING! All changes made in this file will be lost! +****************************************************************************/ + +#include "kbudgetvaluesdecl.h" + +#include <qvariant.h> +#include <kmymoney/kmymoneyedit.h> +#include <qpushbutton.h> +#include <qlabel.h> +#include <qwidgetstack.h> +#include <kpushbutton.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +/* + * Constructs a KBudgetValuesDecl as a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + */ +KBudgetValuesDecl::KBudgetValuesDecl( QWidget* parent, const char* name, WFlags fl ) + : QWidget( parent, name, fl ) +{ + if ( !name ) + setName( "KBudgetValuesDecl" ); + KBudgetValuesDeclLayout = new QGridLayout( this, 1, 1, 0, 6, "KBudgetValuesDeclLayout"); + + m_amount10 = new kMyMoneyEdit( this, "m_amount10" ); + m_amount10->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount10, 3, 5 ); + + m_amount6 = new kMyMoneyEdit( this, "m_amount6" ); + m_amount6->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount6, 5, 3 ); + + m_amount5 = new kMyMoneyEdit( this, "m_amount5" ); + m_amount5->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount5, 4, 3 ); + + m_label6 = new QLabel( this, "m_label6" ); + + KBudgetValuesDeclLayout->addWidget( m_label6, 5, 2 ); + + m_label8 = new QLabel( this, "m_label8" ); + + KBudgetValuesDeclLayout->addWidget( m_label8, 1, 4 ); + + m_amount9 = new kMyMoneyEdit( this, "m_amount9" ); + m_amount9->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount9, 2, 5 ); + + m_amount11 = new kMyMoneyEdit( this, "m_amount11" ); + m_amount11->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount11, 4, 5 ); + + m_label12 = new QLabel( this, "m_label12" ); + + KBudgetValuesDeclLayout->addWidget( m_label12, 5, 4 ); + + m_label10 = new QLabel( this, "m_label10" ); + + KBudgetValuesDeclLayout->addWidget( m_label10, 3, 4 ); + + m_label7 = new QLabel( this, "m_label7" ); + + KBudgetValuesDeclLayout->addWidget( m_label7, 0, 4 ); + + m_amount3 = new kMyMoneyEdit( this, "m_amount3" ); + m_amount3->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount3, 2, 3 ); + + m_label4 = new QLabel( this, "m_label4" ); + + KBudgetValuesDeclLayout->addWidget( m_label4, 3, 2 ); + + m_amount12 = new kMyMoneyEdit( this, "m_amount12" ); + m_amount12->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount12, 5, 5 ); + + m_label3 = new QLabel( this, "m_label3" ); + + KBudgetValuesDeclLayout->addWidget( m_label3, 2, 2 ); + + m_label1 = new QLabel( this, "m_label1" ); + + KBudgetValuesDeclLayout->addWidget( m_label1, 0, 2 ); + + m_label2 = new QLabel( this, "m_label2" ); + + KBudgetValuesDeclLayout->addWidget( m_label2, 1, 2 ); + + m_label5 = new QLabel( this, "m_label5" ); + + KBudgetValuesDeclLayout->addWidget( m_label5, 4, 2 ); + + m_label11 = new QLabel( this, "m_label11" ); + + KBudgetValuesDeclLayout->addWidget( m_label11, 4, 4 ); + + m_amount4 = new kMyMoneyEdit( this, "m_amount4" ); + m_amount4->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount4, 3, 3 ); + + m_label9 = new QLabel( this, "m_label9" ); + + KBudgetValuesDeclLayout->addWidget( m_label9, 2, 4 ); + + m_amount8 = new kMyMoneyEdit( this, "m_amount8" ); + m_amount8->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount8, 1, 5 ); + + m_amount2 = new kMyMoneyEdit( this, "m_amount2" ); + m_amount2->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount2, 1, 3 ); + + m_amount7 = new kMyMoneyEdit( this, "m_amount7" ); + m_amount7->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + + KBudgetValuesDeclLayout->addWidget( m_amount7, 0, 5 ); + + m_firstItemStack = new QWidgetStack( this, "m_firstItemStack" ); + + m_monthlyPage = new QWidget( m_firstItemStack, "m_monthlyPage" ); + m_monthlyPageLayout = new QHBoxLayout( m_monthlyPage, 0, 6, "m_monthlyPageLayout"); + + m_amountMonthly = new kMyMoneyEdit( m_monthlyPage, "m_amountMonthly" ); + m_amountMonthly->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + m_monthlyPageLayout->addWidget( m_amountMonthly ); + m_firstItemStack->addWidget( m_monthlyPage, 0 ); + + m_yearlyPage = new QWidget( m_firstItemStack, "m_yearlyPage" ); + m_yearlyPageLayout = new QHBoxLayout( m_yearlyPage, 0, 6, "m_yearlyPageLayout"); + + m_amountYearly = new kMyMoneyEdit( m_yearlyPage, "m_amountYearly" ); + m_amountYearly->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + m_yearlyPageLayout->addWidget( m_amountYearly ); + m_firstItemStack->addWidget( m_yearlyPage, 1 ); + + m_individualPage = new QWidget( m_firstItemStack, "m_individualPage" ); + m_individualPageLayout = new QHBoxLayout( m_individualPage, 0, 6, "m_individualPageLayout"); + + m_amount1 = new kMyMoneyEdit( m_individualPage, "m_amount1" ); + m_amount1->setProperty( "resetButtonVisibility", QVariant( FALSE, 0 ) ); + m_individualPageLayout->addWidget( m_amount1 ); + m_firstItemStack->addWidget( m_individualPage, 2 ); + + KBudgetValuesDeclLayout->addWidget( m_firstItemStack, 0, 3 ); + + layout12 = new QVBoxLayout( 0, 0, 6, "layout12"); + + m_clearButton = new KPushButton( this, "m_clearButton" ); + layout12->addWidget( m_clearButton ); + spacer7 = new QSpacerItem( 20, 21, QSizePolicy::Minimum, QSizePolicy::Expanding ); + layout12->addItem( spacer7 ); + + KBudgetValuesDeclLayout->addMultiCellLayout( layout12, 0, 5, 1, 1 ); + + m_periodGroup = new QButtonGroup( this, "m_periodGroup" ); + m_periodGroup->setColumnLayout(0, Qt::Vertical ); + m_periodGroup->layout()->setSpacing( 6 ); + m_periodGroup->layout()->setMargin( 11 ); + m_periodGroupLayout = new QVBoxLayout( m_periodGroup->layout() ); + m_periodGroupLayout->setAlignment( Qt::AlignTop ); + + m_monthlyButton = new QRadioButton( m_periodGroup, "m_monthlyButton" ); + m_periodGroupLayout->addWidget( m_monthlyButton ); + + m_yearlyButton = new QRadioButton( m_periodGroup, "m_yearlyButton" ); + m_periodGroupLayout->addWidget( m_yearlyButton ); + + m_individualButton = new QRadioButton( m_periodGroup, "m_individualButton" ); + m_periodGroupLayout->addWidget( m_individualButton ); + spacer10 = new QSpacerItem( 20, 21, QSizePolicy::Minimum, QSizePolicy::Expanding ); + m_periodGroupLayout->addItem( spacer10 ); + + KBudgetValuesDeclLayout->addMultiCellWidget( m_periodGroup, 0, 5, 0, 0 ); + languageChange(); + resize( QSize(553, 188).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); + + // tab order + setTabOrder( m_monthlyButton, m_yearlyButton ); + setTabOrder( m_yearlyButton, m_individualButton ); + setTabOrder( m_individualButton, m_clearButton ); + setTabOrder( m_clearButton, m_amount1 ); + setTabOrder( m_amount1, m_amountMonthly ); + setTabOrder( m_amountMonthly, m_amountYearly ); + setTabOrder( m_amountYearly, m_amount2 ); + setTabOrder( m_amount2, m_amount3 ); + setTabOrder( m_amount3, m_amount4 ); + setTabOrder( m_amount4, m_amount5 ); + setTabOrder( m_amount5, m_amount6 ); + setTabOrder( m_amount6, m_amount7 ); + setTabOrder( m_amount7, m_amount8 ); + setTabOrder( m_amount8, m_amount9 ); + setTabOrder( m_amount9, m_amount10 ); + setTabOrder( m_amount10, m_amount11 ); + setTabOrder( m_amount11, m_amount12 ); +} + +/* + * Destroys the object and frees any allocated resources + */ +KBudgetValuesDecl::~KBudgetValuesDecl() +{ + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void KBudgetValuesDecl::languageChange() +{ + m_label6->setText( kmm_i18n( "xxx" ) ); + m_label8->setText( kmm_i18n( "xxx" ) ); + m_label12->setText( kmm_i18n( "xxx" ) ); + m_label10->setText( kmm_i18n( "xxx" ) ); + m_label7->setText( kmm_i18n( "xxx" ) ); + m_label4->setText( kmm_i18n( "xxx" ) ); + m_label3->setText( kmm_i18n( "xxx" ) ); + m_label1->setText( kmm_i18n( "xxx" ) ); + m_label2->setText( kmm_i18n( "xxx" ) ); + m_label5->setText( kmm_i18n( "xxx" ) ); + m_label11->setText( kmm_i18n( "xxx" ) ); + m_label9->setText( kmm_i18n( "xxx" ) ); + m_clearButton->setText( QString::null ); + m_periodGroup->setTitle( kmm_i18n( "Period" ) ); + m_monthlyButton->setText( kmm_i18n( "Monthly" ) ); + m_yearlyButton->setText( kmm_i18n( "Yearly" ) ); + m_individualButton->setText( kmm_i18n( "Individual" ) ); +} + +#include "kbudgetvaluesdecl.moc" diff --git a/kmymoney2/widgets/kbudgetvaluesdecl.ui b/kmymoney2/widgets/kbudgetvaluesdecl.ui new file mode 100644 index 0000000..6870d64 --- /dev/null +++ b/kmymoney2/widgets/kbudgetvaluesdecl.ui @@ -0,0 +1,400 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>KBudgetValuesDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KBudgetValuesDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>553</width> + <height>188</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="kMyMoneyEdit" row="3" column="5"> + <property name="name"> + <cstring>m_amount10</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="kMyMoneyEdit" row="5" column="3"> + <property name="name"> + <cstring>m_amount6</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="kMyMoneyEdit" row="4" column="3"> + <property name="name"> + <cstring>m_amount5</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="5" column="2"> + <property name="name"> + <cstring>m_label6</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="1" column="4"> + <property name="name"> + <cstring>m_label8</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="kMyMoneyEdit" row="2" column="5"> + <property name="name"> + <cstring>m_amount9</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="kMyMoneyEdit" row="4" column="5"> + <property name="name"> + <cstring>m_amount11</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="5" column="4"> + <property name="name"> + <cstring>m_label12</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="3" column="4"> + <property name="name"> + <cstring>m_label10</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="0" column="4"> + <property name="name"> + <cstring>m_label7</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="kMyMoneyEdit" row="2" column="3"> + <property name="name"> + <cstring>m_amount3</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="3" column="2"> + <property name="name"> + <cstring>m_label4</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="kMyMoneyEdit" row="5" column="5"> + <property name="name"> + <cstring>m_amount12</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="2"> + <property name="name"> + <cstring>m_label3</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>m_label1</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>m_label2</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="4" column="2"> + <property name="name"> + <cstring>m_label5</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="QLabel" row="4" column="4"> + <property name="name"> + <cstring>m_label11</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="kMyMoneyEdit" row="3" column="3"> + <property name="name"> + <cstring>m_amount4</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="4"> + <property name="name"> + <cstring>m_label9</cstring> + </property> + <property name="text"> + <string>xxx</string> + </property> + </widget> + <widget class="kMyMoneyEdit" row="1" column="5"> + <property name="name"> + <cstring>m_amount8</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="kMyMoneyEdit" row="1" column="3"> + <property name="name"> + <cstring>m_amount2</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="kMyMoneyEdit" row="0" column="5"> + <property name="name"> + <cstring>m_amount7</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + <widget class="QWidgetStack" row="0" column="3"> + <property name="name"> + <cstring>m_firstItemStack</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>m_monthlyPage</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="kMyMoneyEdit"> + <property name="name"> + <cstring>m_amountMonthly</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>m_yearlyPage</cstring> + </property> + <attribute name="id"> + <number>1</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="kMyMoneyEdit"> + <property name="name"> + <cstring>m_amountYearly</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>m_individualPage</cstring> + </property> + <attribute name="id"> + <number>2</number> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="kMyMoneyEdit"> + <property name="name"> + <cstring>m_amount1</cstring> + </property> + <property name="resetButtonVisibility" stdset="0"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + </widget> + <widget class="QLayoutWidget" row="0" column="1" rowspan="6" colspan="1"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_clearButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QButtonGroup" row="0" column="0" rowspan="6" colspan="1"> + <property name="name"> + <cstring>m_periodGroup</cstring> + </property> + <property name="title"> + <string>Period</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_monthlyButton</cstring> + </property> + <property name="text"> + <string>Monthly</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_yearlyButton</cstring> + </property> + <property name="text"> + <string>Yearly</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>m_individualButton</cstring> + </property> + <property name="text"> + <string>Individual</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<tabstops> + <tabstop>m_monthlyButton</tabstop> + <tabstop>m_yearlyButton</tabstop> + <tabstop>m_individualButton</tabstop> + <tabstop>m_clearButton</tabstop> + <tabstop>m_amount1</tabstop> + <tabstop>m_amountMonthly</tabstop> + <tabstop>m_amountYearly</tabstop> + <tabstop>m_amount2</tabstop> + <tabstop>m_amount3</tabstop> + <tabstop>m_amount4</tabstop> + <tabstop>m_amount5</tabstop> + <tabstop>m_amount6</tabstop> + <tabstop>m_amount7</tabstop> + <tabstop>m_amount8</tabstop> + <tabstop>m_amount9</tabstop> + <tabstop>m_amount10</tabstop> + <tabstop>m_amount11</tabstop> + <tabstop>m_amount12</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in implementation">kmymoney/kmymoneyedit.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kguiutils.cpp b/kmymoney2/widgets/kguiutils.cpp new file mode 100644 index 0000000..c6157f9 --- /dev/null +++ b/kmymoney2/widgets/kguiutils.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** + kguiutils.cpp - description + ------------------- + begin : Fri Jan 27 2006 + copyright : (C) 2006 Tony Bloomfield + email : Tony Bloomfield <tonybloom@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + // No need for QDateEdit, QSpinBox, etc., since these always return values + +#include <qcheckbox.h> +#include <qlistbox.h> +#include <qcombobox.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qwidget.h> +#include <qhbox.h> +#include <qspinbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kguiutils.h" +#include "../kmymoneyglobalsettings.h" + + /************************************************************************** + * * + * The MandatoryFieldGroup code is courtesy of * + * Mark Summerfield in Qt Quarterly * + * http://doc.trolltech.com/qq/qq11-mandatoryfields.html * + * * + * Enhanced by Thomas Baumgart to support the lineedit field of a * + * a QComboBox. * + * * + **************************************************************************/ + +void kMandatoryFieldGroup::add(QWidget *widget) +{ + if (!widgets.contains(widget)) { + if (widget->inherits("QCheckBox")) + connect((QCheckBox*)widget->qt_cast("QCheckBox"), + SIGNAL(clicked()), + this, SLOT(changed())); + + else if (widget->inherits("QComboBox")) { + QComboBox* combo = (QComboBox*)widget->qt_cast("QComboBox"); + QLineEdit* lineedit = combo->lineEdit(); + if(lineedit) { + connect(lineedit, SIGNAL(textChanged(const QString&)), this, SLOT(changed())); + } else { + connect(combo, SIGNAL(highlighted(int)), this, SLOT(changed())); + } + } + + else if (widget->inherits("QLineEdit")) + connect((QLineEdit*)widget->qt_cast("QLineEdit"), + SIGNAL(textChanged(const QString&)), + this, SLOT(changed())); + + else if (widget->inherits("QSpinBox")) + connect((QSpinBox*)widget->qt_cast("QSpinBox"), + SIGNAL(valueChanged(const QString&)), + this, SLOT(changed())); + + else if (widget->inherits("QListBox")) + connect((QListBox*)widget->qt_cast("QListBox"), + SIGNAL(selectionChanged()), + this, SLOT(changed())); + + else { + qWarning("MandatoryFieldGroup: unsupported class %s", + widget->className()); + return; + } + + widget->setPaletteBackgroundColor(KMyMoneyGlobalSettings::requiredFieldColor()); + widgets.append(widget); + changed(); + } +} + + +void kMandatoryFieldGroup::remove(QWidget *widget) +{ + widget->setPaletteBackgroundColor(widget->colorGroup().background()); + widgets.remove(widget); + changed(); +} + + +void kMandatoryFieldGroup::setOkButton(QPushButton *button) +{ + if (okButton && okButton != button) + okButton->setEnabled(true); + okButton = button; + changed(); +} + + +void kMandatoryFieldGroup::changed(void) +{ + bool enable = true; + QValueList<QWidget *>::ConstIterator i; + for (i = widgets.begin(); i != widgets.end(); ++i) { + QWidget *widget = *i; + // disabled widgets don't count + if(!(widget->isEnabled())) { + continue; + } + if (widget->inherits("QCheckBox")) { + if (((QCheckBox*)widget->qt_cast("QCheckBox"))->state() == QButton::NoChange) { + enable = false; + break; + } else + continue; + } + if (widget->inherits("QComboBox")) { + if (((QComboBox*)widget->qt_cast("QComboBox"))->currentText().isEmpty()) { + enable = false; + break; + } else + continue; + } + if (widget->inherits("QLineEdit")) { + if (((QLineEdit*)widget->qt_cast("QLineEdit"))->text().isEmpty()) { + enable = false; + break; + } else + continue; + } + if (widget->inherits("QListBox")) { + if (((QListBox*)widget->qt_cast("QListBox"))->selectedItem() == 0) { + enable = false; + break; + } else + continue; + } + } + + if (okButton) + okButton->setEnabled(enable); + m_enabled = enable; + + emit stateChanged(); + emit stateChanged(enable); +} + + +void kMandatoryFieldGroup::clear(void) +{ + QValueList<QWidget *>::Iterator i; + for (i = widgets.begin(); i != widgets.end(); ++i) + (*i)->setPaletteBackgroundColor((*i)->colorGroup().background()); + widgets.clear(); + if (okButton) { + okButton->setEnabled(true); + okButton = 0; + m_enabled = true; + } +} + + +#include "kguiutils.moc" + diff --git a/kmymoney2/widgets/kguiutils.h b/kmymoney2/widgets/kguiutils.h new file mode 100644 index 0000000..9a7b603 --- /dev/null +++ b/kmymoney2/widgets/kguiutils.h @@ -0,0 +1,94 @@ +/*************************************************************************** + kguiutils.h - description + ------------------- + begin : Fri Jan 27 2006 + copyright : (C) 2006 Tony Bloomfield + email : Tony Bloomfield <tonybloom@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 KGUIUTILS_H +#define KGUIUTILS_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> +#include <qvaluelist.h> +class QWidget; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kpushbutton.h> +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + * @author Tony Bloomfield + */ +class kMandatoryFieldGroup : public QObject +{ + Q_OBJECT + +public: + kMandatoryFieldGroup(QObject *parent) : + QObject(parent), okButton(0), m_enabled(true) {} + + /** + * This method adds a widget to the list of mandatory fields for the current dialog + * + * @param widget pointer to the widget to be added + */ + void add(QWidget *widget); + + /** + * This method removes a widget form the list of mandatory fields for the current dialog + * + * @param widget pointer to the widget to be removed + */ + void remove(QWidget *widget); + + /** + * This method designates the button to be enabled when all mandatory fields + * have been completed + * + * @param button pointer to the 'ok' button + */ + void setOkButton(QPushButton *button); + + /** + * This method returns if all requirements for the mandatory group + * have been fulfilled (@p true) or not (@p false). + */ + bool isEnabled(void) const { return m_enabled; } + +public slots: + void clear(void); + + /** + * Force update of ok button + */ + void changed(void); + +signals: + void stateChanged(void); + void stateChanged(bool state); + +private: + QValueList<QWidget *> widgets; + QPushButton* okButton; + bool m_enabled; +}; + +#endif // KGUIUTILS_H diff --git a/kmymoney2/widgets/klistviewsearchline.cpp b/kmymoney2/widgets/klistviewsearchline.cpp new file mode 100644 index 0000000..5aba160 --- /dev/null +++ b/kmymoney2/widgets/klistviewsearchline.cpp @@ -0,0 +1,507 @@ +/* This file is part of the KDE libraries + Copyright (c) 2003 Scott Wheeler <wheeler@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "klistviewsearchline.h" + +#include <klistview.h> +#include <kiconloader.h> +#include <ktoolbar.h> +#include <ktoolbarbutton.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qapplication.h> +#include <qtimer.h> +#include <qpopupmenu.h> +#include <qlabel.h> +#include <qheader.h> + +#define KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID 2004 + +class KListViewSearchLine::KListViewSearchLinePrivate +{ +public: + KListViewSearchLinePrivate() : + listView(0), + caseSensitive(false), + activeSearch(false), + keepParentsVisible(true), + queuedSearches(0) {} + + KListView *listView; + bool caseSensitive; + bool activeSearch; + bool keepParentsVisible; + QString search; + int queuedSearches; + QValueList<int> searchColumns; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public methods +//////////////////////////////////////////////////////////////////////////////// + +KListViewSearchLine::KListViewSearchLine(QWidget *parent, KListView *listView, const char *name) : + KLineEdit(parent, name) +{ + d = new KListViewSearchLinePrivate; + + d->listView = listView; + + connect(this, SIGNAL(textChanged(const QString &)), + this, SLOT(queueSearch(const QString &))); + + if(listView) { + connect(listView, SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + +#if KDE_IS_VERSION(3,3,0) + connect(listView, SIGNAL(itemAdded(QListViewItem *)), + this, SLOT(itemAdded(QListViewItem *))); +#endif + } + else + setEnabled(false); +} + +KListViewSearchLine::KListViewSearchLine(QWidget *parent, const char *name) : + KLineEdit(parent, name) +{ + d = new KListViewSearchLinePrivate; + + d->listView = 0; + + connect(this, SIGNAL(textChanged(const QString &)), + this, SLOT(queueSearch(const QString &))); + + setEnabled(false); +} + +KListViewSearchLine::~KListViewSearchLine() +{ + delete d; +} + +bool KListViewSearchLine::caseSensitive() const +{ + return d->caseSensitive; +} + +QValueList<int> KListViewSearchLine::searchColumns() const +{ + return d->searchColumns; +} + +bool KListViewSearchLine::keepParentsVisible() const +{ + return d->keepParentsVisible; +} + +KListView *KListViewSearchLine::listView() const +{ + return d->listView; +} + +//////////////////////////////////////////////////////////////////////////////// +// public slots +//////////////////////////////////////////////////////////////////////////////// + +void KListViewSearchLine::updateSearch(const QString &s) +{ + if(!d->listView) + return; + + d->search = s.isNull() ? text() : s; + + // If there's a selected item that is visible, make sure that it's visible + // when the search changes too (assuming that it still matches). + + QListViewItem *currentItem = 0; + + switch(d->listView->selectionMode()) + { + case KListView::NoSelection: + break; + case KListView::Single: + currentItem = d->listView->selectedItem(); + break; + default: + { + int flags = QListViewItemIterator::Selected | QListViewItemIterator::Visible; + for(QListViewItemIterator it(d->listView, flags); + it.current() && !currentItem; + ++it) + { + if(d->listView->itemRect(it.current()).isValid()) + currentItem = it.current(); + } + } + } + + if(d->keepParentsVisible) + checkItemParentsVisible(d->listView->firstChild()); + else + checkItemParentsNotVisible(); + + if(currentItem) + d->listView->ensureItemVisible(currentItem); +} + +void KListViewSearchLine::setCaseSensitive(bool cs) +{ + d->caseSensitive = cs; +} + +void KListViewSearchLine::setKeepParentsVisible(bool v) +{ + d->keepParentsVisible = v; +} + +void KListViewSearchLine::setSearchColumns(const QValueList<int> &columns) +{ + d->searchColumns = columns; +} + +void KListViewSearchLine::setListView(KListView *lv) +{ + if(d->listView) { + disconnect(d->listView, SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + +#if KDE_IS_VERSION(3,3,0) + disconnect(d->listView, SIGNAL(itemAdded(QListViewItem *)), + this, SLOT(itemAdded(QListViewItem *))); +#endif + } + + d->listView = lv; + + if(lv) { + connect(d->listView, SIGNAL(destroyed()), + this, SLOT(listViewDeleted())); + +#if KDE_IS_VERSION(3,3,0) + connect(d->listView, SIGNAL(itemAdded(QListViewItem *)), + this, SLOT(itemAdded(QListViewItem *))); +#endif + } + + setEnabled(bool(lv)); +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +bool KListViewSearchLine::itemMatches(const QListViewItem *item, const QString &s) const +{ + if(s.isEmpty()) + return true; + + // If the search column list is populated, search just the columns + // specifified. If it is empty default to searching all of the columns. + + if(!d->searchColumns.isEmpty()) { + QValueList<int>::ConstIterator it = d->searchColumns.begin(); + for(; it != d->searchColumns.end(); ++it) { + if(*it < item->listView()->columns() && + item->text(*it).find(s, 0, d->caseSensitive) >= 0) + return true; + } + } + else { + for(int i = 0; i < item->listView()->columns(); i++) { + if(item->listView()->columnWidth(i) > 0 && + item->text(i).find(s, 0, d->caseSensitive) >= 0) + { + return true; + } + } + } + + return false; +} + +QPopupMenu *KListViewSearchLine::createPopupMenu() +{ + QPopupMenu *popup = KLineEdit::createPopupMenu(); + + if (d->listView->columns()>1) { + QPopupMenu *subMenu = new QPopupMenu(popup); + connect(subMenu, SIGNAL(activated(int)), this, SLOT(searchColumnsMenuActivated(int))); + + popup->insertSeparator(); + popup->insertItem(i18n("Search Columns"), subMenu); + + subMenu->insertItem(i18n("All Visible Columns"), KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID); + subMenu->insertSeparator(); + + bool allColumnsAreSearchColumns = true; + // TODO Make the entry order match the actual column order + QHeader* const header = d->listView->header(); + int visibleColumns = 0; + for(int i = 0; i < d->listView->columns(); i++) { + if(d->listView->columnWidth(i)>0) { + QString columnText = d->listView->columnText(i); + if(columnText.isEmpty()) { + int visiblePosition=1; + for(int j = 0; j < header->mapToIndex(i); j++) + if(d->listView->columnWidth(header->mapToSection(j))>0) + visiblePosition++; + columnText = i18n("Column number %1","Column No. %1").arg(visiblePosition); + } + subMenu->insertItem(columnText, visibleColumns); + if(d->searchColumns.isEmpty() || d->searchColumns.find(i) != d->searchColumns.end()) + subMenu->setItemChecked(visibleColumns, true); + else + allColumnsAreSearchColumns = false; + visibleColumns++; + } + } + subMenu->setItemChecked(KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID, allColumnsAreSearchColumns); + + // searchColumnsMenuActivated() relies on one possible "all" representation + if(allColumnsAreSearchColumns && !d->searchColumns.isEmpty()) + d->searchColumns.clear(); + } + + return popup; +} + +//////////////////////////////////////////////////////////////////////////////// +// protected slots +//////////////////////////////////////////////////////////////////////////////// + +void KListViewSearchLine::queueSearch(const QString &search) +{ + d->queuedSearches++; + d->search = search; + QTimer::singleShot(200, this, SLOT(activateSearch())); +} + +void KListViewSearchLine::activateSearch() +{ + --(d->queuedSearches); + + if(d->queuedSearches == 0) + updateSearch(d->search); +} + +//////////////////////////////////////////////////////////////////////////////// +// private slots +//////////////////////////////////////////////////////////////////////////////// + +void KListViewSearchLine::itemAdded(QListViewItem *item) const +{ + item->setVisible(itemMatches(item, text())); +} + +void KListViewSearchLine::listViewDeleted() +{ + d->listView = 0; + setEnabled(false); +} + +void KListViewSearchLine::searchColumnsMenuActivated(int id) +{ + if(id == KLISTVIEWSEARCHLINE_ALLVISIBLECOLUMNS_ID) { + if(d->searchColumns.isEmpty()) + d->searchColumns.append(0); + else + d->searchColumns.clear(); + } + else { + if(d->searchColumns.find(id) != d->searchColumns.end()) + d->searchColumns.remove(id); + else { + if(d->searchColumns.isEmpty()) { + for(int i = 0; i < d->listView->columns(); i++) { + if(i != id) + d->searchColumns.append(i); + } + } + else + d->searchColumns.append(id); + } + } + updateSearch(); +} + +//////////////////////////////////////////////////////////////////////////////// +// private methods +//////////////////////////////////////////////////////////////////////////////// + +void KListViewSearchLine::checkItemParentsNotVisible() +{ + QListViewItemIterator it(d->listView); + for(; it.current(); ++it) + { + QListViewItem *item = it.current(); + if(itemMatches(item, d->search)) + item->setVisible(true); + else + item->setVisible(false); + } +} + +#include <kdebug.h> + +/** Check whether \p item, its siblings and their descendents should be shown. Show or hide the items as necessary. + * + * \p item The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the + * the first child of the list view. + * \p highestHiddenParent The highest (closest to root) ancestor of \p item which is hidden. If 0, all parents of + * \p item must be visible. + * \return \c true if an item which should be visible is found, \c false if all items found should be hidden. If this function + * returns true and \p highestHiddenParent was not 0, highestHiddenParent will have been shown. + */ +bool KListViewSearchLine::checkItemParentsVisible(QListViewItem *item, QListViewItem *highestHiddenParent) +{ + bool visible = false; + QListViewItem * first = item; + for(; item; item = item->nextSibling()) + { + //What we pass to our children as highestHiddenParent: + QListViewItem * hhp = highestHiddenParent ? highestHiddenParent : item->isVisible() ? 0L : item; + bool childMatch = false; + if(item->firstChild() && checkItemParentsVisible(item->firstChild(), hhp)) + childMatch = true; + // Should this item be shown? It should if any children should be, or if it matches. + if(childMatch || itemMatches(item, d->search)) + { + visible = true; + if (highestHiddenParent) + { + highestHiddenParent->setVisible(true); + // Calling setVisible on our ancestor will unhide all its descendents. Hide the ones + // before us that should not be shown. + for(QListViewItem *hide = first; hide != item; hide = hide->nextSibling()) + hide->setVisible(false); + highestHiddenParent = 0; + // If we matched, than none of our children matched, yet the setVisible() call on our + // ancestor unhid them, undo the damage: + if(!childMatch) + for(QListViewItem *hide = item->firstChild(); hide; hide = hide->nextSibling()) + hide->setVisible(false); + } + else + item->setVisible(true); + } + else + item->setVisible(false); + } + return visible; +} + +//////////////////////////////////////////////////////////////////////////////// +// KListViewSearchLineWidget +//////////////////////////////////////////////////////////////////////////////// + +class KListViewSearchLineWidget::KListViewSearchLineWidgetPrivate +{ +public: + KListViewSearchLineWidgetPrivate() : listView(0), searchLine(0), clearButton(0) {} + KListView *listView; + KListViewSearchLine *searchLine; + QToolButton *clearButton; +}; + +KListViewSearchLineWidget::KListViewSearchLineWidget(KListView *listView, + QWidget *parent, + const char *name) : + QHBox(parent, name) +{ + d = new KListViewSearchLineWidgetPrivate; + d->listView = listView; + + setSpacing(5); + + QTimer::singleShot(0, this, SLOT(createWidgets())); +} + +KListViewSearchLineWidget::~KListViewSearchLineWidget() +{ + delete d; +} + +KListViewSearchLine *KListViewSearchLineWidget::createSearchLine(KListView *listView) +{ + if(!d->searchLine) + d->searchLine = new KListViewSearchLine(this, listView); + return d->searchLine; +} + +void KListViewSearchLineWidget::createWidgets() +{ + positionInToolBar(); + + if(!d->clearButton) { + d->clearButton = new QToolButton(this); + QIconSet icon = SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"); + d->clearButton->setIconSet(icon); + } + + d->clearButton->show(); + + QLabel *label = new QLabel(i18n("S&earch:"), this, "kde toolbar widget"); + + d->searchLine = createSearchLine(d->listView); + d->searchLine->show(); + + label->setBuddy(d->searchLine); + label->show(); + + connect(d->clearButton, SIGNAL(clicked()), d->searchLine, SLOT(clear())); +} + +KListViewSearchLine *KListViewSearchLineWidget::searchLine() const +{ + return d->searchLine; +} + +void KListViewSearchLineWidget::positionInToolBar() +{ + KToolBar *toolBar = dynamic_cast<KToolBar *>(parent()); + + if(toolBar) { + + // Here we have The Big Ugly. Figure out how many widgets are in the + // and do a hack-ish iteration over them to find this widget so that we + // can insert the clear button before it. + + int widgetCount = toolBar->count(); + + for(int index = 0; index < widgetCount; index++) { + int id = toolBar->idAt(index); + if(toolBar->getWidget(id) == this) { + toolBar->setItemAutoSized(id); + if(!d->clearButton) { + QString icon = QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"; + d->clearButton = new KToolBarButton(icon, 2005, toolBar); + } + toolBar->insertWidget(2005, d->clearButton->width(), d->clearButton, index); + break; + } + } + } + + if(d->searchLine) + d->searchLine->show(); +} + +#include "klistviewsearchline.moc" diff --git a/kmymoney2/widgets/klistviewsearchline.h b/kmymoney2/widgets/klistviewsearchline.h new file mode 100644 index 0000000..041687e --- /dev/null +++ b/kmymoney2/widgets/klistviewsearchline.h @@ -0,0 +1,258 @@ +/* This file is part of the KDE libraries + Copyright (c) 2003 Scott Wheeler <wheeler@kde.org> + Adapted to be used with KMyMoney under KDE 3.2 .. 3.4 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KLISTVIEWSEARCHLINE_H +#define KLISTVIEWSEARCHLINE_H + +#include <klineedit.h> +#include <qhbox.h> +#include <kmymoney/export.h> + +class KListView; +class QListViewItem; +class QToolButton; + +/** + * This class makes it easy to add a search line for filtering the items in a + * listview based on a simple text search. + * + * No changes to the application other than instantiating this class with an + * appropriate KListView should be needed. + * + * @since 3.3 + */ + +class KMYMONEY_EXPORT KListViewSearchLine : public KLineEdit +{ + Q_OBJECT + +public: + + /** + * Constructs a KListViewSearchLine with \a listView being the KListView to + * be filtered. + * + * If \a listView is null then the widget will be disabled until a listview + * is set with setListView(). + */ + KListViewSearchLine(QWidget *parent = 0, KListView *listView = 0, const char *name = 0); + + /** + * Constructs a KListViewSearchLine without any KListView to filter. The + * KListView object has to be set later with setListView(). + */ + KListViewSearchLine(QWidget *parent, const char *name); + + /** + * Destroys the KListViewSearchLine. + */ + virtual ~KListViewSearchLine(); + + /** + * Returns true if the search is case sensitive. This defaults to false. + * + * @see setCaseSensitive() + */ + bool caseSensitive() const; + + /** + * Returns the current list of columns that will be searched. If the + * returned list is empty all visible columns will be searched. + * + * @see setSearchColumns + */ + QValueList<int> searchColumns() const; + + /** + * If this is true (the default) then the parents of matched items will also + * be shown. + * + * @see setKeepParentsVisible() + */ + bool keepParentsVisible() const; + + /** + * Returns the listview that is currently filtered by the search. + * + * @see setListView() + */ + KListView *listView() const; + +public slots: + /** + * Updates search to only make visible the items that match \a s. If + * \a s is null then the line edit's text will be used. + */ + virtual void updateSearch(const QString &s = QString::null); + + /** + * Make the search case sensitive or case insensitive. + * + * @see caseSenstive() + */ + void setCaseSensitive(bool cs); + + /** + * When a search is active on a list that's organized into a tree view if + * a parent or ancesestor of an item is does not match the search then it + * will be hidden and as such so too will any children that match. + * + * If this is set to true (the default) then the parents of matching items + * will be shown. + * + * @see keepParentsVisible + */ + void setKeepParentsVisible(bool v); + + /** + * Sets the list of columns to be searched. The default is to search all, + * visible columns which can be restored by passing \a columns as an empty + * list. + * + * @see searchColumns + */ + void setSearchColumns(const QValueList<int> &columns); + + /** + * Sets the KListView that is filtered by this search line. If \a lv is null + * then the widget will be disabled. + * + * @see listView() + */ + void setListView(KListView *lv); + +protected: + + /** + * Returns true if \a item matches the search \a s. This will be evaluated + * based on the value of caseSensitive(). This can be overridden in + * subclasses to implement more complicated matching schemes. + */ + virtual bool itemMatches(const QListViewItem *item, const QString &s) const; + + /** + * Re-implemented for internal reasons. API not affected. + * + * See QLineEdit::mousePressEvent(). + */ + virtual QPopupMenu *createPopupMenu(); + +protected slots: + /** + * When keys are pressed a new search string is created and a timer is + * activated. The most recent search is activated when this timer runs out + * if another key has not yet been pressed. + * + * This method makes @param search the most recent search and starts the + * timer. + * + * Together with activateSearch() this makes it such that searches are not + * started until there is a short break in the users typing. + * + * @see activateSearch() + */ + void queueSearch(const QString &search); + + /** + * When the timer started with queueSearch() expires this slot is called. + * If there has been another timer started then this slot does nothing. + * However if there are no other pending searches this starts the list view + * search. + * + * @see queueSearch() + */ + void activateSearch(); + +private: + + /** + * This is used in case parent items of matching items shouldn't be + * visible. It hides all items that don't match the search string. + */ + void checkItemParentsNotVisible(); + + /** + * This is used in case parent items of matching items should be visible. + * It makes a recursive call to all children. It returns true if at least + * one item in the subtree with the given root item is visible. + */ + bool checkItemParentsVisible(QListViewItem *item, QListViewItem *highestHiddenParent = 0); + +private slots: + void itemAdded(QListViewItem *item) const; + void listViewDeleted(); + void searchColumnsMenuActivated(int); + +private: + class KListViewSearchLinePrivate; + KListViewSearchLinePrivate *d; +}; + +/** + * Creates a widget featuring a KListViewSearchLine, a label with the text + * "Search" and a button to clear the search. + * + * @since 3.4 + */ +class KMYMONEY_EXPORT KListViewSearchLineWidget : public QHBox +{ + Q_OBJECT + +public: + /** + * Creates a KListViewSearchLineWidget for \a listView with \a parent as the + * parent with and \a name. + */ + KListViewSearchLineWidget(KListView *listView = 0, QWidget *parent = 0, + const char *name = 0); + + /** + * Destroys the KListViewSearchLineWidget + */ + ~KListViewSearchLineWidget(); + + /** + * Creates the search line. This can be useful to reimplement in cases where + * a KListViewSearchLine subclass is used. + */ + virtual KListViewSearchLine *createSearchLine(KListView *listView); + + /** + * Returns a pointer to the search line. + */ + KListViewSearchLine *searchLine() const; + +protected slots: + /** + * Creates the widgets inside of the widget. This is called from the + * constructor via a single shot timer so that it it guaranteed to run + * after construction is complete. This makes it suitable for overriding in + * subclasses. + */ + virtual void createWidgets(); + +private slots: + void positionInToolBar(); + +private: + class KListViewSearchLineWidgetPrivate; + KListViewSearchLineWidgetPrivate *d; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoney.widgets b/kmymoney2/widgets/kmymoney.widgets new file mode 100644 index 0000000..f844780 --- /dev/null +++ b/kmymoney2/widgets/kmymoney.widgets @@ -0,0 +1,54 @@ +[Includes] +kinstance.h + +[Init] +new KInstance("kmmwidgets"); + +[KMyMoneyTitleLabel] + +[kMyMoneyEdit] + +[kMyMoneyDateInput] + +[KMyMoneyAccountTree] + +[KMyMoneySecuritySelector] +IncludeFile=kmymoney/kmymoneycurrencyselector.h + +[KMyMoneyCurrencySelector] + +[KMyMoneyRegister::Register] +IconSet=register.png +IncludeFile=kmymoney/register.h + +[KMyMoneyTransactionForm::TransactionForm] +IconSet=transactionform.png +IncludeFile=kmymoney/transactionform.h + +[KMyMoneyCategory] + +[KMyMoneyPayeeCombo] +IncludeFile=kmymoney/kmymoneycombo.h + +[KMyMoneyPeriodCombo] +IncludeFile=kmymoney/kmymoneycombo.h + +[KMyMoneyFrequencyCombo] +IncludeFile=kmymoney/kmymoneycombo.h + +[KMyMoneyOccurencePeriodCombo] +IncludeFile=kmymoney/kmymoneycombo.h + +[TransactionSortOption] + +[KMyMoneyGeneralCombo] +IncludeFile=kmymoney/kmymoneycombo.h + +[KBudgetValues] + +[KAccountTemplateSelector] + +# deprecated will be removed at some point in time + +[KMyMoneyAccountCombo] + diff --git a/kmymoney2/widgets/kmymoneyaccountcombo.cpp b/kmymoney2/widgets/kmymoneyaccountcombo.cpp new file mode 100644 index 0000000..8a44786 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountcombo.cpp @@ -0,0 +1,218 @@ +/*************************************************************************** + kmymoneyaccountbutton - description + ------------------- + begin : Mon May 31 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdrawutil.h> +#include <qpainter.h> +#include <qstyle.h> +#include <qapplication.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/kmymoneyaccountcombo.h> +#include "kmymoneyaccountcompletion.h" + +KMyMoneyAccountCombo::KMyMoneyAccountCombo( QWidget* parent, const char* name ) : + KComboBox( parent, name ), + m_completion(0), + m_mlbDown(false) +{ +#ifndef KMM_DESIGNER + m_completion = new kMyMoneyAccountCompletion(this); + + connect(this, SIGNAL(clicked()), this, SLOT(slotButtonPressed())); + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotSelected(const QString&))); +#endif + + // make sure that we can display a minimum of characters + QFontMetrics fm(font()); + setMinimumWidth(fm.maxWidth()*15); + setMaximumHeight(height()); + + // we only use this one item and replace the text as we have our own dropdown box + insertItem(QString("")); +} + +KMyMoneyAccountCombo::~KMyMoneyAccountCombo() +{ +} + +void KMyMoneyAccountCombo::slotButtonPressed(void) +{ + m_completion->show(); +} + +void KMyMoneyAccountCombo::slotSelected(const QString& id) +{ + try { + MyMoneyAccount acc = MyMoneyFile::instance()->account(id); + setText(acc.name()); + emit accountSelected(id); + } catch(MyMoneyException *e) { + delete e; + } +} + +void KMyMoneyAccountCombo::setSelected(const QString& id) +{ + if(!id.isEmpty()) { + try { + MyMoneyAccount acc = MyMoneyFile::instance()->account(id); + setSelected(acc); + } catch(MyMoneyException *e) { + qDebug("Account '%s' not found in %s(%d)", id.data(), __FILE__, __LINE__); + delete e; + } + } else { + setText(QString()); + m_completion->setSelected(id); + } +} + +void KMyMoneyAccountCombo::setSelected(const MyMoneyAccount& acc) +{ + m_completion->setSelected(acc.id()); + setText(acc.name()); +} + +void KMyMoneyAccountCombo::setText(const QString& txt) +{ + changeItem(txt, currentItem()); +} + +int KMyMoneyAccountCombo::loadList(const QString& baseName, const QValueList<QString>& accountIdList, const bool clear) +{ + AccountSet set; + + return set.load(m_completion->selector(), baseName, accountIdList, clear); +} + +int KMyMoneyAccountCombo::loadList(KMyMoneyUtils::categoryTypeE typeMask) +{ + AccountSet set; + QValueList<int> typeList; + + if(typeMask & KMyMoneyUtils::asset) { + set.addAccountGroup(MyMoneyAccount::Asset); + } + if(typeMask & KMyMoneyUtils::liability) { + set.addAccountGroup(MyMoneyAccount::Liability); + } + if(typeMask & KMyMoneyUtils::income) { + set.addAccountGroup(MyMoneyAccount::Income); + } + if(typeMask & KMyMoneyUtils::expense) { + set.addAccountGroup(MyMoneyAccount::Expense); + } + + return set.load(m_completion->selector()); +} + +int KMyMoneyAccountCombo::loadList(MyMoneyAccount::accountTypeE type) +{ + AccountSet set; + + set.addAccountType(type); + + return set.load(m_completion->selector()); +} + +void KMyMoneyAccountCombo::keyPressEvent(QKeyEvent* k) +{ + switch(k->key()) { + case Qt::Key_Tab: + break; + + case Qt::Key_Space: + emit clicked(); + break; + + default: + break; + } + return; +} + +void KMyMoneyAccountCombo::mousePressEvent(QMouseEvent *e) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + bool hit = rect().contains( e->pos() ); + if ( hit ) { // mouse press on button + m_mlbDown = TRUE; // left mouse button down + emit pressed(); + } +} + +void KMyMoneyAccountCombo::mouseReleaseEvent(QMouseEvent *e) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + if ( !m_mlbDown ) + return; + m_mlbDown = FALSE; // left mouse button up + emit released(); + if ( rect().contains( e->pos() ) ) { // mouse release on button + emit clicked(); + } +} + +int KMyMoneyAccountCombo::count(void) const +{ + return m_completion->selector()->accountList().count(); +} + +QStringList KMyMoneyAccountCombo::accountList(const QValueList<MyMoneyAccount::accountTypeE>& list) const +{ + return m_completion->selector()->accountList(list); +}; + +int KMyMoneyAccountCombo::loadList(const QValueList<int>& list) +{ + // FIXME make the caller construct the AccountSet directly + AccountSet set; + QValueList<int>::const_iterator it; + for(it = list.begin(); it != list.end(); ++it) { + set.addAccountType(static_cast<MyMoneyAccount::accountTypeE>(*it)); + } + return set.load(m_completion->selector()); +}; + +QStringList KMyMoneyAccountCombo::selectedAccounts(void) const +{ + QStringList list; + m_completion->selector()->selectedItems(list); + return list; +}; + +#include "kmymoneyaccountcombo.moc" diff --git a/kmymoney2/widgets/kmymoneyaccountcombo.h b/kmymoney2/widgets/kmymoneyaccountcombo.h new file mode 100644 index 0000000..770d5ad --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountcombo.h @@ -0,0 +1,112 @@ +/*************************************************************************** + kmymoneyaccountbutton - description + ------------------- + begin : Mon May 31 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYACCOUNTBUTTON_H +#define KMYMONEYACCOUNTBUTTON_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kpushbutton.h> +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneyutils.h> +class kMyMoneyAccountCompletion; + +/** + * @author Thomas Baumgart + */ +class KMyMoneyAccountCombo : public KComboBox +{ + Q_OBJECT +public: + KMyMoneyAccountCombo( QWidget* parent = 0, const char* name = 0 ); + ~KMyMoneyAccountCombo(); + + /** + * Method returns how many items are in the account selector list. + */ + int count(void) const; + + /** + * This method loads the set of accounts into the widget + * as defined by the parameter @p accountIdList. @p accountIdList is + * a QValueList of account ids. + * + * @param baseName QString which should be used as group text + * @param accountIdList QValueList of QString account ids + * which should be loaded into the widget + * @param clear if true (default) clears the widget before populating + * @return This method returns the number of accounts loaded into the list + */ + int loadList(const QString& baseName, const QValueList<QString>& accountIdList, const bool clear = true); + + QStringList accountList(const QValueList<MyMoneyAccount::accountTypeE>& list = QValueList<MyMoneyAccount::accountTypeE>()) const; + + int loadList(KMyMoneyUtils::categoryTypeE typeMask); + int loadList(const QValueList<int>& list); + int loadList(MyMoneyAccount::accountTypeE type); + + void setSelected(const QString& id); + void setSelected(const MyMoneyAccount& acc); + + /** + * This method returns the list of selected account id's. If + * no account is selected, the list is empty. + * + * @return list of selected accounts + */ + QStringList selectedAccounts(void) const; + + virtual void keyPressEvent(QKeyEvent* e); + +public slots: + void slotButtonPressed(void); + void slotSelected(const QString&); + +protected slots: + +signals: + void accountSelected(const QString&); + + void pressed(); + void released(); + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + void setText(const QString& txt); + +private: + kMyMoneyAccountCompletion* m_completion; + bool m_mlbDown; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyaccountcompletion.cpp b/kmymoney2/widgets/kmymoneyaccountcompletion.cpp new file mode 100644 index 0000000..bb2af2e --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountcompletion.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + kmymoneyaccountcompletion.cpp - description + ------------------- + begin : Mon Apr 26 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qapplication.h> +#include <qregexp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyaccountcompletion.h" +#include <kmymoney/mymoneyfile.h> + +kMyMoneyAccountCompletion::kMyMoneyAccountCompletion(QWidget *parent, const char *name ) : + kMyMoneyCompletion(parent, name) +{ + delete m_selector; + m_selector = new kMyMoneyAccountSelector(this, 0, 0, false); + m_selector->listView()->setFocusProxy(this); + +#ifndef KMM_DESIGNER + // Default is to show all accounts + // FIXME We should leave this also to the caller + AccountSet set; + set.addAccountGroup(MyMoneyAccount::Asset); + set.addAccountGroup(MyMoneyAccount::Liability); + set.addAccountGroup(MyMoneyAccount::Income); + set.addAccountGroup(MyMoneyAccount::Expense); + set.load(selector()); +#endif + + connectSignals(m_selector, m_selector->listView()); +} + +kMyMoneyAccountCompletion::~kMyMoneyAccountCompletion() +{ +} + +void kMyMoneyAccountCompletion::slotMakeCompletion(const QString& txt) +{ + // if(txt.isEmpty() || txt.length() == 0) + // return; + + int cnt = 0; + if(txt.contains(MyMoneyFile::AccountSeperator) == 0) { + m_lastCompletion = QRegExp(QRegExp::escape(txt.stripWhiteSpace()), false); + cnt = selector()->slotMakeCompletion(m_lastCompletion); + } else { + QStringList parts = QStringList::split(MyMoneyFile::AccountSeperator, txt); + QString pattern("^"); + QStringList::iterator it; + for(it = parts.begin(); it != parts.end(); ++it) { + if(pattern.length() > 1) + pattern += MyMoneyFile::AccountSeperator; + pattern += QRegExp::escape(QString(*it).stripWhiteSpace()) + ".*"; + } + pattern += "$"; + m_lastCompletion = QRegExp(pattern, false); + cnt = selector()->slotMakeCompletion(m_lastCompletion); + // if we don't have a match, we try it again, but this time + // we add a wildcard for the top level + if(cnt == 0) { + pattern = pattern.insert(1, QString(".*")+MyMoneyFile::AccountSeperator); + m_lastCompletion = QRegExp(pattern, false); + cnt = selector()->slotMakeCompletion(m_lastCompletion); + } + } + + if(m_parent && m_parent->isVisible() && !isVisible() && cnt) + show(false); + else { + if(cnt != 0) { + adjustSize(); + } else { + hide(); + } + } +} + +#include "kmymoneyaccountcompletion.moc" diff --git a/kmymoney2/widgets/kmymoneyaccountcompletion.h b/kmymoney2/widgets/kmymoneyaccountcompletion.h new file mode 100644 index 0000000..725a07c --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountcompletion.h @@ -0,0 +1,63 @@ +/*************************************************************************** + kmymoneyaccountcompletion.h - description + ------------------- + begin : Mon Apr 26 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYACCOUNTCOMPLETION_H +#define KMYMONEYACCOUNTCOMPLETION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +class QListViewItem; + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneyaccountselector.h> +#include "kmymoneycompletion.h" + +/** + * @author Thomas Baumgart + */ +class kMyMoneyAccountCompletion : public kMyMoneyCompletion +{ + Q_OBJECT +public: + + kMyMoneyAccountCompletion(QWidget *parent=0, const char *name=0); + virtual ~kMyMoneyAccountCompletion(); + + QStringList accountList(const QValueList<MyMoneyAccount::accountTypeE>& list = QValueList<MyMoneyAccount::accountTypeE>()) const { return selector()->accountList(list); } + + /** + * reimplemented from kMyMoneyCompletion + */ + kMyMoneyAccountSelector* selector(void) const { return dynamic_cast<kMyMoneyAccountSelector*>(m_selector); } + +public slots: + void slotMakeCompletion(const QString& txt); +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyaccountselector.cpp b/kmymoney2/widgets/kmymoneyaccountselector.cpp new file mode 100644 index 0000000..f1596b4 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountselector.cpp @@ -0,0 +1,544 @@ +/*************************************************************************** + kmymoneyaccountselector.cpp - description + ------------------- + begin : Thu Sep 18 2003 + copyright : (C) 2003 by Thomas Baumgart + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlayout.h> +#include <qheader.h> +#include <qlabel.h> +#include <qtimer.h> +#include <qpainter.h> +#include <qstyle.h> +#include <qrect.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kpushbutton.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyaccountselector.h" +#include <kmymoney/mymoneyutils.h> +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/kmymoneylistviewitem.h> +#include <kmymoney/kmymoneychecklistitem.h> + +#include "../kmymoneyutils.h" +#include "../kmymoneyglobalsettings.h" + +kMyMoneyAccountSelector::kMyMoneyAccountSelector(QWidget *parent, const char *name, QWidget::WFlags flags, const bool createButtons) : + KMyMoneySelector(parent, name, flags), + m_allAccountsButton(0), + m_noAccountButton(0), + m_incomeCategoriesButton(0), + m_expenseCategoriesButton(0) +{ + + if(createButtons) { + QVBoxLayout* buttonLayout = new QVBoxLayout( 0, 0, 6, "accountSelectorButtonLayout"); + + m_allAccountsButton = new KPushButton( this, "m_allAccountsButton" ); + m_allAccountsButton->setText( i18n( "All" ) ); + buttonLayout->addWidget( m_allAccountsButton ); + + m_incomeCategoriesButton = new KPushButton( this, "m_incomeCategoriesButton" ); + m_incomeCategoriesButton->setText( i18n( "Income" ) ); + buttonLayout->addWidget( m_incomeCategoriesButton ); + + m_expenseCategoriesButton = new KPushButton( this, "m_expenseCategoriesButton" ); + m_expenseCategoriesButton->setText( i18n( "Expense" ) ); + buttonLayout->addWidget( m_expenseCategoriesButton ); + + m_noAccountButton = new KPushButton( this, "m_noAccountButton" ); + m_noAccountButton->setText( i18n( "None" ) ); + buttonLayout->addWidget( m_noAccountButton ); + + QSpacerItem* spacer = new QSpacerItem( 0, 67, QSizePolicy::Minimum, QSizePolicy::Expanding ); + buttonLayout->addItem( spacer ); + m_layout->addLayout( buttonLayout ); + + connect(m_allAccountsButton, SIGNAL(clicked()), this, SLOT(slotSelectAllAccounts())); + connect(m_noAccountButton, SIGNAL(clicked()), this, SLOT(slotDeselectAllAccounts())); + connect(m_incomeCategoriesButton, SIGNAL(clicked()), this, SLOT(slotSelectIncomeCategories())); + connect(m_expenseCategoriesButton, SIGNAL(clicked()), this, SLOT(slotSelectExpenseCategories())); + } + + // sort the list of accounts in ascending order + m_listView->setSorting(0); +} + +kMyMoneyAccountSelector::~kMyMoneyAccountSelector() +{ +} + +void kMyMoneyAccountSelector::removeButtons(void) +{ + delete m_allAccountsButton; + delete m_incomeCategoriesButton; + delete m_expenseCategoriesButton; + delete m_noAccountButton; +} + +void kMyMoneyAccountSelector::selectCategories(const bool income, const bool expense) +{ + QListViewItem* it_v; + + for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(static_cast<QCheckListItem*>(it_v)->text() == i18n("Income categories")) + selectAllSubItems(it_v, income); + else + selectAllSubItems(it_v, expense); + } + emit stateChanged(); +} + +void kMyMoneyAccountSelector::setSelectionMode(QListView::SelectionMode mode) +{ + m_incomeCategoriesButton->setHidden(mode == QListView::Multi); + m_expenseCategoriesButton->setHidden(mode == QListView::Multi); + KMyMoneySelector::setSelectionMode(mode); +} + +QStringList kMyMoneyAccountSelector::accountList(const QValueList<MyMoneyAccount::accountTypeE>& filterList) const +{ + QStringList list; + QListViewItemIterator it; + QListViewItem* it_v; + QValueList<MyMoneyAccount::accountTypeE>::ConstIterator it_f; + + it = QListViewItemIterator(m_listView, QListViewItemIterator::Selectable); + while((it_v = it.current()) != 0) { + { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + MyMoneyAccount acc = MyMoneyFile::instance()->account(it_c->id()); + it_f = filterList.find(acc.accountType()); + if(filterList.count() == 0 || it_f != filterList.end()) + list << it_c->id(); + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + MyMoneyAccount acc = MyMoneyFile::instance()->account(it_c->id()); + it_f = filterList.find(acc.accountType()); + if(filterList.count() == 0 || it_f != filterList.end()) + list << it_c->id(); + } + } + it++; + } + return list; +} + +bool kMyMoneyAccountSelector::match(const QRegExp& exp, QListViewItem* item) const +{ + if(!item->isSelectable()) + return false; + + KMyMoneyListViewItem* it_l = dynamic_cast<KMyMoneyListViewItem*>(item); + if(!it_l) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(item); + if(!it_c) { + return KMyMoneySelector::match(exp, item); + } + return exp.search(it_c->key(1, true)) != -1; + } + return exp.search(it_l->key(1, true)) != -1; +} + +bool kMyMoneyAccountSelector::contains(const QString& txt) const +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + QListViewItem* it_v; + + QString baseName = i18n("Asset") + "|" + + i18n("Liability") + "|" + + i18n("Income")+ "|" + + i18n("Expense")+ "|" + + i18n("Equity") + "|" + + i18n("Security"); + + while((it_v = it.current()) != 0) { + QRegExp exp(QString("^(?:%1):%2$").arg(baseName).arg(QRegExp::escape(txt))); + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(exp.search(it_c->key(1, true)) != -1) { + return true; + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + if(exp.search(it_c->key(1, true)) != -1) { + return true; + } + } + it++; + } + return false; +} + +# if 0 +void kMyMoneyAccountSelector::update(const QString& /* id */) +{ + QListViewItem* it_v = m_listView->currentItem(); + QString previousHighlighted; + bool state = false; + + if(m_selMode == QListView::Multi && it_v) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = static_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + previousHighlighted = it_c->id(); + state = it_c->isOn(); + } + } + } + + QStringList list = selectedAccounts(); + QStringList::Iterator it; + + if(!m_typeList.isEmpty()) + loadList(m_typeList); + else if(!m_baseName.isEmpty()) { + loadList(m_baseName, m_accountList); + } + + // because loadList() sets all accounts selected, we have to + // clear the selection and only turn on those, that were on + // before the update. + slotDeselectAllAccounts(); + for(it = list.begin(); it != list.end(); ++it) { + setSelected(*it, true); + } + + if(m_selMode == QListView::Multi) { + // make the previous highlighted item highlighted again + if(!previousHighlighted.isEmpty()) { + setSelected(previousHighlighted); + } + } +} +#endif + +AccountSet::AccountSet() : + m_count(0), + m_file(MyMoneyFile::instance()), + m_favorites(0), + m_hideClosedAccounts(true) +{ +} + +void AccountSet::addAccountGroup(MyMoneyAccount::accountTypeE group) +{ + if(group == MyMoneyAccount::Asset) { + m_typeList << MyMoneyAccount::Checkings; + m_typeList << MyMoneyAccount::Savings; + m_typeList << MyMoneyAccount::Cash; + m_typeList << MyMoneyAccount::AssetLoan; + m_typeList << MyMoneyAccount::CertificateDep; + m_typeList << MyMoneyAccount::Investment; + m_typeList << MyMoneyAccount::Stock; + m_typeList << MyMoneyAccount::MoneyMarket; + m_typeList << MyMoneyAccount::Asset; + m_typeList << MyMoneyAccount::Currency; + + } else if(group == MyMoneyAccount::Liability) { + m_typeList << MyMoneyAccount::CreditCard; + m_typeList << MyMoneyAccount::Loan; + m_typeList << MyMoneyAccount::Liability; + + } else if(group == MyMoneyAccount::Income) { + m_typeList << MyMoneyAccount::Income; + + } else if(group == MyMoneyAccount::Expense) { + m_typeList << MyMoneyAccount::Expense; + + } else if(group == MyMoneyAccount::Equity) { + m_typeList << MyMoneyAccount::Equity; + } +} + +void AccountSet::addAccountType(MyMoneyAccount::accountTypeE type) +{ + m_typeList << type; +} + +void AccountSet::removeAccountType(MyMoneyAccount::accountTypeE type) +{ + QValueList<MyMoneyAccount::accountTypeE>::iterator it; + it = m_typeList.find(type); + if(it != m_typeList.end()) { + m_typeList.remove(it); + } +} + +void AccountSet::clear(void) +{ + m_typeList.clear(); +} + +int AccountSet::load(kMyMoneyAccountSelector* selector) +{ + QStringList list; + QStringList::ConstIterator it_l; + int count = 0; + int typeMask = 0; + QString currentId; + + if(selector->selectionMode() == QListView::Single) { + QStringList list; + selector->selectedItems(list); + if(list.count() > 0) + currentId = list.first(); + } + if((m_typeList.contains(MyMoneyAccount::Checkings) + + m_typeList.contains(MyMoneyAccount::Savings) + + m_typeList.contains(MyMoneyAccount::Cash) + + m_typeList.contains(MyMoneyAccount::AssetLoan) + + m_typeList.contains(MyMoneyAccount::CertificateDep) + + m_typeList.contains(MyMoneyAccount::Investment) + + m_typeList.contains(MyMoneyAccount::Stock) + + m_typeList.contains(MyMoneyAccount::MoneyMarket) + + m_typeList.contains(MyMoneyAccount::Asset) + + m_typeList.contains(MyMoneyAccount::Currency)) > 0) + typeMask |= KMyMoneyUtils::asset; + + if((m_typeList.contains(MyMoneyAccount::CreditCard) + + m_typeList.contains(MyMoneyAccount::Loan) + + m_typeList.contains(MyMoneyAccount::Liability)) > 0) + typeMask |= KMyMoneyUtils::liability; + + if((m_typeList.contains(MyMoneyAccount::Income)) > 0) + typeMask |= KMyMoneyUtils::income; + + if((m_typeList.contains(MyMoneyAccount::Expense)) > 0) + typeMask |= KMyMoneyUtils::expense; + + if((m_typeList.contains(MyMoneyAccount::Equity)) > 0) + typeMask |= KMyMoneyUtils::equity; + + selector->clear(); + KListView* lv = selector->listView(); + m_count = 0; + QString key; + QListViewItem* after = 0; + + // create the favorite section first and sort it to the beginning + key = QString("A%1").arg(i18n("Favorites")); + m_favorites = selector->newItem(i18n("Favorites"), key); + + for(int mask = 0x01; mask != KMyMoneyUtils::last; mask <<= 1) { + QListViewItem* item = 0; + if((typeMask & mask & KMyMoneyUtils::asset) != 0) { + ++m_count; + key = QString("B%1").arg(i18n("Asset")); + item = selector->newItem(i18n("Asset accounts"), key); + list = m_file->asset().accountList(); + } + + if((typeMask & mask & KMyMoneyUtils::liability) != 0) { + ++m_count; + key = QString("C%1").arg(i18n("Liability")); + item = selector->newItem(i18n("Liability accounts"), key); + list = m_file->liability().accountList(); + } + + if((typeMask & mask & KMyMoneyUtils::income) != 0) { + ++m_count; + key = QString("D%1").arg(i18n("Income")); + item = selector->newItem(i18n("Income categories"), key); + list = m_file->income().accountList(); + if(selector->selectionMode() == QListView::Multi) { + selector->m_incomeCategoriesButton->show(); + } + } + + if((typeMask & mask & KMyMoneyUtils::expense) != 0) { + ++m_count; + key = QString("E%1").arg(i18n("Expense")); + item = selector->newItem(i18n("Expense categories"), key); + list = m_file->expense().accountList(); + if(selector->selectionMode() == QListView::Multi) { + selector->m_expenseCategoriesButton->show(); + } + } + + if((typeMask & mask & KMyMoneyUtils::equity) != 0) { + ++m_count; + key = QString("F%1").arg(i18n("Equity")); + item = selector->newItem(i18n("Equity accounts"), key); + list = m_file->equity().accountList(); + } + + if(!after) + after = item; + + if(item != 0) { + // scan all matching accounts found in the engine + for(it_l = list.begin(); it_l != list.end(); ++it_l) { + const MyMoneyAccount& acc = m_file->account(*it_l); + ++m_count; + ++count; + //this will include an account if it matches the account type and + //if it is still open or it has been set to show closed accounts + if(includeAccount(acc) + && (!isHidingClosedAccounts() || !acc.isClosed()) ) { + QString tmpKey; + tmpKey = key + MyMoneyFile::AccountSeperator + acc.name(); + QListViewItem* subItem = selector->newItem(item, acc.name(), tmpKey, acc.id()); + if(acc.value("PreferredAccount") == "Yes" + && m_typeList.contains(acc.accountType())) { + selector->newItem(m_favorites, acc.name(), tmpKey, acc.id()); + } + if(acc.accountList().count() > 0) { + subItem->setOpen(true); + count += loadSubAccounts(selector, subItem, tmpKey, acc.accountList()); + } + + //disable the item if it has been added only because a subaccount matches the type + if( !m_typeList.contains(acc.accountType()) ) { + subItem->setEnabled(false); + } + } + } + item->sortChildItems(0, true); + } + } + + // if we don't have a favorite account or the selector is for multi-mode + // we get rid of the favorite entry and subentries. + if(m_favorites->childCount() == 0 || selector->selectionMode() == QListView::Multi) { + delete m_favorites; + m_favorites = 0; + } + + // sort the list + selector->listView()->sort(); + + if(lv->firstChild()) { + if(currentId.isEmpty()) { + lv->setCurrentItem(lv->firstChild()); + lv->clearSelection(); + } else { + selector->setSelected(currentId); + } + } + selector->update(); + return count; +} + +int AccountSet::load(kMyMoneyAccountSelector* selector, const QString& baseName, const QValueList<QString>& accountIdList, const bool clear) +{ + int count = 0; + QListViewItem* item = 0; + + m_typeList.clear(); + if(clear) { + m_count = 0; + selector->clear(); + } + + item = selector->newItem(baseName); + ++m_count; + + QValueList<QString>::ConstIterator it; + for(it = accountIdList.begin(); it != accountIdList.end(); ++it) { + const MyMoneyAccount& acc = m_file->account(*it); + if(acc.isClosed()) + continue; + QString tmpKey; + // the first character must be preset. Since we don't know any sort order here, we just use A + tmpKey = QString("A%1%2%3").arg(baseName, MyMoneyFile::AccountSeperator, acc.name()); + selector->newItem(item, acc.name(), tmpKey, acc.id()); + ++m_count; + ++count; + } + + KListView* lv = selector->listView(); + if(lv->firstChild()) { + lv->setCurrentItem(lv->firstChild()); + lv->clearSelection(); + } + + selector->update(); + return count; +} + +int AccountSet::loadSubAccounts(kMyMoneyAccountSelector* selector, QListViewItem* parent, const QString& key, const QStringList& list) +{ + QStringList::ConstIterator it_l; + int count = 0; + + for(it_l = list.begin(); it_l != list.end(); ++it_l) { + const MyMoneyAccount& acc = m_file->account(*it_l); + // don't include stock accounts if not in expert mode + if(acc.isInvest() && !KMyMoneyGlobalSettings::expertMode()) + continue; + + if(includeAccount(acc) + && !acc.isClosed()) { + QString tmpKey; + tmpKey = key + MyMoneyFile::AccountSeperator + acc.name(); + ++count; + ++m_count; + QListViewItem* item = selector->newItem(parent, acc.name(), tmpKey, acc.id()); + if(acc.value("PreferredAccount") == "Yes" + && m_typeList.contains(acc.accountType())) { + selector->newItem(m_favorites, acc.name(), tmpKey, acc.id()); + } + if(acc.accountList().count() > 0) { + item->setOpen(true); + count += loadSubAccounts(selector, item, tmpKey, acc.accountList()); + } + + //disable the item if it has been added only because a subaccount matches the type + if( !m_typeList.contains(acc.accountType()) ) { + item->setEnabled(false); + } + } + } + return count; +} + +bool AccountSet::includeAccount(const MyMoneyAccount& acc) +{ + if( m_typeList.contains(acc.accountType()) ) + return true; + + QStringList accounts = acc.accountList(); + + if(accounts.size() > 0) { + QStringList::ConstIterator it_acc; + for(it_acc = accounts.begin(); it_acc != accounts.end(); ++it_acc) { + MyMoneyAccount account = m_file->account(*it_acc); + if( includeAccount(account) ) + return true; + } + } + return false; +} + + +#include "kmymoneyaccountselector.moc" diff --git a/kmymoney2/widgets/kmymoneyaccountselector.h b/kmymoney2/widgets/kmymoneyaccountselector.h new file mode 100644 index 0000000..2fc0635 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccountselector.h @@ -0,0 +1,187 @@ +/*************************************************************************** + kmymoneyaccountselector.h + ------------------- + begin : Thu Sep 18 2003 + copyright : (C) 2003 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYACCOUNTSELECTOR_H +#define KMYMONEYACCOUNTSELECTOR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KPushButton; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneyselector.h> +#include <kmymoney/kmymoneyutils.h> +#include <kmymoney/mymoneyaccount.h> + +class kMyMoneyAccountCompletion; +class AccountSet; +class MyMoneyFile; + +/** + * This class implements an account/category selector. It is based + * on a tree view. Using this widget, one can select one or multiple + * accounts depending on the mode of operation and the set of accounts + * selected to be displayed. (see setSelectionMode() + * and loadList() about the specifics of configuration). + * + * - Single selection mode\n + * In this mode the widget allows to select a single entry out of + * the set of displayed accounts. + * + * - Multi selection mode\n + * In this mode, the widget allows to select one or more entries + * out of the set of displayed accounts. Selection is performed + * by marking the account in the view. + */ +class kMyMoneyAccountSelector : public KMyMoneySelector +{ + Q_OBJECT +public: + friend class AccountSet; + + kMyMoneyAccountSelector(QWidget *parent=0, const char *name=0, QWidget::WFlags flags = 0, const bool createButtons = true); + virtual ~kMyMoneyAccountSelector(); + + /** + * This method returns a list of account ids of those accounts + * currently loaded into the widget. It is possible to select + * a list of specific account types only. In this case, pass + * a list of account types as parameter @p list. + * + * @param list QValueList of account types to be returned. If this + * list is empty (the default), then the ids of all accounts + * will be returned. + * @return QStringList of account ids + */ + QStringList accountList(const QValueList<MyMoneyAccount::accountTypeE>& list = QValueList<MyMoneyAccount::accountTypeE>()) const; + + void setSelectionMode(QListView::SelectionMode mode); + + /** + * This method checks if a given @a item matches the given regular expression @a exp. + * + * @param exp const reference to a regular expression object + * @param item pointer to QListViewItem + * + * @retval true item matches + * @retval false item does not match + */ + virtual bool match(const QRegExp& exp, QListViewItem* item) const; + + /** + * This method returns, if any of the items in the selector contains + * the text @a txt. + * + * @param txt const reference to string to be looked for + * @retval true exact match found + * @retval false no match found + */ + virtual bool contains(const QString& txt) const; + + /** + * This method removes all the buttons of the widget + */ + void removeButtons(void); + +public slots: + /** + * This slot selects all items that are currently in + * the account list of the widget. + */ + void slotSelectAllAccounts(void) { selectAllItems(true); }; + + /** + * This slot deselects all items that are currently in + * the account list of the widget. + */ + void slotDeselectAllAccounts(void) { selectAllItems(false); }; + +protected: + /** + * This method loads the list of subaccounts as found in the + * @p list and attaches them to the parent widget passed as @p parent. + * + * @param parent pointer to parent widget + * @param list QStringList containing the ids of all subaccounts to load + * @return This method returns the number of accounts loaded into the list + */ + int loadSubAccounts(QListViewItem* parent, const QStringList& list); + + /** + * This is a helper method for selectAllIncomeCategories() + * and selectAllExpenseCategories(). + */ + void selectCategories(const bool income, const bool expense); + +protected slots: + /** + * This slot selects all income categories + */ + void slotSelectIncomeCategories(void) { selectCategories(true, false); }; + + /** + * This slot selects all expense categories + */ + void slotSelectExpenseCategories(void) { selectCategories(false, true); }; + +protected: + KPushButton* m_allAccountsButton; + KPushButton* m_noAccountButton; + KPushButton* m_incomeCategoriesButton; + KPushButton* m_expenseCategoriesButton; + QValueList<int> m_typeList; + QStringList m_accountList; +}; + + +class AccountSet +{ +public: + AccountSet(); + + void addAccountType(MyMoneyAccount::accountTypeE type); + void addAccountGroup(MyMoneyAccount::accountTypeE type); + void removeAccountType(MyMoneyAccount::accountTypeE type); + + void clear(void); + + int load(kMyMoneyAccountSelector* selector); + int load(kMyMoneyAccountSelector* selector, const QString& baseName, const QValueList<QString>& accountIdList, const bool clear = false); + + int count(void) const { return m_count; } + + void setHideClosedAccounts (bool _bool) { m_hideClosedAccounts = _bool; } + bool isHidingClosedAccounts (void) { return m_hideClosedAccounts; } + +protected: + int loadSubAccounts(kMyMoneyAccountSelector* selector, QListViewItem* parent, const QString& key, const QStringList& list); + bool includeAccount(const MyMoneyAccount& acc); + +private: + int m_count; + MyMoneyFile* m_file; + QValueList<MyMoneyAccount::accountTypeE> m_typeList; + QListViewItem* m_favorites; + bool m_hideClosedAccounts; +}; +#endif diff --git a/kmymoney2/widgets/kmymoneyaccounttree.cpp b/kmymoney2/widgets/kmymoneyaccounttree.cpp new file mode 100644 index 0000000..1b669ef --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttree.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + kmymoneyaccounttree.cpp - description + ------------------- + begin : Sat Jan 1 2005 + copyright : (C) 2005 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpoint.h> +#include <qevent.h> +#include <qdragobject.h> +#include <qtimer.h> +#include <qcursor.h> +#include <qheader.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qstyle.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kmessagebox.h> +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kstandarddirs.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/kmymoneyaccounttree.h> +#include <kmymoney/kmymoneyglobalsettings.h> + +#include <kmymoney/kmymoneyutils.h> + +KMyMoneyAccountTree::KMyMoneyAccountTree(QWidget* parent, const char *name) : + KMyMoneyAccountTreeBase(parent,name) +{ + showType(); + + m_taxReportColumn = addColumn(i18n("Column heading for category in tax report", "Tax")); + setColumnWidthMode(m_taxReportColumn, QListView::Manual); + setColumnAlignment(m_taxReportColumn, Qt::AlignHCenter); + + m_vatCategoryColumn = addColumn(i18n("Column heading for VAT category", "VAT")); + setColumnWidthMode(m_vatCategoryColumn, QListView::Manual); + setColumnAlignment(m_vatCategoryColumn, Qt::AlignHCenter); + + showValue(); +} + +KMyMoneyAccountTreeItem::KMyMoneyAccountTreeItem(KListView *parent, const MyMoneyAccount& account, const MyMoneySecurity& security , const QString& name) : + KMyMoneyAccountTreeBaseItem(parent,account,security,name), + m_reconcileFlag(false) +{ + updateAccount(); +} + +KMyMoneyAccountTreeItem::KMyMoneyAccountTreeItem(KMyMoneyAccountTreeBaseItem *parent, const MyMoneyAccount& account, const QValueList<MyMoneyPrice>& price, const MyMoneySecurity& security) : + KMyMoneyAccountTreeBaseItem(parent,account,price,security), + m_reconcileFlag(false) +{ + updateAccount(); +} + +KMyMoneyAccountTreeItem::KMyMoneyAccountTreeItem(KListView *parent, const MyMoneyInstitution& institution) : + KMyMoneyAccountTreeBaseItem(parent,institution), + m_reconcileFlag(false) +{ +} + +void KMyMoneyAccountTreeItem::fillColumns() +{ + KMyMoneyAccountTree* lv = dynamic_cast<KMyMoneyAccountTree*>(listView()); + if (!lv) + return; + KMyMoneyAccountTreeBaseItem::fillColumns(); + QPixmap checkMark = QPixmap(KGlobal::iconLoader()->loadIcon("ok", KIcon::Small)); + MyMoneyMoney vatRate; + if (!isInstitution()) + setPixmap(lv->nameColumn(), m_account.accountPixmap(m_reconcileFlag, 22)); + switch(m_account.accountType()) { + case MyMoneyAccount::Income: + case MyMoneyAccount::Expense: + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + if(m_account.value("Tax").lower() == "yes") + setPixmap(lv->taxReportColumn(), checkMark); + if(!m_account.value("VatAccount").isEmpty()) { + setPixmap(lv->vatCategoryColumn(), checkMark); + } + if(!m_account.value("VatRate").isEmpty()) { + vatRate = MyMoneyMoney(m_account.value("VatRate")) * MyMoneyMoney(100,1); + setText(lv->vatCategoryColumn(), QString("%1 %").arg(vatRate.formatMoney("", 1))); + } + break; + default: + break; + } +} + +void KMyMoneyAccountTreeItem::setReconciliation(bool on) +{ + if(m_reconcileFlag == on) + return; + m_reconcileFlag = on; + updateAccount(); +} + +MyMoneyMoney KMyMoneyAccountTreeItem::balance() const +{ + MyMoneyMoney result; + // account.balance() is not compatable with stock accounts + if ( m_account.isInvest() ) + result = MyMoneyFile::instance()->balance(m_account.id()); + else + result = m_account.balance(); + // for income and liability accounts, we reverse the sign + switch(m_account.accountGroup()) { + case MyMoneyAccount::Income: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Equity: + result = -result; + break; + + default: + break; + } + return result; +} + + +#include "kmymoneyaccounttree.moc" +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/kmymoneyaccounttree.h b/kmymoney2/widgets/kmymoneyaccounttree.h new file mode 100644 index 0000000..a4b741d --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttree.h @@ -0,0 +1,109 @@ +/*************************************************************************** + kmymoneyaccounttree.h - description + ------------------- + begin : Sat Jan 1 2005 + copyright : (C) 2005 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYACCOUNTTREE_H +#define KMYMONEYACCOUNTTREE_H + + +#include <kmymoney/kmymoneyaccounttreebase.h> + +class KMyMoneyAccountTreeItem; + +class KMyMoneyAccountTree : public KMyMoneyAccountTreeBase +{ + Q_OBJECT +public: + KMyMoneyAccountTree(QWidget* parent = 0, const char *name = 0); + int taxReportColumn(void) const { return m_taxReportColumn; } + int vatCategoryColumn(void) const { return m_vatCategoryColumn; } +private: + int m_taxReportColumn; + int m_vatCategoryColumn; +}; + +class KMyMoneyAccountTreeItem : public KMyMoneyAccountTreeBaseItem +{ +public: + /** + * Constructor to be used to construct an institution entry + * object. + * + * @param parent pointer to the KListView object this entry should be + * added to. + * @param institution const reference to MyMoneyInstitution for which + * the KListView entry is constructed + */ + KMyMoneyAccountTreeItem(KListView *parent, const MyMoneyInstitution& institution); + + /** + * Constructor to be used to construct a standard account entry object (e.g. Asset, + * Liability, etc.). + * + * @param parent pointer to the KListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + * @param name name of the account to be used instead of the one stored with @p account + * If empty, the one stored with @p account will be used. Default: empty + */ + KMyMoneyAccountTreeItem(KListView *parent, const MyMoneyAccount& account, const MyMoneySecurity& security = MyMoneySecurity(), const QString& name = QString()); + + /** + * Constructor to be used to construct an account entry + * object. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param price price to be used to calculate value (defaults to 1) + * This is used for accounts denominated in foreign currencies or stocks + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + */ + KMyMoneyAccountTreeItem(KMyMoneyAccountTreeBaseItem *parent, const MyMoneyAccount& account, const QValueList<MyMoneyPrice>& price = QValueList<MyMoneyPrice>(), const MyMoneySecurity& security = MyMoneySecurity()); + + void setReconciliation(bool); + + +protected: + /** + * Returns the current balance of this account. + * + * This is a pure virtual function, to allow subclasses to calculate + * the balance in different ways. + * + * Parent items in the tree will only be recomputed if the balance() for + * a son changes. + * @param account Account to get the balance for + * @return Balance of this account + */ + MyMoneyMoney balance() const; + + bool m_reconcileFlag; + + /** + * populates the columns. Derived classes should override this. The + * name column is already filled and should not be changed. + */ + void fillColumns(); +}; + +#endif + diff --git a/kmymoney2/widgets/kmymoneyaccounttreebase.cpp b/kmymoney2/widgets/kmymoneyaccounttreebase.cpp new file mode 100644 index 0000000..da35ea1 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreebase.cpp @@ -0,0 +1,825 @@ +/*************************************************************************** + kmymoneyaccounttree.cpp - description + ------------------- + begin : Sat Jan 1 2005 + copyright : (C) 2005 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpoint.h> +#include <qevent.h> +#include <qdragobject.h> +#include <qtimer.h> +#include <qcursor.h> +#include <qheader.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qstyle.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kmessagebox.h> +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kstandarddirs.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/kmymoneyaccounttree.h> +#include <kmymoney/kmymoneyglobalsettings.h> + +#include <kmymoney/kmymoneyutils.h> + +KMyMoneyAccountTreeBase::KMyMoneyAccountTreeBase(QWidget* parent, const char* name) : + KListView(parent, name), + m_accountConnections(false), + m_institutionConnections(false), + m_queuedSort(0) +{ + setRootIsDecorated(true); + setAllColumnsShowFocus(true); + + m_nameColumn = addColumn(i18n("Account")); + setColumnWidthMode(m_nameColumn, QListView::Manual); + + m_typeColumn = -1; + m_balanceColumn = -1; + m_valueColumn = -1; + + setMultiSelection(false); + + setResizeMode(QListView::LastColumn); + setShowSortIndicator(true); + setSorting(0); + + header()->setResizeEnabled(true); + + setDragEnabled(false); + setAcceptDrops(false); + setItemsMovable(false); + setDropVisualizer(false); + setDropHighlighter(true); + + // setup a default + m_baseCurrency.setSmallestAccountFraction(100); + m_baseCurrency.setSmallestCashFraction(100); + + connect(this, SIGNAL(dropped(QDropEvent*,QListViewItem*,QListViewItem*)), this, SLOT(slotObjectDropped(QDropEvent*,QListViewItem*,QListViewItem*))); + connect(this, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotSelectObject(QListViewItem*))); + connect(this, SIGNAL(contextMenu(KListView*, QListViewItem* , const QPoint&)), this, SLOT(slotOpenContextMenu(KListView*, QListViewItem*, const QPoint&))); + connect(this, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)), this, SLOT(slotOpenObject(QListViewItem*))); + + // drag and drop timer connections + connect( &m_autoopenTimer, SIGNAL( timeout() ), this, SLOT( slotOpenFolder() ) ); + connect( &m_autoscrollTimer, SIGNAL( timeout() ), this, SLOT( slotAutoScroll() ) ); + +} + +KMyMoneyAccountTreeBase::~KMyMoneyAccountTreeBase() +{ + if (!m_configGroup.isEmpty()) + saveLayout(KGlobal::config(), m_configGroup); +} + +void KMyMoneyAccountTreeBase::restoreLayout(const QString& group) +{ + if (!m_configGroup.isEmpty()) + return; // already done + // make sure to use the previous settings. If no settings are found + // we use equal distribution of all fields as an initial setting + // TODO this only makes the first column invisible if settings exist setColumnWidth(0, 0); + m_configGroup = group; + KListView::restoreLayout(KGlobal::config(), m_configGroup); +} + +void KMyMoneyAccountTreeBase::showType(void) +{ + m_typeColumn = addColumn(i18n("Type")); + setColumnWidthMode(m_typeColumn, QListView::Manual); + setColumnAlignment(m_typeColumn, Qt::AlignLeft); +} + +void KMyMoneyAccountTreeBase::showValue(void) +{ + m_balanceColumn = addColumn(i18n("Total Balance")); + setColumnWidthMode(m_balanceColumn, QListView::Manual); + setColumnAlignment(m_balanceColumn, Qt::AlignRight); + + m_valueColumn = addColumn(i18n("Total Value")); + setColumnWidthMode(m_valueColumn, QListView::Manual); + setColumnAlignment(m_valueColumn, Qt::AlignRight); +} + +void KMyMoneyAccountTreeBase::connectNotify(const char * /* s */) +{ + // update drag and drop settings + m_accountConnections = (receivers(SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyAccount&))) != 0); + m_institutionConnections = (receivers(SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyInstitution&))) != 0); + setDragEnabled(m_accountConnections | m_institutionConnections); + setAcceptDrops(m_accountConnections | m_institutionConnections); +} + +void KMyMoneyAccountTreeBase::disconnectNotify(const char * /* s */) +{ + // update drag and drop settings + m_accountConnections = (receivers(SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyAccount&))) != 0); + m_institutionConnections = (receivers(SIGNAL(reparent(const MyMoneyAccount&, const MyMoneyInstitution&))) != 0); + setDragEnabled(m_accountConnections | m_institutionConnections); + setAcceptDrops(m_accountConnections | m_institutionConnections); +} + +void KMyMoneyAccountTreeBase::setSectionHeader(const QString& txt) +{ + header()->setLabel(nameColumn(), txt); +} + +KMyMoneyAccountTreeBaseItem* KMyMoneyAccountTreeBase::selectedItem(void) const +{ + return dynamic_cast<KMyMoneyAccountTreeBaseItem *>(KListView::selectedItem()); +} + +const KMyMoneyAccountTreeBaseItem* KMyMoneyAccountTreeBase::findItem(const QString& id) const +{ + // tried to use a QListViewItemIterator but that does not fit + // with the constness of this method. Arghhh. + + QListViewItem* p = firstChild(); + while(p) { + // item found, check for the id + KMyMoneyAccountTreeBaseItem* item = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(p); + if(item && item->id() == id) + break; + + // item did not match, search the next one + QListViewItem* next = p->firstChild(); + if(!next) { + while((next = p->nextSibling()) == 0) { + p = p->parent(); + if(!p) + break; + } + } + p = next; + } + + return dynamic_cast<KMyMoneyAccountTreeBaseItem*>(p); +} + +bool KMyMoneyAccountTreeBase::dropAccountOnAccount(const MyMoneyAccount& accFrom, const MyMoneyAccount& accTo) const +{ + bool rc = false; + + // it does not make sense to reparent an account to oneself + // or to reparent it to it's current parent + if(accTo.id() != accFrom.id() + && accFrom.parentAccountId() != accTo.id()) { + // Moving within a group is generally ok + rc = accTo.accountGroup() == accFrom.accountGroup(); + + // now check for exceptions + if(rc) { + if(accTo.accountType() == MyMoneyAccount::Investment + && !accFrom.isInvest()) + rc = false; + + else if(accFrom.isInvest() + && accTo.accountType() != MyMoneyAccount::Investment) + rc = false; + + } else { + if(accFrom.accountGroup() == MyMoneyAccount::Income + && accTo.accountGroup() == MyMoneyAccount::Expense) + rc = true; + + if(accFrom.accountGroup() == MyMoneyAccount::Expense + && accTo.accountGroup() == MyMoneyAccount::Income) + rc = true; + } + + // if it's generally ok to drop here, make sure that + // the accTo does not have a child with the same name + const KMyMoneyAccountTreeBaseItem* to = findItem(accTo.id()); + if(to) { + to = dynamic_cast<KMyMoneyAccountTreeBaseItem*> (to->firstChild()); + while(to && rc) { + if(to->isAccount()) { + const MyMoneyAccount& acc = dynamic_cast<const MyMoneyAccount&>(to->itemObject()); + if(acc.name() == accFrom.name()) + rc = false; + } + to = dynamic_cast<KMyMoneyAccountTreeBaseItem*> (to->nextSibling()); + } + } + } + + return rc; +} + +bool KMyMoneyAccountTreeBase::acceptDrag(QDropEvent* event) const +{ + bool rc; + + if((rc = (acceptDrops()) && (event->source() == viewport()))) { + rc = false; + KMyMoneyAccountTreeBaseItem* to = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(itemAt( contentsToViewport(event->pos()) )); + QString fromId(event->encodedData("text/plain")); + const KMyMoneyAccountTreeBaseItem* from = findItem(fromId); + + // we can only move accounts around + if(!from->isAccount()) + from = 0; + + if(to && from && !to->isChildOf(from)) { + const MyMoneyAccount& accFrom = dynamic_cast<const MyMoneyAccount&>(from->itemObject()); + + if(to->isAccount() && m_accountConnections) { + const MyMoneyAccount& accTo = dynamic_cast<const MyMoneyAccount&>(to->itemObject()); + rc = dropAccountOnAccount(accFrom, accTo); + + } else if(to->isInstitution() && m_institutionConnections) { + // Moving a non-stock account to an institution is ok + if(!accFrom.isInvest()) + rc = true; + } + } + } + + return rc; +} + +void KMyMoneyAccountTreeBase::startDrag(void) +{ + QListViewItem* item = currentItem(); + KMyMoneyAccountTreeBaseItem* p = dynamic_cast<KMyMoneyAccountTreeBaseItem *>(item); + if(!p) + return; + + if(p->isAccount()) { + QTextDrag* drag = new QTextDrag(p->id(), viewport()); + drag->setSubtype("plain"); + + // use the icon that is attached to the item to be dragged + if (p->pixmap(0)) { + QPixmap pixmap(*p->pixmap(0)); + QPoint hotspot( pixmap.width() / 2, pixmap.height() / 2 ); + drag->setPixmap(pixmap, hotspot); + } + + if (drag->dragMove() && drag->target() != viewport()) + emit moved(); + } + return; +} + +void KMyMoneyAccountTreeBase::slotObjectDropped(QDropEvent* event, QListViewItem* /* parent */, QListViewItem* /* after */) +{ + m_autoopenTimer.stop(); + slotStopAutoScroll(); + if(dropHighlighter()) + cleanItemHighlighter(); + + KMyMoneyAccountTreeBaseItem* newParent = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(m_dropItem); + if(newParent) { + QString fromId(event->encodedData("text/plain")); + const KMyMoneyAccountTreeBaseItem* from = findItem(fromId); + + // we can only move accounts around + if(!from->isAccount()) + from = 0; + + if(from) { + const MyMoneyAccount& accFrom = dynamic_cast<const MyMoneyAccount&>(from->itemObject()); + if(newParent->isAccount()) { + const MyMoneyAccount& accTo = dynamic_cast<const MyMoneyAccount&>(newParent->itemObject()); + if(dropAccountOnAccount(accFrom, accTo)) { + emit reparent(accFrom, accTo); + } + + } else if(newParent->isInstitution()) { + if(!accFrom.isInvest()) { + const MyMoneyInstitution& institution = dynamic_cast<const MyMoneyInstitution&>(newParent->itemObject()); + emit reparent(accFrom, institution); + } + } + } + } +} + +void KMyMoneyAccountTreeBase::slotSelectObject(QListViewItem* i) +{ + emit selectObject(MyMoneyInstitution()); + emit selectObject(MyMoneyAccount()); + + KMyMoneyAccountTreeBaseItem* item = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(i); + if(item != 0) { + emit selectObject(item->itemObject()); + } +} + +void KMyMoneyAccountTreeBase::slotOpenContextMenu(KListView* lv, QListViewItem* i, const QPoint&) +{ + Q_UNUSED(lv); + + KMyMoneyAccountTreeBaseItem* item = dynamic_cast<KMyMoneyAccountTreeBaseItem *>(i); + if(item) { + emit selectObject(item->itemObject()); + + // Create a copy of the item since the original might be destroyed + // during processing of this signal. + if(item->isInstitution()) { + MyMoneyInstitution institution = dynamic_cast<const MyMoneyInstitution&>(item->itemObject()); + emit openContextMenu(institution); + } else { + MyMoneyAccount account = dynamic_cast<const MyMoneyAccount&>(item->itemObject()); + emit openContextMenu(account); + } + } +} + +void KMyMoneyAccountTreeBase::slotOpenObject(QListViewItem* i) +{ + KMyMoneyAccountTreeBaseItem* item = dynamic_cast<KMyMoneyAccountTreeBaseItem *>(i); + if(item) { + // Create a copy of the item since the original might be destroyed + // during processing of this signal. + if(item->isAccount()) { + MyMoneyAccount acc = dynamic_cast<const MyMoneyAccount&>(item->itemObject()); + emit openObject(acc); + } else if(item->isInstitution()) { + MyMoneyInstitution inst = dynamic_cast<const MyMoneyInstitution&>(item->itemObject()); + emit openObject(inst); + } + } +} + +/* drag and drop support inspired partially from KMail */ +/* --------------------------------------------------- */ +static const int autoscrollMargin = 16; +static const int initialScrollTime = 30; +static const int initialScrollAccel = 5; +static const int autoopenTime = 750; + +void KMyMoneyAccountTreeBase::slotOpenFolder(void) +{ + m_autoopenTimer.stop(); + if ( m_dropItem && !m_dropItem->isOpen() ) { + m_dropItem->setOpen( TRUE ); + m_dropItem->repaint(); + } +} + +void KMyMoneyAccountTreeBase::slotStartAutoScroll(void) +{ + if ( !m_autoscrollTimer.isActive() ) { + m_autoscrollTime = initialScrollTime; + m_autoscrollAccel = initialScrollAccel; + m_autoscrollTimer.start( m_autoscrollTime ); + } +} + +void KMyMoneyAccountTreeBase::slotStopAutoScroll(void) +{ + m_autoscrollTimer.stop(); +} + +void KMyMoneyAccountTreeBase::slotAutoScroll(void) +{ + // don't show a highlighter during scrolling + cleanItemHighlighter(); + + QPoint p = viewport()->mapFromGlobal( QCursor::pos() ); + + if ( m_autoscrollAccel-- <= 0 && m_autoscrollTime ) { + m_autoscrollAccel = initialScrollAccel; + m_autoscrollTime--; + m_autoscrollTimer.start( m_autoscrollTime ); + } + int l = QMAX(1,(initialScrollTime-m_autoscrollTime)); + + int dx=0,dy=0; + if ( p.y() < autoscrollMargin ) { + dy = -l; + } else if ( p.y() > visibleHeight()-autoscrollMargin ) { + dy = +l; + } + if ( p.x() < autoscrollMargin ) { + dx = -l; + } else if ( p.x() > visibleWidth()-autoscrollMargin ) { + dx = +l; + } + if ( dx || dy ) { + scrollBy(dx, dy); + } else { + slotStopAutoScroll(); + } +} + +void KMyMoneyAccountTreeBase::contentsDragMoveEvent(QDragMoveEvent* e) +{ + QPoint vp = contentsToViewport(e->pos()); + QRect inside_margin((contentsX() > 0) ? autoscrollMargin : 0, + (contentsY() > 0) ? autoscrollMargin : 0, + visibleWidth() - ((contentsX() + visibleWidth() < contentsWidth()) + ? autoscrollMargin*2 : 0), + visibleHeight() - ((contentsY() + visibleHeight() < contentsHeight()) + ? autoscrollMargin*2 : 0)); + + bool accepted = false; + QListViewItem *i = itemAt( vp ); + if ( i ) { + accepted = acceptDrag(e); + if(accepted && !m_autoscrollTimer.isActive()) { + if (dropHighlighter()) { + QRect tmpRect = drawItemHighlighter(0, i); + if (tmpRect != m_lastDropHighlighter) { + cleanItemHighlighter(); + m_lastDropHighlighter = tmpRect; + viewport()->repaint(tmpRect); + } + } + } + if ( !inside_margin.contains(vp) ) { + slotStartAutoScroll(); + e->accept(QRect(0,0,0,0)); // Keep sending move events + m_autoopenTimer.stop(); + + } else { + if(accepted) + e->accept(); + else + e->ignore(); + if ( i != m_dropItem ) { + m_autoopenTimer.stop(); + m_dropItem = i; + m_autoopenTimer.start( autoopenTime ); + } + } + if ( accepted ) { + switch ( e->action() ) { + case QDropEvent::Copy: + case QDropEvent::Link: + break; + case QDropEvent::Move: + e->acceptAction(); + break; + default: + break; + } + } + } else { + e->ignore(); + m_autoopenTimer.stop(); + m_dropItem = 0; + } + + if(!accepted && dropHighlighter()) + cleanItemHighlighter(); +} + +void KMyMoneyAccountTreeBase::cleanItemHighlighter(void) +{ + if(m_lastDropHighlighter.isValid()) { + QRect rect=m_lastDropHighlighter; + m_lastDropHighlighter = QRect(); + // make sure, we repaint a bit more. that's important during + // autoscroll. if we don't do that, parts of the highlighter + // do not get removed + rect.moveBy(-1, -1); + rect.setSize(rect.size() + QSize(2,2)); + viewport()->repaint(rect, true); + } +} + +void KMyMoneyAccountTreeBase::viewportPaintEvent(QPaintEvent* e) +{ + QListView::viewportPaintEvent(e); + + if (m_lastDropHighlighter.isValid() && e->rect().intersects(m_lastDropHighlighter)) { + QPainter painter(viewport()); + + // This is where we actually draw the drop-highlighter + style().drawPrimitive(QStyle::PE_FocusRect, &painter, m_lastDropHighlighter, colorGroup(), + QStyle::Style_FocusAtBorder); + } +} + + + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +const MyMoneyObject& KMyMoneyAccountTreeBaseItem::itemObject(void) const +{ + if(m_type == Institution) + return m_institution; + return m_account; +} + +KMyMoneyAccountTreeBaseItem::KMyMoneyAccountTreeBaseItem(KListView *parent, const MyMoneyInstitution& institution) : + KListViewItem(parent), + m_totalValue(MyMoneyMoney(0)), + m_negative(false), + m_institution(institution), + m_type(Institution) +{ + setName(); +} + +KMyMoneyAccountTreeBaseItem::KMyMoneyAccountTreeBaseItem(KListView *parent, const MyMoneyAccount& account, const MyMoneySecurity& security, const QString& name) : + KListViewItem(parent), + m_security(security), + m_totalValue(MyMoneyMoney(0)), + m_account(account), + m_negative(false), + m_type(Account) +{ + if(!name.isEmpty()) { + // we do not want to modify the original account + MyMoneyAccount acc(account); + acc.setName(name); + m_account = acc; + } + setName(); +} + +KMyMoneyAccountTreeBaseItem::KMyMoneyAccountTreeBaseItem(KMyMoneyAccountTreeBaseItem *parent, const MyMoneyAccount& account, const QValueList<MyMoneyPrice>& price, const MyMoneySecurity& security) : + KListViewItem(parent), + m_price(price), + m_security(security), + m_totalValue(MyMoneyMoney(0)), + m_account(account), + m_negative(false), + m_type(Account) +{ + setName(); +} + +KMyMoneyAccountTreeBaseItem::~KMyMoneyAccountTreeBaseItem() +{ +} + +const QString& KMyMoneyAccountTreeBaseItem::id(void) const +{ + if(m_type == Institution) + return m_institution.id(); + return m_account.id(); +} + +bool KMyMoneyAccountTreeBaseItem::isChildOf(const QListViewItem* const item) const +{ + QListViewItem *p = parent(); + while(p && p != item) { + p = p->parent(); + } + return (p != 0); +} + +MyMoneyMoney KMyMoneyAccountTreeBaseItem::value() const +{ + // calculate the new value by running down the price list + MyMoneyMoney result = balance(); + QValueList<MyMoneyPrice>::const_iterator it_p; + QString security = m_security.id(); + for(it_p = m_price.begin(); it_p != m_price.end(); ++it_p) { + result = (result * (MyMoneyMoney(1,1) / (*it_p).rate(security))).convert(MyMoneyMoney::precToDenom(KMyMoneyGlobalSettings::pricePrecision())); + if((*it_p).from() == security) + security = (*it_p).to(); + else + security = (*it_p).from(); + } + if (listView()) + result = result.convert(listView()->baseCurrency().smallestAccountFraction()); + return result; +} + +void KMyMoneyAccountTreeBaseItem::setName() +{ + KMyMoneyAccountTreeBase* lv = dynamic_cast<KMyMoneyAccountTreeBase*>(listView()); + if (!lv) + return; + if (isInstitution()) { + setPixmap(lv->nameColumn(), m_institution.pixmap()); + setText(lv->nameColumn(), m_institution.name()); + } else { + setPixmap(lv->nameColumn(), m_account.accountPixmap(false, 22)); + setText(lv->nameColumn(), m_account.name()); +#ifndef KMM_DESIGNER + if(lv->typeColumn()>=0 && !MyMoneyFile::instance()->isStandardAccount(m_account.id())) + setText(lv->typeColumn(), KMyMoneyUtils::accountTypeToString(m_account.accountType())); +#endif + } +} + +void KMyMoneyAccountTreeBaseItem::fillColumns() +{ + KMyMoneyAccountTreeBase* lv = dynamic_cast<KMyMoneyAccountTreeBase*>(listView()); + if (!lv) + return; + if (lv->valueColumn()<0) + return; + // show the top accounts always in total value + if((isOpen() || m_account.accountList().count() == 0) && parent()) { + + // only show the balance, if its a different security/currency + if(m_security.id() != listView()->baseCurrency().id()) { + setText(lv->balanceColumn(), balance().formatMoney(m_security)); + } + setText(lv->valueColumn(), m_value.formatMoney(listView()->baseCurrency()) + " "); + + } else { + setText(lv->balanceColumn(), " "); + if(parent()) + setText(lv->valueColumn(), m_totalValue.formatMoney(listView()->baseCurrency()) + " "); + else + setText(lv->valueColumn(), m_totalValue.formatMoney(listView()->baseCurrency())); + } +} + +void KMyMoneyAccountTreeBaseItem::updateAccount(bool forceTotalUpdate) +{ + MyMoneyMoney oldValue = m_value; + m_value = value(); + + fillColumns(); + + // check if we need to tell upstream account objects in the tree + // that the value has changed + if(oldValue != m_value || forceTotalUpdate) { + adjustTotalValue(m_value - oldValue); + if (listView()) + listView()->emitValueChanged(); + } +} + +void KMyMoneyAccountTreeBaseItem::setOpen(bool open) +{ + if (open == isOpen()) + return; + KListViewItem::setOpen(open); + fillColumns(); + + if(listView()) + listView()->queueSort(); +} + +void KMyMoneyAccountTreeBaseItem::adjustTotalValue(const MyMoneyMoney& diff) +{ + m_totalValue += diff; + + // 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 + KMyMoneyAccountTreeBase* lv = dynamic_cast<KMyMoneyAccountTreeBase*>(listView()); + if(!lv) + return; + if(!firstChild() || !parent() || (!isOpen() && firstChild())) { + if(firstChild()) + setText(lv->balanceColumn(), " "); + if(parent()) + setText(lv->valueColumn(), m_totalValue.formatMoney(listView()->baseCurrency()) + " "); + else + setText(lv->valueColumn(), m_totalValue.formatMoney(listView()->baseCurrency())); + } + + // now make sure, the upstream accounts also get notified about the value change + KMyMoneyAccountTreeBaseItem* p = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(parent()); + if(p != 0) { + p->adjustTotalValue(diff); + } +} + +int KMyMoneyAccountTreeBaseItem::compare(QListViewItem* i, int col, bool ascending) const +{ + KMyMoneyAccountTreeBaseItem* item = dynamic_cast<KMyMoneyAccountTreeBaseItem*>(i); + // do special sorting only if + // a) name + // b) account + // c) and different group + // d) value column + // in all other cases use the standard sorting + KMyMoneyAccountTreeBase* lv = dynamic_cast<KMyMoneyAccountTreeBase*>(listView()); + if(lv && item) { + if (col == lv->nameColumn()) { + if(m_account.accountGroup() != item->m_account.accountGroup()) + return (m_account.accountGroup() - item->m_account.accountGroup()); + } else if (col == lv->balanceColumn() || col == lv->valueColumn()) { + MyMoneyMoney result = MyMoneyMoney(text(col)) - MyMoneyMoney(item->text(col)); + if(result.isNegative()) + return -1; + if(result.isZero()) + return 0; + return 1; + } + } + // do standard sorting here + return KListViewItem::compare(i, col, ascending); +} + +void KMyMoneyAccountTreeBaseItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align) +{ + QColorGroup cg2(cg); + + //set item background + if(isAlternate()) + cg2.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listColor()); + else + cg2.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listBGColor()); + +#ifndef KMM_DESIGNER + // display base accounts in bold + QFont font = KMyMoneyGlobalSettings::listCellFont(); + if(!parent()) + font.setBold(true); + + // strike out closed accounts + if(m_account.isClosed()) + font.setStrikeOut(true); + + p->setFont(font); +#endif + //set text color + QColor textColour; + if(m_negative == true) { + textColour = KMyMoneyGlobalSettings::listNegativeValueColor(); //if the item is marked is marked as negative, all columns will be painted negative + } else { + textColour = m_columnsColor[column]; //otherwise, respect the color for each column + } + cg2.setColor(QColorGroup::Text, textColour); + + QListViewItem::paintCell(p, cg2, column, width, align); +} + +void KMyMoneyAccountTreeBase::expandCollapseAll(bool expand) +{ + QListViewItemIterator it(this); + QListViewItem* p; + while((p = it.current()) != 0) { + p->setOpen(expand); + ++it; + } +} + +void KMyMoneyAccountTreeBase::slotExpandAll(void) +{ + expandCollapseAll(true); +} + +void KMyMoneyAccountTreeBase::slotCollapseAll(void) +{ + expandCollapseAll(false); +} + +void KMyMoneyAccountTreeBase::queueSort(void) +{ + if (sortColumn() == balanceColumn() || sortColumn() == valueColumn()) { + ++m_queuedSort; + QTimer::singleShot(100, this, SLOT(slotActivateSort())); + } +} + +void KMyMoneyAccountTreeBase::slotActivateSort(void) +{ + --m_queuedSort; + if(!m_queuedSort) + KListView::sort(); +} + +void KMyMoneyAccountTreeBaseItem::setNegative(bool isNegative) +{ + m_negative = isNegative; +} + +void KMyMoneyAccountTreeBaseItem::setText( int column, const QString &text, const bool &negative) +{ + //if negative set the map to negative color according to KMyMoneySettings + if(negative) { + m_columnsColor[column] = KMyMoneyGlobalSettings::listNegativeValueColor(); + } else { + m_columnsColor[column] = QColorGroup::Text; + } + + KListViewItem::setText(column, text); +} + + +#include "kmymoneyaccounttreebase.moc" +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/kmymoneyaccounttreebase.h b/kmymoney2/widgets/kmymoneyaccounttreebase.h new file mode 100644 index 0000000..4ee6a32 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreebase.h @@ -0,0 +1,475 @@ +/*************************************************************************** + kmymoneyaccounttreebase.h - description + ------------------- + begin : Sat Jan 1 2005 + copyright : (C) 2005 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYACCOUNTTREEBASE_H +#define KMYMONEYACCOUNTTREEBASE_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtimer.h> +class QDragObject; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/mymoneyinstitution.h> +#include <kmymoney/mymoneyprice.h> +#include <kmymoney/mymoneysecurity.h> +#include <kmymoney/mymoneybudget.h> + +class KMyMoneyAccountTreeBaseItem; + +class KMyMoneyAccountTreeBase : public KListView +{ + friend class KMyMoneyAccountTreeBaseItem; + + Q_OBJECT +public: + KMyMoneyAccountTreeBase(QWidget* parent = 0, const char *name = 0); + virtual ~KMyMoneyAccountTreeBase(); + + /** + * Modify the text shown in the header of the name column. + * + * @param txt the text to be used in the header + */ + void setSectionHeader(const QString& txt); + + /** + * overridden from base class implementation to return a pointer + * to a KMyMoneyAccountTreeBaseItem. + * + * @return pointer to currently selected item + */ + KMyMoneyAccountTreeBaseItem* selectedItem(void) const; + + /** + */ + void setBaseCurrency(const MyMoneySecurity& currency) { m_baseCurrency = currency; }; + + const MyMoneySecurity& baseCurrency(void) const { return m_baseCurrency; }; + + void emitValueChanged(void) { emit valueChanged(); }; + + /** + * restores the layout from the config file + * @param group the group to be used from the config file. + * At destruction time, we will use this group name to save + * the layout. + */ + void restoreLayout(const QString& group); + +public slots: + /** autoscroll support */ + void slotStartAutoScroll(void); + void slotStopAutoScroll(void); + void slotExpandAll(void); + void slotCollapseAll(void); + +protected: + virtual bool acceptDrag (QDropEvent* event) const; + virtual void startDrag(void); + const KMyMoneyAccountTreeBaseItem* findItem(const QString& id) const; + + /** + * This method checks, if account @p accFrom can be dropped onto + * account @p accTo. + * + * @param accFrom source account + * @param accTo new parent account for @p accFrom + * @retval true drop is ok + * @retval false drop is not ok (@p accTo cannot be parent of @p accFrom) + */ + bool dropAccountOnAccount(const MyMoneyAccount& accFrom, const MyMoneyAccount& accTo) const; + // virtual void contentsDropEvent(QDropEvent*); + + /** + * This member counts the connects to the signals + * newAccountParent(const MyMoneyAccount&, const MyMoneyAccount&)) and + * newAccountParent(const MyMoneyAccount&, const MyMoneyInstitution&)) + * in m_accountConnections and m_institutionConnections. + */ + void connectNotify(const char *); + + /** + * This member counts the disconnects from the signals + * newAccountParent(const MyMoneyAccount&, const MyMoneyAccount&)) and + * newAccountParent(const MyMoneyAccount&, const MyMoneyInstitution&)) + * in m_accountConnections and m_institutionConnections. + */ + void disconnectNotify(const char *); + + void contentsDragMoveEvent( QDragMoveEvent *e ); + + /** + * Reimplemented for internal reasons. + * Further reimplementations should call this function or else + * some features may not work correctly. + * + * The API is unaffected. + */ + virtual void viewportPaintEvent(QPaintEvent*); + + void expandCollapseAll(bool expand); + + void queueSort(void); + +protected slots: + void slotObjectDropped(QDropEvent* event, QListViewItem* parent, QListViewItem* after); + + /** + * Select the object pointed to by @p i. This slot emits selectObject signals + * with an emtpy MyMoneyAccount and an empty MyMoneyInstitution object + * to deselect current selections. If @p i points to a KMyMoneyAccountTreeItem + * object, it emits selectObject() for this item. + * + * @param i pointer to QListViewItem of object to be selected + */ + void slotSelectObject(QListViewItem *i); + + /** + * This slot is connected to the accout list view's contextMenu signal + * and checks if the item pointed to by @p i is either an account or institution + * and sends out the necessary signal openContextMenu. + * + * @param lv pointer to KListView + * @param i pointer to QListViewItem + * @param p position information + */ + void slotOpenContextMenu(KListView* lv, QListViewItem* i, const QPoint& p); + + /** + * This slot is connected to the accout list view's executed signal + * and checks if the item pointed to by @p i is either an account or institution + * and sends out the necessary signal openObject. + * + * @param i pointer to QListViewItem + */ + void slotOpenObject(QListViewItem* i); + + void slotAutoScroll(void); + + /** Open the folder pointed to by m_dropItem */ + void slotOpenFolder(void); + + /** override KListView implementation */ + void cleanItemHighlighter(void); + + void slotActivateSort(void); + +private: + MyMoneySecurity m_baseCurrency; + bool m_accountConnections; + bool m_institutionConnections; + QTimer m_autoopenTimer; + QTimer m_autoscrollTimer; + int m_autoscrollTime; + int m_autoscrollAccel; + QListViewItem* m_dropItem; + QRect m_lastDropHighlighter; + int m_queuedSort; + int m_nameColumn; + int m_typeColumn; + int m_valueColumn; + int m_balanceColumn; + QString m_configGroup; + +public: + int typeColumn(void) const { return m_typeColumn; } + int nameColumn(void) const { return m_nameColumn; } + int balanceColumn(void) const { return m_balanceColumn; } + int valueColumn(void) const { return m_valueColumn; } + + void showType(void); + void showValue(void); + + +signals: + /** + * This signal is emitted whenever an object in the view is selected + * + * @param obj reference to actual MyMoneyObject (is either + * MyMoneyAccount or MyMoneyInstitution depending on selected item) + */ + void selectObject(const MyMoneyObject& obj); + + /** + * This signal is emitted whenever the user requests the context menu for an object + * + * @param obj reference to actual MyMoneyObject (is either + * MyMoneyAccount or MyMoneyInstitution depending on selected item) + */ + void openContextMenu(const MyMoneyObject& obj); + + /** + * This signal is emitted whenever the user requests to open an object + * + * @param obj reference to actual MyMoneyObject (is either + * MyMoneyAccount or MyMoneyInstitution depending on selected item) + */ + void openObject(const MyMoneyObject& obj); + + /** + * This signal is emitted whenever the value of an object changed + */ + void valueChanged(void); + + /** + * This signal is emitted, when the user selected to reparent the + * account @p acc to be a subordinate account of @p parent. + * + * @param acc const reference to account to be reparented + * @param parent const reference to new parent account + */ + void reparent(const MyMoneyAccount& acc, const MyMoneyAccount& parent); + + /** + * This signal is emitted, when the user selected to reparent the + * account @p acc to be a subordinate account of @p institution. + * + * @param acc const reference to account to be reparented + * @param institution const reference to new institution + */ + void reparent(const MyMoneyAccount& acc, const MyMoneyInstitution& institution); +}; + +class KMyMoneyAccountTreeBaseItem : public KListViewItem +{ +public: + typedef enum { + Account, + Institution + } KMyMoneyAccountTreeItemType; + + /** + * Constructor to be used to construct an institution entry + * object. + * + * @param parent pointer to the KListView object this entry should be + * added to. + * @param institution const reference to MyMoneyInstitution for which + * the KListView entry is constructed + */ + KMyMoneyAccountTreeBaseItem(KListView *parent, const MyMoneyInstitution& institution); + + /** + * Constructor to be used to construct a standard account entry object (e.g. Asset, + * Liability, etc.). + * + * @param parent pointer to the KListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + * @param name name of the account to be used instead of the one stored with @p account + * If empty, the one stored with @p account will be used. Default: empty + */ + KMyMoneyAccountTreeBaseItem(KListView *parent, const MyMoneyAccount& account, const MyMoneySecurity& security = MyMoneySecurity(), const QString& name = QString()); + + /** + * Constructor to be used to construct an account entry + * object. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param price price to be used to calculate value (defaults to 1) + * This is used for accounts denominated in foreign currencies or stocks + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + */ + KMyMoneyAccountTreeBaseItem(KMyMoneyAccountTreeBaseItem *parent, const MyMoneyAccount& account, const QValueList<MyMoneyPrice>& price = QValueList<MyMoneyPrice>(), const MyMoneySecurity& security = MyMoneySecurity()); + + ~KMyMoneyAccountTreeBaseItem(); + + /** + * populates the columns. Derived classes should override this. The + * name column is already filled and should not be changed. + */ + virtual void fillColumns(); + + /** + * This method loads new information into the item and updates the fields + * + * @param forceTotalUpdate set to true to force update of total values + * (used in constructor, should not be necessary to + * be set by application code) + * + */ + void updateAccount(bool forceTotalUpdate = false); + + /** + * This method checks, if the item contains an account or not. + * + * @retval true item holds an account + * @retval false item does not hold an account + */ + bool isAccount(void) const { return m_type == Account; }; + + /** + * This method checks, if the item contains an institution or not. + * + * @retval true item holds an institution + * @retval false item does not hold an institution + */ + bool isInstitution(void) const { return m_type == Institution; }; + + /** + * This method returns the id of the object held by this item + * + * @return const reference to id of object + */ + const QString& id(void) const; + + /** + * Helper method to show the right order + */ + int compare(QListViewItem* i, int col, bool ascending) const; + + /** + * If o is TRUE all child items are shown initially. The user can + * hide them by clicking the - icon to the left of the item. If + * o is FALSE, the children of this item are initially hidden. + * The user can show them by clicking the + icon to the left of the item. + * + * Overrides KListViewItem::setOpen() and exchanges the value field + * with either the value of this account and its subaccounts if @p o + * is false or the value of this account if @p o is true. + * + * @param o show item open (true) or closed (false) + */ + virtual void setOpen(bool o); + + /** + * This method is re-implemented from QListViewItem::paintCell(). + * Besides the standard implementation, the QPainter is set + * according to the applications settings. + */ + void paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int align); + + /** + * Convenience method to return casted pointer + */ + KMyMoneyAccountTreeBase* listView(void) const { return dynamic_cast<KMyMoneyAccountTreeBase*>(KListViewItem::listView()); }; + + /** + * Return the type of entry + * + * @return type of this entry. + */ + KMyMoneyAccountTreeItemType entryType(void) const { return m_type; }; + + /** + * This method returns a const reference to this item (either the m_account or m_institution) + * depending on m_type. + * + * @return reference to the MyMoneyObject of this entry + */ + const MyMoneyObject& itemObject(void) const; + + /** + * This method returns the value of this account and all it's subordinate accounts. + * + * @return value of this account including all subordinate accounts + */ + const MyMoneyMoney& totalValue(void) const { return m_totalValue; }; + + /** + * This method adjusts the current total value by @p diff. + * + * @param diff difference to be added to the current value to + * get the new value + */ + void adjustTotalValue(const MyMoneyMoney& diff); + + /** + * Checks whether this object is a child of the one passed + * by @p item. + * + * @param item pointer to other KMyMoneyAccountTreeItem that + * should be checked for parent/grand-parenthood of this + * object + * @retval true @p this object is a decendant of @p item + * @retval false @p this object is no decendant of @p item + */ + bool isChildOf(const QListViewItem* const item) const; + + /** + * Sets the whole item to be shown with negative colors + */ + void setNegative(bool isNegative); + + /** + * Sets the text of a given column. @param negative indicates whether it should + * be shown as negative number or not + */ + void setText( int column, const QString &text, const bool &negative = false ); + +protected: + /** + * Returns the current balance of this account. + * + * This is a pure virtual function, to allow subclasses to calculate + * the balance in different ways. + * + * Parent items in the tree will only be recomputed if the balance() for + * a child changes. + * @param account Account to get the balance for + * @return Balance of this account + */ + virtual MyMoneyMoney balance() const = 0; + + /** + * Computes and returns the current value of the account held by this item. + * This is the same as balance() but in the currency of the view. + * if value() changed since the item has been displayed, updateAccount() + * will notify the parent. + * @return value of the account held by this item + */ + MyMoneyMoney value() const; + +protected: + MyMoneyMoney m_value; + QValueList<MyMoneyPrice> m_price; + MyMoneySecurity m_security; + MyMoneyMoney m_totalValue; + MyMoneyAccount m_account; + QMap<int, QColor> m_columnsColor; + bool m_negative; + +private: + MyMoneyInstitution m_institution; + KMyMoneyAccountTreeItemType m_type; + + /** + * fills the name column with text and pixmap + */ + void setName(); + +}; + +#endif + diff --git a/kmymoney2/widgets/kmymoneyaccounttreebudget.cpp b/kmymoney2/widgets/kmymoneyaccounttreebudget.cpp new file mode 100644 index 0000000..cd1da0b --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreebudget.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + kmymoneyaccounttreebudget.cpp - description + ------------------- + begin : Tue Feb 21 2006 + copyright : (C) 2005 by Darren Gould + email : Darren Gould <darren_gould@gmx.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// Project Includes +#include <kmymoneyaccounttreebudget.h> + +KMyMoneyAccountTreeBudget::KMyMoneyAccountTreeBudget(QWidget* parent, const char* name) : + KMyMoneyAccountTreeBase::KMyMoneyAccountTreeBase(parent, name) +{ + showType(); + showValue(); +} + +void KMyMoneyAccountTreeBudget::slotSelectObject(const QListViewItem* i) +{ + emit selectObject(MyMoneyInstitution()); + emit selectObject(MyMoneyAccount()); + + const KMyMoneyAccountTreeBaseItem* item = dynamic_cast<const KMyMoneyAccountTreeBaseItem*>(i); + if(item) { + emit openObject(item->itemObject()); + } +} + +KMyMoneyAccountTreeBudgetItem::KMyMoneyAccountTreeBudgetItem(KListView *parent, const MyMoneyAccount& account, const MyMoneyBudget &budget, const MyMoneySecurity& security, const QString& name) : + KMyMoneyAccountTreeBaseItem(parent, account, security, name), + m_budget(budget) +{ + updateAccount(true); +} + +KMyMoneyAccountTreeBudgetItem::KMyMoneyAccountTreeBudgetItem(KMyMoneyAccountTreeBudgetItem *parent, const MyMoneyAccount& account, const MyMoneyBudget& budget, const QValueList<MyMoneyPrice>& price, const MyMoneySecurity& security) : + KMyMoneyAccountTreeBaseItem(parent, account, price, security), + m_budget(budget) +{ + updateAccount(true); +} + + +KMyMoneyAccountTreeBudgetItem::~KMyMoneyAccountTreeBudgetItem() +{ +} + +void KMyMoneyAccountTreeBudgetItem::setBudget(const MyMoneyBudget& budget) +{ + m_budget = budget; + updateAccount(); +} + +MyMoneyMoney KMyMoneyAccountTreeBudgetItem::balance() const +{ + MyMoneyMoney result = MyMoneyMoney(); + // find out if the account is budgeted + MyMoneyBudget::AccountGroup budgetAccount = m_budget.account( m_account.id() ); + if ( budgetAccount.id() == m_account.id() ) { + result = budgetAccount.balance(); + switch(budgetAccount.budgetLevel()) { + case MyMoneyBudget::AccountGroup::eMonthly: + result = result * 12; + break; + + default: + break; + } + } + return result; +} + +#include "kmymoneyaccounttreebudget.moc" diff --git a/kmymoney2/widgets/kmymoneyaccounttreebudget.h b/kmymoney2/widgets/kmymoneyaccounttreebudget.h new file mode 100644 index 0000000..86c1b07 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreebudget.h @@ -0,0 +1,112 @@ +/*************************************************************************** + kmymoneyaccounttreebudget.h - description + ------------------- + begin : Tue Feb 21 2006 + copyright : (C) 2005 by Darren Gould + email : Darren Gould <darren_gould@gmx.de> +***************************************************************************/ + +/*************************************************************************** + * * + * 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 KMYMONEYACCOUNTTREEBUDGET_H +#define KMYMONEYACCOUNTTREEBUDGET_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtimer.h> +class QDragObject; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneyaccounttree.h" +#include "../../kmymoney/mymoneybudget.h" + +class KMyMoneyAccountTreeBudgetItem; + +class KMyMoneyAccountTreeBudget : public KMyMoneyAccountTreeBase +{ + Q_OBJECT +public: + KMyMoneyAccountTreeBudget(QWidget* parent = 0, const char *name = 0); + virtual ~KMyMoneyAccountTreeBudget() {} + +public slots: + void slotSelectObject(const QListViewItem* i); + +}; + +class KMyMoneyAccountTreeBudgetItem : public KMyMoneyAccountTreeBaseItem +{ +public: + + /** + * Constructor to be used to construct an account + * entry object for a budget. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param budget const reference to the budget to + * which the account belongs + * @param price price to be used to calculate value (defaults to 1) + * This is used for accounts denominated in foreign currencies or stocks + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + */ + KMyMoneyAccountTreeBudgetItem(KMyMoneyAccountTreeBudgetItem *parent, const MyMoneyAccount& account, const MyMoneyBudget& budget, const QValueList<MyMoneyPrice>& price = QValueList<MyMoneyPrice>(), const MyMoneySecurity& security = MyMoneySecurity()); + + /** + * Constructor to be used to construct an account + * entry object for a budget. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param budget const reference to the budget to + * which the account belongs + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + * @param name name of the account to be used instead of the one stored with @p account + * If empty, the one stored with @p account will be used. Default: empty + */ + KMyMoneyAccountTreeBudgetItem(KListView *parent, const MyMoneyAccount& account, const MyMoneyBudget &budget, const MyMoneySecurity& security = MyMoneySecurity(), const QString& name = QString()); + + ~KMyMoneyAccountTreeBudgetItem(); + + void setBudget(const MyMoneyBudget& budget); + +protected: + /** + * Returns the current balance of this account. + * + * This is a pure virtual function, to allow subclasses to calculate + * the balance in different ways. + * + * Parent items in the tree will only be recomputed if the balance() for + * a son changes. + * @param account Account to get the balance for + * @return Balance of this account + */ + MyMoneyMoney balance() const; + +private: + MyMoneyBudget m_budget; +}; + +#endif + diff --git a/kmymoney2/widgets/kmymoneyaccounttreeforecast.cpp b/kmymoney2/widgets/kmymoneyaccounttreeforecast.cpp new file mode 100644 index 0000000..0da5194 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreeforecast.cpp @@ -0,0 +1,401 @@ +/*************************************************************************** + kmymoneyaccounttreeforecast.cpp + ------------------- + begin : Fri Aug 01 2008 + copyright : (C) 2008 by Alvaro Soliverez + email : asoliverez@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// KDE Includes +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include <kmymoney/mymoneyfile.h> +#include <kmymoneyaccounttreeforecast.h> +#include "../kmymoney2.h" +#include "../kmymoneyglobalsettings.h" +#include "../mymoney/mymoneyforecast.h" +#include "../reports/reportaccount.h" + +using namespace reports; + +KMyMoneyAccountTreeForecast::KMyMoneyAccountTreeForecast(QWidget* parent, const char* name) : + KMyMoneyAccountTreeBase::KMyMoneyAccountTreeBase(parent, name) +{ + setResizeMode(QListView::NoColumn); +} + +void KMyMoneyAccountTreeForecast::showAccount( void ) +{ + addColumn(i18n("Account")); +} + +void KMyMoneyAccountTreeForecast::clearColumns( void ) +{ + clear(); + while(columns() > 0) { + removeColumn(0); + } +} + +void KMyMoneyAccountTreeForecast::showSummary(MyMoneyForecast& forecast) +{ + int daysToBeginDay; + + //add cycle interval columns + addColumn(i18n("Current"), -1); + + //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(); + } + for(int i = 0; ((i*forecast.accountsCycle())+daysToBeginDay) <= forecast.forecastDays(); ++i) { + int intervalDays = ((i*forecast.accountsCycle())+daysToBeginDay); + QString columnName = i18n("%1 days").arg(intervalDays, 0, 10); + addColumn(columnName, -1); + } + + //add variation columns + addColumn(i18n("Total variation"), -1); + + //align columns + for(int i = 1; i < columns(); ++i) { + setColumnAlignment(i, Qt::AlignRight); + } + +} + +void KMyMoneyAccountTreeForecast::showDetailed(MyMoneyForecast& forecast) +{ + //add cycle interval columns + addColumn(i18n("Current"), -1); + + for(int i = 1; i <= forecast.forecastDays(); ++i) { + QDate forecastDate = QDate::currentDate().addDays(i); + QString columnName = KGlobal::locale()->formatDate(forecastDate, true); + addColumn(columnName, -1); + } + + //add variation columns + addColumn(i18n("Total variation"), -1); + + //align columns + for(int i = 1; i < columns(); ++i) { + setColumnAlignment(i, Qt::AlignRight); + } +} + +void KMyMoneyAccountTreeForecast::showAdvanced(MyMoneyForecast& forecast) +{ + int daysToBeginDay; + + //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) { + int col = addColumn(i18n("Min Bal %1").arg(i), -1); + setColumnAlignment(col, Qt::AlignRight); + addColumn(i18n("Min Date %1").arg(i), -1); + } + for(int i = 1; ((i * forecast.accountsCycle()) + daysToBeginDay) <= forecast.forecastDays(); ++i) { + int col = addColumn(i18n("Max Bal %1").arg(i), -1); + setColumnAlignment(col, Qt::AlignRight); + addColumn(i18n("Max Date %1").arg(i), -1); + } + int col = addColumn(i18n("Average"), -1); + setColumnAlignment(col, Qt::AlignRight); +} + +void KMyMoneyAccountTreeForecast::showBudget(MyMoneyForecast& forecast) +{ + 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)) { + QString columnName = QDate::longMonthName(f_date.month()); + addColumn(columnName, -1); + } + //add total column + addColumn(i18n("Total"), -1); + + + //align columns + for(int i = 1; i < columns(); ++i) { + setColumnAlignment(i, Qt::AlignRight); + } +} + +void KMyMoneyAccountTreeForecast::slotSelectObject(const QListViewItem* i) +{ + emit selectObject(MyMoneyInstitution()); + emit selectObject(MyMoneyAccount()); + + const KMyMoneyAccountTreeBaseItem* item = dynamic_cast<const KMyMoneyAccountTreeBaseItem*>(i); + if(item) { + emit openObject(item->itemObject()); + } +} + +KMyMoneyAccountTreeForecastItem::KMyMoneyAccountTreeForecastItem(KListView *parent, const MyMoneyAccount& account, const MyMoneyForecast &forecast, const MyMoneySecurity& security, const QString& name) : + KMyMoneyAccountTreeBaseItem(parent, account, security, name), + m_forecast(forecast) +{ + updateAccount(true); + +} + +KMyMoneyAccountTreeForecastItem::KMyMoneyAccountTreeForecastItem(KMyMoneyAccountTreeForecastItem *parent, const MyMoneyAccount& account, const MyMoneyForecast& forecast, const QValueList<MyMoneyPrice>& price, const MyMoneySecurity& security, const EForecastViewType forecastType) : + KMyMoneyAccountTreeBaseItem(parent, account, price, security), + m_forecast(forecast), + m_forecastType(forecastType) +{ + //setForecastViewType(forecastViewType); + updateAccount(true); + switch(forecastViewType()) + { + case eSummary: + updateSummary(); + break; + case eDetailed: + updateDetailed(); + break; + case eBudget: + updateBudget(); + break; + default: + break; + } +} + + +KMyMoneyAccountTreeForecastItem::~KMyMoneyAccountTreeForecastItem() +{ +} + +void KMyMoneyAccountTreeForecastItem::setForecast(const MyMoneyForecast& forecast) +{ + m_forecast = forecast; + updateAccount(); +} + +void KMyMoneyAccountTreeForecastItem::updateSummary() +{ + MyMoneyMoney amountMM; + int it_c = 1; // iterator for the columns of the listview + MyMoneyFile* file = MyMoneyFile::instance(); + int daysToBeginDay; + + if(QDate::currentDate() < m_forecast.beginForecastDate()) { + daysToBeginDay = QDate::currentDate().daysTo(m_forecast.beginForecastDate()); + } else { + daysToBeginDay = m_forecast.accountsCycle(); + } + + MyMoneySecurity currency; + if(m_account.isInvest()) { + MyMoneySecurity underSecurity = file->security(m_account.currencyId()); + currency = file->security(underSecurity.tradingCurrency()); + } else { + currency = file->security(m_account.currencyId()); + } + + + //add current balance column + QDate summaryDate = QDate::currentDate(); + amountMM = m_forecast.forecastBalance(m_account, summaryDate); + + //calculate the balance in base currency for the total row + setAmount(it_c, amountMM); + setValue(it_c, amountMM, summaryDate); + showAmount(it_c, amountMM, currency); + it_c++; + + //iterate through all other columns + for(QDate summaryDate = QDate::currentDate().addDays(daysToBeginDay); summaryDate <= m_forecast.forecastEndDate();summaryDate = summaryDate.addDays(m_forecast.accountsCycle()), ++it_c) { + amountMM = m_forecast.forecastBalance(m_account, summaryDate); + + //calculate the balance in base currency for the total row + setAmount(it_c, amountMM); + setValue(it_c, amountMM, summaryDate); + showAmount(it_c, amountMM, currency); + } + //calculate and add variation per cycle + setNegative(m_forecast.accountTotalVariation(m_account).isNegative()); + setAmount(it_c, m_forecast.accountTotalVariation(m_account)); + setValue(it_c, m_forecast.accountTotalVariation(m_account), m_forecast.forecastEndDate()); + showAmount(it_c, m_forecast.accountTotalVariation(m_account), currency); +} + +void KMyMoneyAccountTreeForecastItem::updateDetailed() +{ + QString amount; + QString vAmount; + MyMoneyMoney vAmountMM; + MyMoneyFile* file = MyMoneyFile::instance(); + + MyMoneySecurity currency; + if(m_account.isInvest()) { + MyMoneySecurity underSecurity = file->security(m_account.currencyId()); + currency = file->security(underSecurity.tradingCurrency()); + } else { + currency = file->security(m_account.currencyId()); + } + + int it_c = 1; // iterator for the columns of the listview + + for(QDate forecastDate = QDate::currentDate(); forecastDate <= m_forecast.forecastEndDate(); ++it_c, forecastDate = forecastDate.addDays(1)) { + MyMoneyMoney amountMM = m_forecast.forecastBalance(m_account, forecastDate); + + //calculate the balance in base currency for the total row + setAmount(it_c, amountMM); + setValue(it_c, amountMM, forecastDate); + showAmount(it_c, amountMM, currency); + } + + //calculate and add variation per cycle + vAmountMM = m_forecast.accountTotalVariation(m_account); + setAmount(it_c, vAmountMM); + setValue(it_c, vAmountMM, m_forecast.forecastEndDate()); + showAmount(it_c, vAmountMM, currency); +} + +void KMyMoneyAccountTreeForecastItem::updateBudget() +{ + MyMoneySecurity currency; + MyMoneyMoney tAmountMM; + + MyMoneyFile* file = MyMoneyFile::instance(); + int it_c = 1; // iterator for the columns of the listview + QDate forecastDate = m_forecast.forecastStartDate(); + + if(m_account.isInvest()) { + MyMoneySecurity underSecurity = file->security(m_account.currencyId()); + currency = file->security(underSecurity.tradingCurrency()); + } else { + currency = file->security(m_account.currencyId()); + } + + //iterate columns + for(; forecastDate <= m_forecast.forecastEndDate(); forecastDate = forecastDate.addMonths(1), ++it_c) { + MyMoneyMoney amountMM; + amountMM = m_forecast.forecastBalance(m_account,forecastDate); + if(m_account.accountType() == MyMoneyAccount::Expense) + amountMM = -amountMM; + + tAmountMM += amountMM; + setAmount(it_c, amountMM); + setValue(it_c, amountMM, forecastDate); + showAmount(it_c, amountMM, currency); + } + + //set total column + setAmount(it_c, tAmountMM); + setValue(it_c, tAmountMM, m_forecast.forecastEndDate()); + showAmount(it_c, tAmountMM, currency); +} + +MyMoneyMoney KMyMoneyAccountTreeForecastItem::balance() const +{ + return MyMoneyMoney(); +} + +void KMyMoneyAccountTreeForecastItem::showAmount(int column, const MyMoneyMoney amount, const MyMoneySecurity security) +{ + setText(column, amount.formatMoney(m_account, security), amount.isNegative() ); +} + +void KMyMoneyAccountTreeForecastItem::adjustParentValue(int column, const MyMoneyMoney& value) +{ + m_values[column] += value; + m_values[column] = m_values[column].convert(listView()->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 + KMyMoneyAccountTreeForecast* lv = dynamic_cast<KMyMoneyAccountTreeForecast*>(listView()); + if(!lv) + return; + if(!firstChild() || !parent() || (!isOpen() && firstChild()) + || depth() == 1 ) { + if(firstChild()) + setText(column, " "); + + showAmount(column, m_values[column], listView()->baseCurrency()); + } + + // now make sure, the upstream accounts also get notified about the value change + KMyMoneyAccountTreeForecastItem* p = dynamic_cast<KMyMoneyAccountTreeForecastItem*>(parent()); + if(p != 0) { + p->adjustParentValue(column, value); + } +} + +void KMyMoneyAccountTreeForecastItem::setValue(int column, MyMoneyMoney amount, QDate forecastDate) +{ + KMyMoneyAccountTreeForecastItem* p = dynamic_cast<KMyMoneyAccountTreeForecastItem*>(parent()); + + //calculate the balance in base currency for the total row + if(m_account.currencyId() != listView()->baseCurrency().id()) { + ReportAccount repAcc = ReportAccount(m_account.id()); + MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(forecastDate); + MyMoneyMoney baseAmountMM = amount * curPrice; + m_values[column] = baseAmountMM.convert(listView()->baseCurrency().smallestAccountFraction()); + + if(p != 0) { + p->adjustParentValue(column, m_values[column]); + } + } else { + m_values[column] += amount; + if(p != 0) { + p->adjustParentValue(column, amount); + } + } +} + +void KMyMoneyAccountTreeForecastItem::setAmount(int column, MyMoneyMoney amount) +{ + m_amounts[column] = amount; +} + +void KMyMoneyAccountTreeForecastItem::setOpen(bool open) +{ + if (open == isOpen()) + return; + KMyMoneyAccountTreeBaseItem::setOpen(open); + + if(!open) + { + for(int i = 1; i < listView()->columns(); ++i) + { + showAmount(i, m_values[i], listView()->baseCurrency()); + } + } else if (depth() > 1) { + for(int i = 1; i < listView()->columns(); ++i) + { + showAmount(i, m_amounts[i], m_security); + } + } +} + +#include "kmymoneyaccounttreeforecast.moc" diff --git a/kmymoney2/widgets/kmymoneyaccounttreeforecast.h b/kmymoney2/widgets/kmymoneyaccounttreeforecast.h new file mode 100644 index 0000000..925d2e9 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyaccounttreeforecast.h @@ -0,0 +1,168 @@ +/*************************************************************************** + kmymoneyaccounttreeforecast.h + ------------------- + begin : Fri Aug 01 2008 + copyright : (C) 2008 by Alvaro Soliverez + email : asoliverez@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 KMYMONEYACCOUNTTREEFORECAST_H +#define KMYMONEYACCOUNTTREEFORECAST_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtimer.h> +class QDragObject; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneyaccounttree.h" +#include "../../kmymoney/mymoneyforecast.h" + +class KMyMoneyAccountTreeForecastItem; + +class KMyMoneyAccountTreeForecast : public KMyMoneyAccountTreeBase +{ + Q_OBJECT + public: + KMyMoneyAccountTreeForecast(QWidget* parent = 0, const char *name = 0); + virtual ~KMyMoneyAccountTreeForecast() {} + + void showSummary(MyMoneyForecast& forecast); + void showDetailed(MyMoneyForecast& forecast); + void showAdvanced(MyMoneyForecast& forecast); + void showBudget(MyMoneyForecast& forecast); + void showAccount(void); + void clearColumns(void); + + public slots: + void slotSelectObject(const QListViewItem* i); + +}; + +class KMyMoneyAccountTreeForecastItem : public KMyMoneyAccountTreeBaseItem +{ +public: + + enum EForecastViewType { eSummary = 0, eDetailed, eAdvanced, eBudget, eUndefined }; + + /** + * Constructor to be used to construct an account + * entry object for a forecast. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param forecast const reference to the forecast to + * which the account belongs + * @param price price to be used to calculate value (defaults to 1) + * This is used for accounts denominated in foreign currencies or stocks + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + */ + KMyMoneyAccountTreeForecastItem(KMyMoneyAccountTreeForecastItem *parent, const MyMoneyAccount& account, const MyMoneyForecast& forecast, const QValueList<MyMoneyPrice>& price = QValueList<MyMoneyPrice>(), const MyMoneySecurity& security = MyMoneySecurity(), const EForecastViewType forecastViewType = eUndefined); + + /** + * Constructor to be used to construct an account + * entry object for a forecast. + * + * @param parent pointer to the parent KAccountListView object this entry should be + * added to. + * @param account const reference to MyMoneyAccount for which + * the KListView entry is constructed + * @param forecast const reference to the forecast to + * which the account belongs + * @param security const reference to the security used to show the value. Usually + * one should pass MyMoneyFile::baseCurrency() here. + * @param name name of the account to be used instead of the one stored with @p account + * If empty, the one stored with @p account will be used. Default: empty + */ + KMyMoneyAccountTreeForecastItem(KListView *parent, const MyMoneyAccount& account, const MyMoneyForecast &forecast, const MyMoneySecurity& security = MyMoneySecurity(), const QString& name = QString()); + + ~KMyMoneyAccountTreeForecastItem(); + + /** + * Sets the forecast object + */ + void setForecast(const MyMoneyForecast& forecast); + + /** + * updates the item with summary information. Used in Summary tab of Forecast View + */ + void updateSummary(void); + + /** + * updates the item with detailed information. Used in Detailed tab of Forecast View + */ + void updateDetailed(void); + + /** + * updates the item with budget forecast information. Used in Budget tab of Forecast View + */ + void updateBudget(void); + + /** + * sets when to begin a forecast cycle. This is used when showing forecast information per cycle, eg. + * on the summary tab of forecast view. + */ + void setDaysToBeginDay(int _days) {m_daysToBeginDay = _days;} + + /** + * sets the type of forecast that the time will show, eg. summary, detailed, budget + */ + void setForecastViewType(EForecastViewType forecastType) { m_forecastType = forecastType; } + + /** + * returns the forecast type of the item + */ + EForecastViewType forecastViewType(void) { return m_forecastType; } + + /** + * it executes some logic specific to this class before calling the same method on the base class + */ + virtual void setOpen(bool o); + +protected: + /** + * Returns the current balance of this account. + * + * This is a pure virtual function, to allow subclasses to calculate + * the balance in different ways. + * + * Parent items in the tree will only be recomputed if the balance() for + * a son changes. + * @param account Account to get the balance for + * @return Balance of this account + */ + MyMoneyMoney balance() const; + void showAmount(int column, const MyMoneyMoney amount, const MyMoneySecurity security); + void adjustParentValue(int column, const MyMoneyMoney& value); + void setValue(int column, MyMoneyMoney amount, QDate forecastDate); + void setAmount(int column, MyMoneyMoney amount); + +private: + MyMoneyForecast m_forecast; + int m_daysToBeginDay; + QMap<int, MyMoneyMoney> m_values; + QMap<int, MyMoneyMoney> m_amounts; + EForecastViewType m_forecastType; +}; + +#endif + diff --git a/kmymoney2/widgets/kmymoneybriefschedule.cpp b/kmymoney2/widgets/kmymoneybriefschedule.cpp new file mode 100644 index 0000000..82776af --- /dev/null +++ b/kmymoney2/widgets/kmymoneybriefschedule.cpp @@ -0,0 +1,191 @@ +/*************************************************************************** + kmymoneybriefschedule.cpp - description + ------------------- + begin : Sun Jul 6 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlabel.h> +#include <qlineedit.h> +#include <qtextedit.h> +#include <qtoolbutton.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kpushbutton.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyscheduled.h> +#include "kmymoneybriefschedule.h" +#include "../kmymoneyutils.h" + +KMyMoneyBriefSchedule::KMyMoneyBriefSchedule(QWidget *parent, const char *name ) + : kScheduleBriefWidget(parent,name, WStyle_Customize | WStyle_NoBorder) +{ + KIconLoader *ic = KGlobal::iconLoader(); + m_nextButton->setPixmap(BarIcon(QString::fromLatin1("1rightarrow"))); + m_prevButton->setPixmap(BarIcon(QString::fromLatin1("1leftarrow"))); + + connect(m_prevButton, SIGNAL(clicked()), this, SLOT(slotPrevClicked())); + connect(m_nextButton, SIGNAL(clicked()), this, SLOT(slotNextClicked())); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(hide())); + connect(m_skipButton, SIGNAL(clicked()), this, SLOT(slotSkipClicked())); + connect(m_buttonEnter, SIGNAL(clicked()), this, SLOT(slotEnterClicked())); + + KGuiItem skipGuiItem( i18n("&Skip"), + QIconSet(ic->loadIcon("player_fwd", KIcon::Small, KIcon::SizeSmall)), + i18n("Skip this transaction"), + i18n("Use this button to skip this transaction")); + m_skipButton->setGuiItem(skipGuiItem); + + KGuiItem enterGuiItem( i18n("&Enter"), + QIconSet(ic->loadIcon("key_enter", KIcon::Small, KIcon::SizeSmall)), + i18n("Record this transaction into the register"), + i18n("Use this button to record this transaction")); + m_buttonEnter->setGuiItem(enterGuiItem); +} + +KMyMoneyBriefSchedule::~KMyMoneyBriefSchedule() +{ +} + +void KMyMoneyBriefSchedule::setSchedules(QValueList<MyMoneySchedule> list, const QDate& date) +{ + m_scheduleList = list; + m_date = date; + + m_index = 0; + if (list.count() >= 1) + { + loadSchedule(); + } +} + +void KMyMoneyBriefSchedule::loadSchedule() +{ + try + { + if (m_index < m_scheduleList.count()) + { + MyMoneySchedule sched = m_scheduleList[m_index]; + + m_indexLabel->setText(i18n("%1 of %2") + .arg(QString::number(m_index+1)) + .arg(QString::number(m_scheduleList.count()))); + m_name->setText(sched.name()); + m_type->setText(KMyMoneyUtils::scheduleTypeToString(sched.type())); + m_account->setText(sched.account().name()); + QString text; + MyMoneyMoney amount = sched.transaction().splitByAccount(sched.account().id()).value(); + amount = amount.abs(); + + if (sched.willEnd()) + { + int transactions = sched.paymentDates(m_date, sched.endDate()).count()-1; + text = i18n("Payment on %1 for %2 with %3 transactions remaining occuring %4.") + .arg(KGlobal::locale()->formatDate(m_date, true)) + .arg(amount.formatMoney(sched.account().fraction())) + .arg(QString::number(transactions)) + .arg(i18n(sched.occurenceToString())); + } else { + text = i18n("Payment on %1 for %2 occuring %4.") + .arg(KGlobal::locale()->formatDate(m_date, true)) + .arg(amount.formatMoney(sched.account().fraction())) + .arg(i18n(sched.occurenceToString())); + } + + if (m_date < QDate::currentDate()) + { + if (sched.isOverdue()) + { + QDate startD = (sched.lastPayment().isValid()) ? + sched.lastPayment() : + sched.startDate(); + + if (m_date.isValid()) + startD = m_date; + + int days = startD.daysTo(QDate::currentDate()); + int transactions = sched.paymentDates(startD, QDate::currentDate()).count(); + + text += "<br><font color=red>"; + text += i18n("%1 days overdue (%2 occurences).") + .arg(QString::number(days)) + .arg(QString::number(transactions)); + text += "</color>"; + } + } + + m_details->setText(text); + + m_prevButton->setEnabled(true); + m_nextButton->setEnabled(true); + m_skipButton->setEnabled(sched.occurencePeriod() != MyMoneySchedule::OCCUR_ONCE); + + if (m_index == 0) + m_prevButton->setEnabled(false); + if (m_index == (m_scheduleList.count()-1)) + m_nextButton->setEnabled(false); + } + } + catch (MyMoneyException *e) + { + delete e; + } +} + +void KMyMoneyBriefSchedule::slotPrevClicked() +{ + if (m_index >= 1) + { + --m_index; + loadSchedule(); + } +} + +void KMyMoneyBriefSchedule::slotNextClicked() +{ + if (m_index < (m_scheduleList.count()-1)) + { + m_index++; + loadSchedule(); + } +} + +void KMyMoneyBriefSchedule::slotEnterClicked() +{ + hide(); + emit enterClicked(m_scheduleList[m_index], m_date); +} + +void KMyMoneyBriefSchedule::slotSkipClicked() +{ + hide(); + emit skipClicked(m_scheduleList[m_index], m_date); +} + +#include "kmymoneybriefschedule.moc" diff --git a/kmymoney2/widgets/kmymoneybriefschedule.h b/kmymoney2/widgets/kmymoneybriefschedule.h new file mode 100644 index 0000000..f910e35 --- /dev/null +++ b/kmymoney2/widgets/kmymoneybriefschedule.h @@ -0,0 +1,68 @@ +/*************************************************************************** + kmymoneybriefschedule.h - description + ------------------- + begin : Sun Jul 6 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYBRIEFSCHEDULE_H +#define KMYMONEYBRIEFSCHEDULE_H + + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qwidget.h> +#include <qstringlist.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes +#include "../widgets/kschedulebriefwidget.h" +#include "../mymoney/mymoneyfile.h" + +/** + *@author Michael Edwardes + */ + +class KMyMoneyBriefSchedule : public kScheduleBriefWidget { + Q_OBJECT +public: + KMyMoneyBriefSchedule(QWidget *parent=0, const char *name=0); + ~KMyMoneyBriefSchedule(); + void setSchedules(QValueList<MyMoneySchedule> list, const QDate& date); + +signals: + void enterClicked(const MyMoneySchedule&, const QDate&); + void skipClicked(const MyMoneySchedule&, const QDate&); + +protected slots: + void slotPrevClicked(); + void slotNextClicked(); + void slotEnterClicked(); + void slotSkipClicked(); + +private: + QValueList<MyMoneySchedule> m_scheduleList; + unsigned int m_index; + QDate m_date; + + void loadSchedule(); +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycalculator.cpp b/kmymoney2/widgets/kmymoneycalculator.cpp new file mode 100644 index 0000000..3e59331 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycalculator.cpp @@ -0,0 +1,448 @@ +/*************************************************************************** + kmymoneycalculator.cpp - description + ------------------- + begin : Sat Oct 19 2002 + copyright : (C) 2000-2002 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlabel.h> +#include <qsignalmapper.h> +#include <qregexp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneycalculator.h" + +kMyMoneyCalculator::kMyMoneyCalculator(QWidget* parent, const char *name) + : QFrame(parent, name) +{ + m_comma = KGlobal::locale()->monetaryDecimalSymbol()[0]; + m_clearOperandOnDigit = false; + + QGridLayout* grid = new QGridLayout(this, 5, 5, 1, 2); + + display = new QLabel(this); + display->setBackgroundColor(QColor("#BDFFB4")); + + display->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + display->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + grid->addMultiCellWidget(display, 0, 0, 0, 4); + + buttons[0] = new KPushButton("0", this); + buttons[1] = new KPushButton("1", this); + buttons[2] = new KPushButton("2", this); + buttons[3] = new KPushButton("3", this); + buttons[4] = new KPushButton("4", this); + buttons[5] = new KPushButton("5", this); + buttons[6] = new KPushButton("6", this); + buttons[7] = new KPushButton("7", this); + buttons[8] = new KPushButton("8", this); + buttons[9] = new KPushButton("9", this); + buttons[PLUS] = new KPushButton("+", this); + buttons[MINUS] = new KPushButton("-", this); + buttons[STAR] = new KPushButton("X", this); + buttons[COMMA] = new KPushButton(m_comma, this); + buttons[EQUAL] = new KPushButton("=", this); + buttons[SLASH] = new KPushButton("/", this); + buttons[CLEAR] = new KPushButton("C", this); + buttons[CLEARALL] = new KPushButton("AC", this); + buttons[PLUSMINUS] = new KPushButton("+-", this); + buttons[PERCENT] = new KPushButton("%", this); + + grid->addWidget(buttons[7], 1, 0); + grid->addWidget(buttons[8], 1, 1); + grid->addWidget(buttons[9], 1, 2); + grid->addWidget(buttons[4], 2, 0); + grid->addWidget(buttons[5], 2, 1); + grid->addWidget(buttons[6], 2, 2); + grid->addWidget(buttons[1], 3, 0); + grid->addWidget(buttons[2], 3, 1); + grid->addWidget(buttons[3], 3, 2); + grid->addWidget(buttons[0], 4, 1); + + grid->addWidget(buttons[COMMA], 4, 0); + grid->addWidget(buttons[PLUS], 3, 3); + grid->addWidget(buttons[MINUS], 4, 3); + grid->addWidget(buttons[STAR], 3, 4); + grid->addWidget(buttons[SLASH], 4, 4); + grid->addWidget(buttons[EQUAL], 4, 2); + grid->addWidget(buttons[PLUSMINUS], 2, 3); + grid->addWidget(buttons[PERCENT], 2, 4); + grid->addWidget(buttons[CLEAR], 1, 3); + grid->addWidget(buttons[CLEARALL], 1, 4); + + buttons[EQUAL]->setFocus(); + + op1 = 0.0; + stackedOp = op = 0; + operand = QString(); + changeDisplay("0"); + + // connect the digit signals through a signal mapper + QSignalMapper* mapper = new QSignalMapper(this); + for(int i = 0; i < 10; ++i) { + mapper->setMapping(buttons[i], i); + connect(buttons[i], SIGNAL(clicked()), mapper, SLOT(map())); + } + connect(mapper, SIGNAL(mapped(int)), this, SLOT(digitClicked(int))); + + // connect the calculation operations through another mapper + mapper = new QSignalMapper(this); + for(int i = PLUS; i <= EQUAL; ++i) { + mapper->setMapping(buttons[i], i); + connect(buttons[i], SIGNAL(clicked()), mapper, SLOT(map())); + } + connect(mapper, SIGNAL(mapped(int)), this, SLOT(calculationClicked(int))); + + // connect all remaining signals + connect(buttons[COMMA], SIGNAL(clicked()), SLOT(commaClicked())); + connect(buttons[PLUSMINUS], SIGNAL(clicked()), SLOT(plusminusClicked())); + connect(buttons[PERCENT], SIGNAL(clicked()), SLOT(percentClicked())); + connect(buttons[CLEAR], SIGNAL(clicked()), SLOT(clearClicked())); + connect(buttons[CLEARALL], SIGNAL(clicked()), SLOT(clearAllClicked())); + + for(int i = 0; i < MAX_BUTTONS; ++i) { + buttons[i]->setMinimumSize(40, 30); + buttons[i]->setMaximumSize(40, 30); + } + int height = 4 * (buttons[0]->minimumHeight()+6) + 15; + int width = 5 * (buttons[0]->minimumWidth()+6); + + setMinimumSize(width, height); + setMaximumSize(width, height); + + show(); +} + +kMyMoneyCalculator::~kMyMoneyCalculator() +{ +} + +void kMyMoneyCalculator::digitClicked(int button) +{ + if(m_clearOperandOnDigit) { + operand = QString(); + m_clearOperandOnDigit = false; + } + + operand += QChar(button + 0x30); + if(operand.length() > 16) + operand = operand.left(16); + changeDisplay(operand); +} + +void kMyMoneyCalculator::commaClicked(void) +{ + if(operand.length() == 0) + operand = "0"; + if(operand.contains('.', FALSE) == 0) + operand.append('.'); + + if(operand.length() > 16) + operand = operand.left(16); + changeDisplay(operand); +} + +void kMyMoneyCalculator::plusminusClicked(void) +{ + if(operand.length() == 0 && m_result.length() > 0) + operand = m_result; + + if(operand.length() > 0) { + if(operand.find('-') != -1) + operand.replace('-', QString()); + else + operand.prepend('-'); + changeDisplay(operand); + } +} + +void kMyMoneyCalculator::calculationClicked(int button) +{ + if(operand.length() == 0 && op != 0 && button == EQUAL) { + op = 0; + m_result = normalizeString(op1); + changeDisplay(m_result); + + } else if(operand.length() > 0 && op != 0) { + // perform operation + double op2 = operand.toDouble(); + bool error = false; + + // if the pending operation is addition and we now do multiplication + // we just stack op1 and remember the operation in + if((op == PLUS || op == MINUS) && (button == STAR || button == SLASH)) { + op0 = op1; + stackedOp = op; + op = 0; + } + + switch(op) { + case PLUS: + op2 = op1 + op2; + break; + case MINUS: + op2 = op1 - op2; + break; + case STAR: + op2 = op1 * op2; + break; + case SLASH: + if(op2 == 0.0) + error = true; + else + op2 = op1 / op2; + break; + } + + // if we have a pending addition operation, and the next operation is + // not multiplication, we calculate the stacked operation + if(stackedOp && button != STAR && button != SLASH) { + switch(stackedOp) { + case PLUS: + op2 = op0 + op2; + break; + case MINUS: + op2 = op0 - op2; + break; + } + stackedOp = 0; + } + + if(error) { + op = 0; + changeDisplay("Error"); + operand = QString(); + } else { + op1 = op2; + m_result = normalizeString(op1); + changeDisplay(m_result); + } + } else if(operand.length() > 0 && op == 0) { + op1 = operand.toDouble(); + m_result = normalizeString(op1); + changeDisplay(m_result); + } + + if(button != EQUAL) { + op = button; + } else { + op = 0; + emit signalResultAvailable(); + } + operand = QString(); +} + +QString kMyMoneyCalculator::normalizeString(const double& val) +{ + QString str; + str.setNum(val, 'f'); + int i = str.length(); + while(i > 1 && str[i-1] == '0') { + --i; + } + // cut off trailing 0's + str.remove(i, str.length()); + if(str.length() > 0) { + // possibly remove trailing period + if(str[str.length()-1] == '.') { + str.remove(str.length()-1, 1); + } + } + return str; +} + +void kMyMoneyCalculator::clearClicked(void) +{ + if(operand.length() > 0) { + operand = operand.left(operand.length() - 1); + } + if(operand.length() == 0) + changeDisplay("0"); + else + changeDisplay(operand); +} + +void kMyMoneyCalculator::clearAllClicked(void) +{ + operand = QString(); + op = 0; + changeDisplay("0"); +} + +void kMyMoneyCalculator::percentClicked(void) +{ + if(op != 0) { + double op2 = operand.toDouble(); + switch(op) { + case PLUS: + case MINUS: + op2 = (op1 * op2) / 100; + break; + + case STAR: + case SLASH: + op2 /= 100; + break; + } + operand = normalizeString(op2); + changeDisplay(operand); + } +} + +const QString kMyMoneyCalculator::result(void) const +{ + QString txt = m_result; + txt.replace(QRegExp("\\."), m_comma); + if(txt[0] == '-') { + txt = txt.mid(1); // get rid of the minus sign + QString mask; + switch(KGlobal::locale()->negativeMonetarySignPosition()) { + case KLocale::ParensAround: + mask = "(%1)"; + break; + case KLocale::AfterQuantityMoney: + mask = "%1-"; + break; + case KLocale::AfterMoney: + case KLocale::BeforeMoney: + mask = "%1 -"; + break; + case KLocale::BeforeQuantityMoney: + mask = "-%1"; + break; + } + txt = QString(mask).arg(txt); + } + return txt; +} + +void kMyMoneyCalculator::changeDisplay(const QString& str) +{ + QString txt = str; + txt.replace(QRegExp("\\."), m_comma); + display->setText("<b>" + txt + "</b>"); +} + +void kMyMoneyCalculator::keyPressEvent(QKeyEvent* ev) +{ + int button = -1; + + switch(ev->key()) { + case Qt::Key_0: + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + case Qt::Key_9: + if(m_clearOperandOnDigit) { + operand = QString(); + m_clearOperandOnDigit = false; + } + button = ev->key() - Qt::Key_0; + break; + case Qt::Key_Plus: + button = PLUS; + break; + case Qt::Key_Minus: + button = MINUS; + break; + case Qt::Key_Comma: + case Qt::Key_Period: + if(m_clearOperandOnDigit) { + operand = QString(); + m_clearOperandOnDigit = false; + } + button = COMMA; + break; + case Qt::Key_Slash: + button = SLASH; + break; + case Qt::Key_Backspace: + button = CLEAR; + break; + case Qt::Key_Asterisk: + button = STAR; + break; + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Equal: + button = EQUAL; + break; + case Qt::Key_Escape: + button = CLEARALL; + break; + case Qt::Key_Percent: + button = PERCENT; + break; + default: + ev->ignore(); + break; + } + if(button != -1) + buttons[button]->animateClick(); + + m_clearOperandOnDigit = false; +} + +void kMyMoneyCalculator::setInitialValues(const QString& value, QKeyEvent* ev) +{ + bool negative = false; + // setup operand + operand = value; + operand.replace(QRegExp(QString("\\")+KGlobal::locale()->thousandsSeparator()), QString()); + operand.replace(QRegExp(QString("\\")+m_comma), "."); + if(operand.contains('(')) { + negative = true; + operand.replace("(", QString()); + operand.replace(")", QString()); + } + if(operand.contains('-')) { + negative = true; + operand.replace("-", QString()); + } + if(operand.isEmpty()) + operand = "0"; + else if(negative) + operand = QString("-%1").arg(operand); + + changeDisplay(operand); + + // and operation + op = 0; + if(ev) + keyPressEvent(ev); + else + m_clearOperandOnDigit = true; +} + +#include "kmymoneycalculator.moc" diff --git a/kmymoney2/widgets/kmymoneycalculator.h b/kmymoney2/widgets/kmymoneycalculator.h new file mode 100644 index 0000000..594d6c7 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycalculator.h @@ -0,0 +1,249 @@ +/*************************************************************************** + kmymoneycalculator.h - description + ------------------- + begin : Sat Oct 19 2002 + copyright : (C) 2000-2002 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYCALCULATOR_H +#define KMYMONEYCALCULATOR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qframe.h> +#include <qlayout.h> +#include <qgrid.h> +#include <qlcdnumber.h> +#include <qlineedit.h> +#include <qlabel.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kpushbutton.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + *@author Thomas Baumgart + */ + +/** + * This class implements a simple electronic calculator with the + * ability of addition, subtraction, multiplication and division + * and percentage calculation. Memory locations are not available. + * + * The first operand and operation can be loaded from an external + * source to switch from an edit-widget to the calculator widget + * without having the user to re-type the data. See setInitialValues() + * for details. + */ +class kMyMoneyCalculator : public QFrame { + Q_OBJECT +public: + kMyMoneyCalculator(QWidget* parent = 0, const char *name = 0); + ~kMyMoneyCalculator(); + + /** + * This methods is used to extract the result of the last + * calculation. The fractional part is separated from the + * integral part by the character setup using setComma(). + * + * @return QString representing the result of the + * last operation + */ + const QString result(void) const; + + /** + * This method is used to set the character to be used + * as the separator between the integer and fractional part + * of an operand. Upon creation of the object, m_comma is + * set to the current locale setting of KDE's decimalSymbol. + * + * @param ch QChar representing the character to be used + */ + void setComma(const QChar ch) { m_comma = ch; }; + + /** + * This method is used to preset the first operand and start + * execution of an operation. This method is currently used + * by kMyMoneyEdit. If @p ev is 0, then no operation will be + * started. + * + * @param value reference to QString representing the operands value + * @param ev pointer to QKeyEvent representing the operation's key + */ + void setInitialValues(const QString& value, QKeyEvent* ev); + +signals: + /** + * This signal is emitted, when a new result is available + */ + void signalResultAvailable(); + +protected: + void keyPressEvent(QKeyEvent* ev); + + /** + * This method is used to transform a double into a QString + * and removing any trailing 0's and decimal seperators + * + * @param val reference to double value to be converted + * @return QString object containing the converted value + */ + QString normalizeString(const double& val); + +protected slots: + /** + * This method appends the digit represented by the parameter + * to the current operand + * + * @param button integer value of the digit to be added in the + * range [0..9] + */ + void digitClicked(int button); + + /** + * This methods starts the operation contained in the parameter + * + * @param button The Qt::Keycode for the button pressed or clicked + */ + void calculationClicked(int button); + + /** + * This method appends a period (comma) to initialize the fractional + * part of an operand. The period is only appended once. + */ + void commaClicked(void); + + /** + * This method reverses the sign of the current operand + */ + void plusminusClicked(void); + + /** + * This method clears the current operand + */ + void clearClicked(void); + + /** + * This method clears all registers + */ + void clearAllClicked(void); + + /** + * This method executes the percent operation + */ + void percentClicked(void); + + /** + * This method updates the display of the calculator with + * the text passed as argument + * + * @param str reference to QString containing the new display contents + */ + void changeDisplay(const QString& str); + +private: + /** + * This member variable stores the current (second) operand + */ + QString operand; + + /** + * This member variable stores the last result + */ + QString m_result; + + /** + * This member variable stores the representation of the + * character to be used to separate the integer and fractional + * part of numbers. The internal representation is always a period. + */ + QChar m_comma; + + /** + * The numeric representation of a stacked first operand + */ + double op0; + + /** + * The numeric representation of the first operand + */ + double op1; + + /** + * This member stores the operation to be performed between + * the first and the second operand. + */ + int op; + + /** + * This member stores a pending addition operation + */ + int stackedOp; + + /** + * This member stores a pointer to the display area + */ + QLabel *display; + + /** + * This member array stores the pointers to the various + * buttons of the calculator. It is setup during the + * constructor of this object + */ + KPushButton *buttons[20]; + + /** + * This enumeration type stores the values used for the + * various keys internally + */ + enum { + /* 0-9 are used by digits */ + COMMA = 10, + /* + * make sure, that PLUS through EQUAL remain in + * the order they are. Otherwise, check the calculation + * signal mapper + */ + PLUS, + MINUS, + SLASH, + STAR, + EQUAL, + PLUSMINUS, + PERCENT, + CLEAR, + CLEARALL, + /* insert new buttons before this line */ + MAX_BUTTONS + }; + + /** + * This flag signals, if the operand should be replaced upon + * a digit key pressure. Defaults to false and will be set, if + * setInitialValues() is called without an operation. + */ + bool m_clearOperandOnDigit; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycalendar.cpp b/kmymoney2/widgets/kmymoneycalendar.cpp new file mode 100644 index 0000000..8f5291f --- /dev/null +++ b/kmymoney2/widgets/kmymoneycalendar.cpp @@ -0,0 +1,661 @@ +/*************************************************************************** + kmymoneycalendar.cpp - description + ------------------- + begin : Wed Jul 2 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@users.sourceforge.net> + ***************************************************************************/ + +/*************************************************************************** + * Contains code from KDatePicker in kdelibs-3.1.2. + * Original license message: + * + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + ****************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qpainter.h> +#include <qdrawutil.h> +#include <qframe.h> +#include <qpainter.h> +#include <qdialog.h> +#include <qstyle.h> +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <qfont.h> +#include <qvalidator.h> +#include <qpushbutton.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include "kdecompat.h" +#include <kglobal.h> +#include <kapplication.h> +#include <klocale.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <kdebug.h> +#include <knotifyclient.h> +#include <kdatetbl.h> // for maximum re-use +#include <kpopupmenu.h> + +#if KDE_IS_VERSION(3,2,0) +#include <kcalendarsystem.h> +#endif + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneycalendar.h" +#include "kmymoneydatetbl.h" + +class kMyMoneyCalendar::kMyMoneyCalendarPrivate +{ +public: + kMyMoneyCalendarPrivate() + : closeButton(0L), selectWeek(0L), userButton1(0), userButton2(0) {} + + QToolButton *closeButton; + QToolButton *selectWeek; + QPushButton *userButton1; + QPushButton *userButton2; +}; + +kMyMoneyCalendar::kMyMoneyCalendar(QWidget *parent, const char *name ) : + QFrame(parent,name), + table(0), + d(new kMyMoneyCalendarPrivate) +{ +} + +kMyMoneyCalendar::~kMyMoneyCalendar() +{ + delete d; +} + +void kMyMoneyCalendar::init( const QDate &dt ) +{ + styleControl = new QPushButton(i18n("Select Style"), this); + yearForward = new QToolButton(this); + yearBackward = new QToolButton(this); + monthForward = new QToolButton(this); + monthBackward = new QToolButton(this); + selectMonth = new QToolButton(this); + selectYear = new QToolButton(this); + line = new KLineEdit(this); + val = new KDateValidator(this); + fontsize = 10; + + d->selectWeek = new QToolButton( this ); + +// KIconLoader *kiconloader = KGlobal::iconLoader(); + KPopupMenu* kpopupmenuNew = new KPopupMenu(this); + kpopupmenuNew->insertItem(i18n("Week"), this, SLOT(slotSetStyleWeekly())); + kpopupmenuNew->insertItem(i18n("Month"), this, SLOT(slotSetStyleMonthly())); +/* kpopupmenuNew->insertItem(i18n("3 Months"), this, SLOT(slotSetStyleQuarterly())); */ + styleControl->setPopup(kpopupmenuNew); + + QToolTip::add(styleControl, i18n("Choose Style")); + QToolTip::add(yearForward, i18n("Next year")); + QToolTip::add(yearBackward, i18n("Previous year")); + QToolTip::add(monthForward, i18n("Next month")); + QToolTip::add(monthBackward, i18n("Previous month")); + QToolTip::add(d->selectWeek, i18n("Select a week")); + QToolTip::add(selectMonth, i18n("Select a month")); + QToolTip::add(selectYear, i18n("Select a year")); + + // ----- + setFontSize(10); + line->setValidator(val); + line->installEventFilter( this ); + yearForward->setPixmap(BarIcon(QString::fromLatin1("2rightarrow"))); + yearBackward->setPixmap(BarIcon(QString::fromLatin1("2leftarrow"))); + monthForward->setPixmap(BarIcon(QString::fromLatin1("1rightarrow"))); + monthBackward->setPixmap(BarIcon(QString::fromLatin1("1leftarrow"))); + setDate(dt); // set button texts + connect(table, SIGNAL(dateChanged(QDate)), SLOT(dateChangedSlot(QDate))); + connect(table, SIGNAL(tableClicked()), SLOT(tableClickedSlot())); + connect(monthForward, SIGNAL(clicked()), SLOT(monthForwardClicked())); + connect(monthBackward, SIGNAL(clicked()), SLOT(monthBackwardClicked())); + connect(yearForward, SIGNAL(clicked()), SLOT(yearForwardClicked())); + connect(yearBackward, SIGNAL(clicked()), SLOT(yearBackwardClicked())); + connect(d->selectWeek, SIGNAL(clicked()), SLOT(selectWeekClicked())); + connect(selectMonth, SIGNAL(clicked()), SLOT(selectMonthClicked())); + connect(selectYear, SIGNAL(clicked()), SLOT(selectYearClicked())); + connect(line, SIGNAL(returnPressed()), SLOT(lineEnterPressed())); + if (table) + table->setFocus(); +} + +bool +kMyMoneyCalendar::eventFilter(QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *k = (QKeyEvent *)e; + + if ( (k->key() == Qt::Key_Prior) || + (k->key() == Qt::Key_Next) || + (k->key() == Qt::Key_Up) || + (k->key() == Qt::Key_Down) ) + { + QApplication::sendEvent( table, e ); + table->setFocus(); + return TRUE; // eat event + } + } + return QFrame::eventFilter( o, e ); +} + +void +kMyMoneyCalendar::resizeEvent(QResizeEvent*) +{ + QWidget *buttons[] = { + styleControl, + d->userButton1, + d->userButton2, + yearBackward, + monthBackward, + selectMonth, + selectYear, + monthForward, + yearForward, + d->closeButton + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + QSize sizes[NoOfButtons]; + int buttonHeight=0; + int count; + int w; + int x=0; + // ----- calculate button row height: + for(count=0; count<NoOfButtons; ++count) { + if ( buttons[count] ) { // closeButton may be 0L + sizes[count]=buttons[count]->sizeHint(); + buttonHeight=QMAX(buttonHeight, sizes[count].height()); + } + else + sizes[count] = QSize(0,0); // closeButton + } + + // ----- calculate size of the month button: + for(count=0; count<NoOfButtons; ++count) { + if(buttons[count]==selectMonth) { + QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect); + sizes[count].setWidth(QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin))); + } + } + // ----- place the buttons: + // Put the style button and user buttons to the left and the rest to the right + x = 0; + int noUserButtons=2; + buttons[0]->setGeometry(x, 0, sizes[0].width(), buttonHeight); + x += sizes[0].width(); + for (count=1; count<=noUserButtons; ++count) + { + if (buttons[count]) + { + buttons[count]->setGeometry(x, 0, sizes[count].width(), buttonHeight); + x += sizes[count].width(); + } + } + + x = width(); + for(count=(1+noUserButtons); count<NoOfButtons; ++count) + { + w=sizes[count].width(); + x -= w; + } + + for(count=(1+noUserButtons); count<NoOfButtons; ++count) + { + w=sizes[count].width(); + if ( buttons[count] ) + buttons[count]->setGeometry(x, 0, w, buttonHeight); + x+=w; + } + + // ----- place the line edit for direct input: + sizes[0]=line->sizeHint(); + int week_width=d->selectWeek->fontMetrics().width(i18n("Week XX"))+((d->closeButton != 0L) ? 50 : 20); + line->setGeometry(0, height()-sizes[0].height(), width()-week_width, sizes[0].height()); + d->selectWeek->setGeometry(width()-week_width, height()-sizes[0].height(), week_width, sizes[0].height()); + // ----- adjust the table: + table->setGeometry(0, buttonHeight, width(), + height()-buttonHeight-sizes[0].height()); + + table->setFocus(); +} + +void +kMyMoneyCalendar::dateChangedSlot(QDate date) +{ + kdDebug() << "kMyMoneyCalendar::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl; + line->setText(KGlobal::locale()->formatDate(date, true)); + d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date))); + selectMonth->setText(MONTH_NAME(date.month(), date.year(), false)); + selectYear->setText(date.toString("yyyy")); + emit(dateChanged(date)); +} + +void +kMyMoneyCalendar::tableClickedSlot() +{ + kdDebug() << "kMyMoneyCalendar::tableClickedSlot: table clicked." << endl; + emit(dateSelected(table->getDate())); + emit(tableClicked()); +} + +const QDate& +kMyMoneyCalendar::getDate() const +{ + return table->getDate(); +} + +const QDate & +kMyMoneyCalendar::date() const +{ + return table->getDate(); +} + +bool +kMyMoneyCalendar::setDate(const QDate& date) +{ + if (!table) + return true; // hack + + if(date.isValid()) { + QString temp; + // ----- + table->setDate(date); + d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date))); + selectMonth->setText(MONTH_NAME(date.month(), date.year(), false)); + temp.setNum(date.year()); + selectYear->setText(temp); + line->setText(KGlobal::locale()->formatDate(date, true)); + return true; + } else { + kdDebug() << "kMyMoneyCalendar::setDate: refusing to set invalid date." << endl; + return false; + } +} + +void +kMyMoneyCalendar::monthForwardClicked() +{ + setDate( table->getDate().addMonths(1) ); +} + +void +kMyMoneyCalendar::monthBackwardClicked() +{ + setDate( table->getDate().addMonths(-1) ); +} + +void +kMyMoneyCalendar::yearForwardClicked() +{ + setDate( table->getDate().addYears(1) ); +} + +void +kMyMoneyCalendar::yearBackwardClicked() +{ + setDate( table->getDate().addYears(-1) ); +} + +void +kMyMoneyCalendar::selectWeekClicked() +{ +#if KDE_VERSION >= 310 && KDE_VERSION <= 314 + int week; + KPopupFrame* popup = new KPopupFrame(this); + KDateInternalWeekSelector* picker = new KDateInternalWeekSelector(/*fontsize, */popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + picker->setFocus(); + if(popup->exec(d->selectWeek->mapToGlobal(QPoint(0, d->selectWeek->height())))) + { + QDate date; + int year; + // ----- + week=picker->getWeek(); + date=table->getDate(); + year=date.year(); + // ----- find the first selectable day in this week (hacky solution :) + date.setYMD(year, 1, 1); + while (weekOfYear(date)>50) + date=date.addDays(1); + while (weekOfYear(date)<week && (week!=53 || (week==53 && + (weekOfYear(date)!=52 || weekOfYear(date.addDays(1))!=1)))) + date=date.addDays(1); + if (week==53 && weekOfYear(date)==52) + while (weekOfYear(date.addDays(-1))==52) + date=date.addDays(-1); + // ----- set this date + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +#endif +} + +void +kMyMoneyCalendar::selectMonthClicked() +{ +#if KDE_VERSION >= 310 && KDE_VERSION <= 314 + int month; + KPopupFrame* popup = new KPopupFrame(this); + KDateInternalMonthPicker* picker = new KDateInternalMonthPicker(/*fontsize, */popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + picker->setFocus(); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + if(popup->exec(selectMonth->mapToGlobal(QPoint(0, selectMonth->height())))) + { + QDate date; + int day; + // ----- + month=picker->getResult(); + date=table->getDate(); + day=date.day(); + // ----- construct a valid date in this month: + date.setYMD(date.year(), month, 1); + date.setYMD(date.year(), month, QMIN(day, date.daysInMonth())); + // ----- set this month + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +#endif +} + +void +kMyMoneyCalendar::selectYearClicked() +{ +#if KDE_VERSION >= 310 && KDE_VERSION <= 314 + int year; + KPopupFrame* popup = new KPopupFrame(this); + KDateInternalYearSelector* picker = new KDateInternalYearSelector(fontsize, popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + picker->setFocus(); + if(popup->exec(selectYear->mapToGlobal(QPoint(0, selectMonth->height())))) + { + QDate date; + int day; + // ----- + year=picker->getYear(); + date=table->getDate(); + day=date.day(); + // ----- construct a valid date in this month: + date.setYMD(year, date.month(), 1); + date.setYMD(year, date.month(), QMIN(day, date.daysInMonth())); + // ----- set this month + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +#endif +} + +void +kMyMoneyCalendar::setEnabled(bool enable) +{ + QWidget *widgets[]= { + styleControl, yearForward, yearBackward, monthForward, monthBackward, + selectMonth, selectYear, + line, table, d->selectWeek, d->userButton1, d->userButton2 }; + const int Size=sizeof(widgets)/sizeof(widgets[0]); + int count; + // ----- + for(count=0; count<Size; ++count) + { + if (widgets[count]) + widgets[count]->setEnabled(enable); + } +} + +void +kMyMoneyCalendar::lineEnterPressed() +{ + QDate temp; + // ----- + if(val->date(line->text(), temp)==QValidator::Acceptable) + { + kdDebug() << "kMyMoneyCalendar::lineEnterPressed: valid date entered." << endl; + emit(dateEntered(temp)); + setDate(temp); + } else { + KNotifyClient::beep(); + kdDebug() << "kMyMoneyCalendar::lineEnterPressed: invalid date entered." << endl; + } +} + +QSize +kMyMoneyCalendar::sizeHint() const +{ + QSize tableSize=table->sizeHint(); + QWidget *buttons[]={ + styleControl, + yearBackward, + monthBackward, + selectMonth, + selectYear, + monthForward, + yearForward, + d->closeButton, + d->userButton1, + d->userButton2 + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + QSize sizes[NoOfButtons]; + int cx=0, cy=0, count; + // ----- store the size hints: + for(count=0; count<NoOfButtons; ++count) + { + if ( buttons[count] ) + sizes[count]=buttons[count]->sizeHint(); + else + sizes[count] = QSize(0,0); + + if(buttons[count]==selectMonth) + { + QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect); + cx+=QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin)); + } else { + cx+=sizes[count].width(); + } + cy=QMAX(sizes[count].height(), cy); + } + // ----- calculate width hint: + cx=QMAX(cx, tableSize.width()); // line edit ignored + // ----- calculate height hint: + cy+=tableSize.height()+line->sizeHint().height(); + return QSize(cx, cy); +} + +void +kMyMoneyCalendar::setFontSize(int s) +{ + if (table) + { + QWidget *buttons[]= { + // styleControl + // yearBackward, + // monthBackward, + selectMonth, + selectYear, + // monthForward, + // yearForward + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + int count; + QFont font; + QRect r; + // ----- + fontsize=s; + for(count=0; count<NoOfButtons; ++count) + { + font=buttons[count]->font(); + font.setPointSize(s); + buttons[count]->setFont(font); + } + QFontMetrics metrics(selectMonth->fontMetrics()); + for(int i=1; i <= 12; ++i) + { // maxMonthRect is used by sizeHint() + r=metrics.boundingRect(MONTH_NAME(i, 2000, false)); + maxMonthRect.setWidth(QMAX(r.width(), maxMonthRect.width())); + maxMonthRect.setHeight(QMAX(r.height(), maxMonthRect.height())); + } + table->setFontSize(s); + } +} + +void +kMyMoneyCalendar::setCloseButton( bool enable ) +{ + if ( enable == (d->closeButton != 0L) ) + return; + + if ( enable ) { + d->closeButton = new QToolButton( this ); + QToolTip::add(d->closeButton, i18n("Close")); + d->closeButton->setPixmap( SmallIcon("remove") ); + connect( d->closeButton, SIGNAL( clicked() ), + topLevelWidget(), SLOT( close() ) ); + } + else { + delete d->closeButton; + d->closeButton = 0L; + } + + updateGeometry(); +} + +bool kMyMoneyCalendar::hasCloseButton() const +{ + return (d->closeButton != 0L); +} + +int kMyMoneyCalendar::weekOfYear(QDate date) +{ + // Calculate ISO 8601 week number (taken from glibc/Gnumeric) + int year, week, wday, jan1wday, nextjan1wday; + QDate jan1date, nextjan1date; + + year=date.year(); + wday=date.dayOfWeek(); + + jan1date=QDate(year,1,1); + jan1wday=jan1date.dayOfWeek(); + + week = (date.dayOfYear()-1 + jan1wday-1)/7 + ((jan1wday-1) == 0 ? 1 : 0); + + /* Does date belong to last week of previous year? */ + if ((week == 0) && (jan1wday > 4 /*THURSDAY*/)) { + QDate tmpdate=QDate(year-1,12,31); + return weekOfYear(tmpdate); + } + + if ((jan1wday <= 4 /*THURSDAY*/) && (jan1wday > 1 /*MONDAY*/)) + week++; + + if (week == 53) { + nextjan1date=QDate(year+1, 1, 1); + nextjan1wday = nextjan1date.dayOfWeek(); + if (nextjan1wday <= 4 /*THURSDAY*/) + week = 1; + } + + return week; +} + +void kMyMoneyCalendar::virtual_hook( int /*id*/, void* /*data*/ ) +{ /*BASE::virtual_hook( id, data );*/ } + +void kMyMoneyCalendar::slotSetStyleWeekly() +{ + setType(kMyMoneyDateTbl::WEEKLY); +} + +void kMyMoneyCalendar::slotSetStyleMonthly() +{ + setType(kMyMoneyDateTbl::MONTHLY); +} + +void kMyMoneyCalendar::slotSetStyleQuarterly() +{ + setType(kMyMoneyDateTbl::QUARTERLY); +} + +void kMyMoneyCalendar::setUserButton1(bool enable, QPushButton* pb) +{ + if ( enable == (d->userButton1 != 0L) ) + return; + + if ( enable ) { + d->userButton1 = pb; + } + else { + delete d->userButton1; + d->userButton1 = 0L; + } + + updateGeometry(); +} + +void kMyMoneyCalendar::setUserButton2(bool enable, QPushButton* pb) +{ + if ( enable == (d->userButton2 != 0L) ) + return; + + if ( enable ) { + d->userButton2 = pb; + } + else { + delete d->userButton2; + d->userButton2 = 0L; + } + + updateGeometry(); +} + +#include "kmymoneycalendar.moc" diff --git a/kmymoney2/widgets/kmymoneycalendar.h b/kmymoney2/widgets/kmymoneycalendar.h new file mode 100644 index 0000000..b0e2328 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycalendar.h @@ -0,0 +1,262 @@ +/*************************************************************************** + kmymoneycalendar.h - description + ------------------- + begin : Wed Jul 2 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * Contains code from KDatePicker in kdelibs-3.1.2. + * Original license message: + * + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + ****************************************************************************/ + + +#ifndef KMYMONEYCALENDAR_H +#define KMYMONEYCALENDAR_H + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qframe.h> +#include <qdatetime.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include "kdecompat.h" + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneydatetbl.h" + +class QLineEdit; +class QToolButton; +class KDateValidator; +class kMyMoneyDateTbl; +class QPushButton; + +/** + * A representation of a calendar. + * + * @author Michael Edwardes 2003 + * +**/ +class kMyMoneyCalendar : public QFrame { + Q_OBJECT +public: + +public: + /** + * Standard constructor. + **/ + kMyMoneyCalendar(QWidget *parent=0, const char *name=0); + + /** + * Standard destructor. + **/ + ~kMyMoneyCalendar(); + + /** + * Sets the calendar type. + **/ + void setType(kMyMoneyDateTbl::calendarType type) { table->setType(type); } + + /** + * Get the calendar type. + **/ + kMyMoneyDateTbl::calendarType type(void) const { return table->type(); } + + /** The size hint for date pickers. The size hint recommends the + * minimum size of the widget so that all elements may be placed + * without clipping. This sometimes looks ugly, so when using the + * size hint, try adding 28 to each of the reported numbers of + * pixels. + **/ + QSize sizeHint() const; + + /** + * Sets the date. + * + * @returns @p false and does not change anything + * if the date given is invalid. + **/ + bool setDate(const QDate&); + + /** + * Returns the selected date. + * @deprecated + **/ + const QDate& getDate() const; + + /** + * @returns the selected date. + */ + const QDate &date() const; + + /** + * Enables or disables the widget. + **/ + void setEnabled(bool); + + /** + * Sets the font size of the widgets elements. + **/ + void setFontSize(int); + /** + * Returns the font size of the widget elements. + */ + int fontSize() const + { return fontsize; } + + /** + * By calling this method with @p enable = true, KDatePicker will show + * a little close-button in the upper button-row. Clicking the + * close-button will cause the KDatePicker's topLevelWidget()'s close() + * method being called. This is mostly useful for toplevel datepickers + * without a window manager decoration. + * @see #hasCloseButton + * @since 3.1 + */ + void setCloseButton( bool enable ); + + /** + * @returns true if a KDatePicker shows a close-button. + * @see #setCloseButton + * @since 3.1 + */ + bool hasCloseButton() const; + + /** + * Dynamically set the Date Table + **/ + virtual void setDateTable(kMyMoneyDateTbl *tbl) = 0; + + void setUserButton1(bool enable, QPushButton* pb); + void setUserButton2(bool enable, QPushButton* pb); + +protected: + /// to catch move keyEvents when QLineEdit has keyFocus + virtual bool eventFilter(QObject *o, QEvent *e ); + /// the resize event + virtual void resizeEvent(QResizeEvent*); + /// the style control button + QPushButton *styleControl; + /// the year forward button + QToolButton *yearForward; + /// the year backward button + QToolButton *yearBackward; + /// the month forward button + QToolButton *monthForward; + /// the month backward button + QToolButton *monthBackward; + /// the button for selecting the month directly + QToolButton *selectMonth; + /// the button for selecting the year directly + QToolButton *selectYear; + /// the line edit to enter the date directly + QLineEdit *line; + /// the validator for the line edit: + KDateValidator *val; + /// the date table + kMyMoneyDateTbl *table; + /// the size calculated during resize events + // QSize sizehint; + /// the widest month string in pixels: + QSize maxMonthRect; + +protected slots: + void dateChangedSlot(QDate); + void tableClickedSlot(); + void monthForwardClicked(); + void monthBackwardClicked(); + void yearForwardClicked(); + void yearBackwardClicked(); + /// @since 3.1 + void selectWeekClicked(); + void selectMonthClicked(); + void selectYearClicked(); + void lineEnterPressed(); + + void slotSetStyleWeekly(); + void slotSetStyleMonthly(); + void slotSetStyleQuarterly(); + +signals: + /** This signal is emitted each time the selected date is changed. + * Usually, this does not mean that the date has been entered, + * since the date also changes, for example, when another month is + * selected. + * @see dateSelected + */ + void dateChanged(QDate); + /** This signal is emitted each time a day has been selected by + * clicking on the table (hitting a day in the current month). It + * has the same meaning as dateSelected() in older versions of + * KDatePicker. + */ + void dateSelected(QDate); + /** This signal is emitted when enter is pressed and a VALID date + * has been entered before into the line edit. Connect to both + * dateEntered() and dateSelected() to receive all events where the + * user really enters a date. + */ + void dateEntered(QDate); + /** This signal is emitted when the day has been selected by + * clicking on it in the table. + */ + void tableClicked(); + +private: + /// the font size for the widget + int fontsize; + +protected: + virtual void virtual_hook( int id, void* data ); + void init( const QDate &dt ); + +private: + class kMyMoneyCalendarPrivate; + kMyMoneyCalendarPrivate* const d; + // calculate ISO 8601 week number + int weekOfYear(QDate); + +#if KDE_IS_VERSION(3,2,0) + #define MONTH_NAME(a,b,c) KGlobal::locale()->calendar()->monthName(a,b,c) +#else + #define MONTH_NAME(a,b,c) KGlobal::locale()->monthName(a,c) +#endif +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycategory.cpp b/kmymoney2/widgets/kmymoneycategory.cpp new file mode 100644 index 0000000..e14a5a5 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycategory.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + kmymoneycategory.cpp - description + ------------------- + begin : Mon Jul 10 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qrect.h> +#include <qpainter.h> +#include <qpalette.h> +#include <qlayout.h> +#include <qtimer.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kpushbutton.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kguiitem.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneycategory.h" +#include <kmymoney/mymoneyfile.h> +#include "kmymoneyaccountcompletion.h" + +class KMyMoneyCategory::Private +{ +public: + Private() : + splitButton(0), + frame(0), + recursive(false) {} + + KPushButton* splitButton; + QFrame* frame; + bool recursive; +}; + +KMyMoneyCategory::KMyMoneyCategory(QWidget* parent, const char * name, bool splitButton) : + KMyMoneyCombo(true, parent, name), + d(new Private) +{ + if(splitButton) { + d->frame = new QFrame(0); + d->frame->setFocusProxy(this); + QHBoxLayout* layout = new QHBoxLayout(d->frame); + // make sure not to use our own overridden version of reparent() here + KMyMoneyCombo::reparent(d->frame, getWFlags() & ~WType_Mask, QPoint(0, 0), true); + if(parent) + d->frame->reparent(parent, QPoint(0, 0), true); + + // create button + KGuiItem splitButtonItem("", + QIconSet(KGlobal::iconLoader()->loadIcon("split_transaction", KIcon::Small, + KIcon::SizeSmall)), "", ""); + d->splitButton = new KPushButton(splitButtonItem, d->frame, "splitButton"); + + layout->addWidget(this, 5); + layout->addWidget(d->splitButton); + } + + m_completion = new kMyMoneyAccountCompletion(this, 0); + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(textChanged(const QString&)), m_completion, SLOT(slotMakeCompletion(const QString&))); +} + +KMyMoneyCategory::~KMyMoneyCategory() +{ + // make sure to wipe out the frame, button and layout + if(d->frame && !d->frame->parentWidget()) + d->frame->deleteLater(); + + delete d; +} + +KPushButton* KMyMoneyCategory::splitButton(void) const +{ + return d->splitButton; +} + +void KMyMoneyCategory::setPalette(const QPalette& palette) +{ + if(d->frame) + d->frame->setPalette(palette); + KMyMoneyCombo::setPalette(palette); +} + +void KMyMoneyCategory::reparent(QWidget *parent, WFlags w, const QPoint& pos, bool showIt) +{ + if(d->frame) + d->frame->reparent(parent, w, pos, showIt); + else + KMyMoneyCombo::reparent(parent, w, pos, showIt); +} + +kMyMoneyAccountSelector* KMyMoneyCategory::selector(void) const +{ + return dynamic_cast<kMyMoneyAccountSelector*>(KMyMoneyCombo::selector()); +} + +void KMyMoneyCategory::setCurrentTextById(const QString& id) +{ + if(!id.isEmpty()) + setCurrentText(MyMoneyFile::instance()->accountToCategory(id)); + else + setCurrentText(); + setSuppressObjectCreation(false); +} + +void KMyMoneyCategory::slotItemSelected(const QString& id) +{ + setCurrentTextById(id); + + m_completion->hide(); + + if(m_id != id) { + m_id = id; + emit itemSelected(id); + } +} + +void KMyMoneyCategory::focusOutEvent(QFocusEvent *ev) +{ + if(isSplitTransaction()) { + KComboBox::focusOutEvent(ev); + } else { + KMyMoneyCombo::focusOutEvent(ev); + } +} + +void KMyMoneyCategory::focusInEvent(QFocusEvent *ev) +{ + KMyMoneyCombo::focusInEvent(ev); + + // make sure, we get a clean state before we automagically move the focus to + // some other widget (like for 'split transaction'). We do this by delaying + // the emission of the focusIn signal until the next run of the event loop. + QTimer::singleShot(0, this, SIGNAL(focusIn())); +} + +void KMyMoneyCategory::setSplitTransaction(void) +{ + setCurrentText(i18n("Split transaction (category replacement)", "Split transaction")); + setSuppressObjectCreation(true); +} + +bool KMyMoneyCategory::isSplitTransaction(void) const +{ + return currentText() == i18n("Split transaction (category replacement)", "Split transaction"); +} + +void KMyMoneyCategory::setEnabled(bool enable) +{ + if(d->recursive || !d->frame) { + KMyMoneyCombo::setEnabled(enable); + + } else if(d->frame) { + d->recursive = true; + d->frame->setEnabled(enable); + d->recursive = false; + } +} + +void KMyMoneyCategory::setDisabled(bool disable) +{ + setEnabled(!disable); +} + +KMyMoneySecurity::KMyMoneySecurity(QWidget* parent, const char * name) : + KMyMoneyCategory(parent, name, false) +{ +} + +KMyMoneySecurity::~KMyMoneySecurity() +{ +} + +void KMyMoneySecurity::setCurrentTextById(const QString& id) +{ + if(!id.isEmpty()) + KMyMoneyCategory::setCurrentText(MyMoneyFile::instance()->account(id).name()); + else + KMyMoneyCategory::setCurrentText(); +} + +#include "kmymoneycategory.moc" diff --git a/kmymoney2/widgets/kmymoneycategory.h b/kmymoney2/widgets/kmymoneycategory.h new file mode 100644 index 0000000..474062d --- /dev/null +++ b/kmymoney2/widgets/kmymoneycategory.h @@ -0,0 +1,196 @@ +/*************************************************************************** + kmymoneycategory.h + ------------------- + begin : Mon Jul 10 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYCATEGORY_H +#define KMYMONEYCATEGORY_H + +// ---------------------------------------------------------------------------- +// QT Includes + +class QWidget; +class QFrame; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include "kdecompat.h" +#include <kcombobox.h> +class KPushButton; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/kmymoneycombo.h> + +class kMyMoneyAccountSelector; + +/** + * This class implements a text based account/category selector. + * When initially used, the widget has the functionality of a KComboBox object. + * Whenever a key is pressed, the set of loaded accounts is searched for + * accounts which match the currently entered text. + * + * If any match is found a list selection box is opened and the user can use + * the up/down, page-up/page-down keys or the mouse to navigate in the list. If + * an account is selected, the selection box is closed. Other key-strokes are + * directed to the parent object to manipulate the text. The visible contents of + * the selection box is updated with every key-stroke. + * + * This object is a replacement of the kMyMoneyCategory object and should be used + * for new code. + * + * @author Thomas Baumgart + */ +class KMyMoneyCategory : public KMyMoneyCombo +{ + Q_OBJECT +public: + /** + * Standard constructor for the account selection object. + * + * If parameter @a splitButton is @a true, the widget + * will construct a surrounding QFrame and reparent itself to be a child of this + * QFrame. It also adds a KPushButton with the "Split" icon to the right of the + * input field. In this case it is important not to use the pointer to this widget + * but it's parent when placing the object in a QLayout, QTable or some such. The + * parent widget (the QFrame in this case) can be extracted with the parentWidget() + * method. + * + * Reparenting is handled by the object transparently for both cases. + * + * Standard usage example (no split button): + * + * @code + * KMyMoneyCategory* category = new KMyMoneyCategory; + * category->reparent(newParent); + * layout->addWidget(category); + * table->setCellWidget(category); + * @endcode + * + * Enhanced usage example (with split button): + * + * @code + * KMyMoneyCategory* category = new KMyMoneyCategory(0, 0, true); + * category->reparent(newParent); + * layout->addWidget(category->parentWidget()); + * table->setCellWidget(category->parentWidget()); + * @endcode + */ + KMyMoneyCategory(QWidget* parent = 0, const char* name = 0, bool splitButton = false); + + virtual ~KMyMoneyCategory(); + + /** + * This member returns a pointer to the completion object. + * + * @return pointer to completion's selector object + */ + kMyMoneyAccountSelector* selector(void) const; + + /** + * This member returns a pointer to the split button. In case the @a splitButton parameter + * of the constructor was @a false, this method prints a warning to stderr and returns 0. + */ + KPushButton* splitButton(void) const; + + /** + * Reimplemented for internal reasons. No API change + */ + virtual void reparent( QWidget *parent, WFlags, const QPoint &, bool showIt = FALSE ); + + /** + * Reimplemented for internal reasons. No API change. + */ + virtual void setPalette(const QPalette& palette); + + /** + * Force the text field to show the text for split transaction. + */ + void setSplitTransaction(void); + + /** + * Check if the text field contains the text for a split transaction + */ + bool isSplitTransaction(void) const; + + /** + * overridden for internal reasons, no API change + */ + void setCurrentText(const QString& txt = QString()) { KMyMoneyCombo::setCurrentText(txt); } + +protected: + /** + * Reimplemented to support protected category text ("split transactions") + * + * @sa focusIn() + */ + virtual void focusInEvent(QFocusEvent* ev); + + /** + * Reimplemented to support protected category text ("split transactions") + */ + virtual void focusOutEvent(QFocusEvent* ev); + + /** + * set the widgets text area based on the item with the given @a id. + */ + virtual void setCurrentTextById(const QString& id); + +public slots: + virtual void slotItemSelected(const QString& id); + virtual void setEnabled(bool); + virtual void setDisabled(bool); + +signals: + /** + * Signal to inform other objects that this object has reached focus. + * Used for e.g. to open the split dialog when the focus reaches this + * object and it contains the text 'Split transaction'. + * + * @sa focusInEvent() + */ + void focusIn(void); + +private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + + +class KMyMoneySecurity : public KMyMoneyCategory +{ + Q_OBJECT +public: + KMyMoneySecurity(QWidget* parent = 0, const char* name = 0); + virtual ~KMyMoneySecurity(); + + /** + * overridden for internal reasons, no API change + */ + void setCurrentText(const QString& txt = QString()) { KMyMoneyCategory::setCurrentText(txt); } + +protected: + /** + * set the widgets text area based on the item with the given @a id. + */ + virtual void setCurrentTextById(const QString& id); +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneychecklistitem.cpp b/kmymoney2/widgets/kmymoneychecklistitem.cpp new file mode 100644 index 0000000..8992beb --- /dev/null +++ b/kmymoney2/widgets/kmymoneychecklistitem.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + kmymoneychecklistitem + ------------------- + begin : Wed Jun 28 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qfont.h> +#include <qpainter.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneychecklistitem.h" +#include "kmymoneylistviewitem.h" +#include "../kmymoneyglobalsettings.h" + +KMyMoneyCheckListItem::KMyMoneyCheckListItem(QListView* parent, const QString& txt, const QString& key, const QString& id, Type type) : + QCheckListItem(parent, txt, type), + m_key(key), + m_id(id), + m_isOdd(0), + m_isKnown(0) +{ + setOn(true); + if(key.isEmpty()) + m_key = txt; +} + +KMyMoneyCheckListItem::KMyMoneyCheckListItem(QListViewItem* parent, const QString& txt, const QString& key, const QString& id, Type type) : + QCheckListItem(parent, txt, type), + m_key(key), + m_id(id), + m_isOdd(0), + m_isKnown(0) +{ + setOn(true); + if(key.isEmpty()) + m_key = txt; +} + +KMyMoneyCheckListItem::KMyMoneyCheckListItem(QListView* parent, QListViewItem* after, const QString& txt, const QString& key, const QString& id, Type type) : + QCheckListItem(parent, after, txt, type), + m_key(key), + m_id(id), + m_isOdd(0), + m_isKnown(0) +{ + setOn(true); + if(key.isEmpty()) + m_key = txt; +} + +KMyMoneyCheckListItem::~KMyMoneyCheckListItem() +{ +} + +QString KMyMoneyCheckListItem::key(int column, bool ascending) const +{ + Q_UNUSED(ascending); + + if(column == 0) + return m_key[0] + text(0); + return m_key.mid(1); +} + +void KMyMoneyCheckListItem::stateChange(bool state) +{ + emit stateChanged(state); +} + +void KMyMoneyCheckListItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) +{ + QColorGroup _cg = cg; + _cg.setColor(QColorGroup::Base, backgroundColor()); + + // write the groups in bold + QFont f = p->font(); + f.setBold(!isSelectable()); + p->setFont(f); + + QCheckListItem::paintCell(p, _cg, column, width, alignment); +} + +const QColor KMyMoneyCheckListItem::backgroundColor() +{ + return isAlternate() ? KMyMoneyGlobalSettings::listBGColor() : KMyMoneyGlobalSettings::listColor(); +} + +bool KMyMoneyCheckListItem::isAlternate(void) +{ +// logic taken from KListViewItem::isAlternate() + KMyMoneyCheckListItem* ciAbove; + KMyMoneyListViewItem* liAbove; + ciAbove = dynamic_cast<KMyMoneyCheckListItem*> (itemAbove()); + liAbove = dynamic_cast<KMyMoneyListViewItem*> (itemAbove()); + + m_isKnown = ciAbove ? ciAbove->m_isKnown : (liAbove ? liAbove->m_isKnown : true); + if(m_isKnown) { + m_isOdd = ciAbove ? !ciAbove->m_isOdd : (liAbove ? !liAbove->m_isOdd : false); + } else { + KMyMoneyCheckListItem* clItem; + KMyMoneyListViewItem* liItem; + bool previous = true; + if(QListViewItem::parent()) { + clItem = dynamic_cast<KMyMoneyCheckListItem *>(QListViewItem::parent()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(QListViewItem::parent()); + if(clItem) + previous = clItem->m_isOdd; + else + previous = liItem->m_isOdd; + clItem = dynamic_cast<KMyMoneyCheckListItem *>(QListViewItem::parent()->firstChild()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(QListViewItem::parent()->firstChild()); + } else { + clItem = dynamic_cast<KMyMoneyCheckListItem *>(listView()->firstChild()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(listView()->firstChild()); + } + while(clItem || liItem) { + if(clItem) { + clItem->m_isOdd = previous = !previous; + clItem->m_isKnown = true; + liItem = dynamic_cast<KMyMoneyListViewItem *>(clItem->nextSibling()); + clItem = dynamic_cast<KMyMoneyCheckListItem *>(clItem->nextSibling()); + } else if(liItem) { + liItem->m_isOdd = previous = !previous; + liItem->m_isKnown = true; + clItem = dynamic_cast<KMyMoneyCheckListItem *>(liItem->nextSibling()); + liItem = dynamic_cast<KMyMoneyListViewItem *>(liItem->nextSibling()); + } + } + } + return m_isOdd; +} + +#include "kmymoneychecklistitem.moc" diff --git a/kmymoney2/widgets/kmymoneychecklistitem.h b/kmymoney2/widgets/kmymoneychecklistitem.h new file mode 100644 index 0000000..6a22ec9 --- /dev/null +++ b/kmymoney2/widgets/kmymoneychecklistitem.h @@ -0,0 +1,93 @@ +/*************************************************************************** + kmymoneychecklistitem - description + ------------------- + begin : Wed Jun 28 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYCHECKLISTITEM_H +#define KMYMONEYCHECKLISTITEM_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> +#include <qlistview.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +class KMyMoneyListViewItem; + +/** + * This class implements a derived version of a QCheckListItem that + * allows the storage of an engine object id with the object and emits + * a signal upon state change. + * + * @author Thomas Baumgart + */ +class KMyMoneyCheckListItem : public QObject, public QCheckListItem +{ + friend class KMyMoneyListViewItem; + + Q_OBJECT +public: + KMyMoneyCheckListItem(QListView *parent, const QString& txt, const QString& key, const QString& id, Type type = QCheckListItem::CheckBox); + KMyMoneyCheckListItem(QListView *parent, QListViewItem* after, const QString& txt, const QString& key, const QString& id, Type type = QCheckListItem::CheckBox); + KMyMoneyCheckListItem(QListViewItem *parent, const QString& txt, const QString& key, const QString& id, Type type = QCheckListItem::CheckBox); + ~KMyMoneyCheckListItem(); + + const QString& id(void) const { return m_id; }; + + /** + * use my own paint method + */ + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); + + /** + * use my own backgroundColor method + */ + const QColor backgroundColor(); + + /** + * see KListViewItem::isAlternate() + */ + bool isAlternate(void); + + /** + * This method returns a const reference to the key passed to the constructor. The column + * defines what is returned: For @a column equals 0, the first character passed as @a key to + * the constructor concatenated with the value returned by text(0) is returned. For @a column + * equals to 1, the @a key as passed to the constructor except the first character is returned. + */ + QString key(int column, bool ascending) const; + +signals: + void stateChanged(bool); + +protected: + virtual void stateChange(bool); + +private: + QString m_key; + QString m_id; + // copied from KListViewItem() + unsigned int m_isOdd : 1; + unsigned int m_isKnown : 1; + unsigned int m_unused : 30; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycombo.cpp b/kmymoney2/widgets/kmymoneycombo.cpp new file mode 100644 index 0000000..8bbec10 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycombo.cpp @@ -0,0 +1,778 @@ +/*************************************************************************** + kmymoneycombo.cpp - description + ------------------- + begin : Mon Mar 12 2007 + copyright : (C) 2007 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qrect.h> +#include <qstyle.h> +#include <qpainter.h> +#include <qapplication.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <klistview.h> +#include <kdebug.h> +#include <kconfig.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneycombo.h" +#include "kmymoneyselector.h" +#include <kmymoney/kmymoneycompletion.h> +#include <kmymoney/kmymoneylineedit.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/registeritem.h> +#include <kmymoney/mymoneyscheduled.h> +#include "../kmymoneyutils.h" + +KMyMoneyCombo::KMyMoneyCombo(QWidget *w, const char *name) : + KComboBox(w, name), + m_completion(0), + m_edit(0), + m_canCreateObjects(false), + m_inFocusOutEvent(false) +{ +} + +KMyMoneyCombo::KMyMoneyCombo(bool rw, QWidget *w, const char *name) : + KComboBox(rw, w, name), + m_completion(0), + m_edit(0), + m_canCreateObjects(false), + m_inFocusOutEvent(false) +{ + if(rw) { + m_edit = new kMyMoneyLineEdit(this, "combo edit"); + setLineEdit(m_edit); + } +} + +void KMyMoneyCombo::setCurrentTextById(const QString& id) +{ + setCurrentText(); + if(!id.isEmpty()) { + QListViewItem* item = selector()->item(id); + if(item) + setCurrentText(item->text(0)); + } +} + +void KMyMoneyCombo::slotItemSelected(const QString& id) +{ + if(editable()) { + bool blocked = signalsBlocked(); + blockSignals(true); + setCurrentTextById(id); + blockSignals(blocked); + } + + m_completion->hide(); + + if(m_id != id) { + m_id = id; + emit itemSelected(id); + } +} + +void KMyMoneyCombo::setEditable(bool y) +{ + if(y == editable()) + return; + + KComboBox::setEditable(y); + + // make sure we use our own line edit style + if(y) { + m_edit = new kMyMoneyLineEdit(this, "combo edit"); + setLineEdit(m_edit); + m_edit->setPaletteBackgroundColor(paletteBackgroundColor()); + + } else { + m_edit = 0; + } +} + +void KMyMoneyCombo::setHint(const QString& hint) const +{ + if(m_edit) + m_edit->setHint(hint); +} + +void KMyMoneyCombo::paintEvent(QPaintEvent* ev) +{ + KComboBox::paintEvent(ev); + + // if we don't have an edit field, we need to paint the text onto the button + if(!m_edit) { + if(m_completion) { + QStringList list; + selector()->selectedItems(list); + if(!list.isEmpty()) { + QString str = selector()->item(list[0])->text(0); + // we only paint, if the text is longer than 1 char. Assumption + // is that length 1 is the blank case so no need to do painting + if(str.length() > 1) { + QPainter p( this ); + const QColorGroup & g = colorGroup(); + p.setPen(g.text()); + + QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxEditField ); + re = QStyle::visualRect(re, this); + p.setClipRect( re ); + p.save(); + p.setFont(font()); + QFontMetrics fm(font()); + int x = re.x(), y = re.y() + fm.ascent(); + p.drawText( x, y, str ); + p.restore(); + } + } + } + } +} + +void KMyMoneyCombo::setPaletteBackgroundColor(const QColor& color) +{ + KComboBox::setPaletteBackgroundColor(color); + if(m_edit) { + m_edit->setPaletteBackgroundColor(color); + } +} + +void KMyMoneyCombo::mousePressEvent(QMouseEvent *e) +{ + // mostly copied from QCombo::mousePressEvent() and adjusted for our needs + if(e->button() != LeftButton) + return; + + if(((!editable() || isInArrowArea(mapToGlobal(e->pos()))) && selector()->itemList().count()) && !m_completion->isVisible()) { + m_completion->show(); + } + + if(m_timer.isActive()) { + m_timer.stop(); + m_completion->slotMakeCompletion(""); + } else { + KConfig config( "kcminputrc", true ); + config.setGroup("KDE"); + m_timer.start(config.readNumEntry("DoubleClickInterval", 400), true); + } +} + +bool KMyMoneyCombo::isInArrowArea(const QPoint& pos) const +{ + QRect arrowRect = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxArrow); + arrowRect = QStyle::visualRect(arrowRect, this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); + + // if the button is not editable, it covers the whole widget + if(!editable()) + arrowRect = rect(); + + return arrowRect.contains(mapFromGlobal(pos)); +} + +void KMyMoneyCombo::keyPressEvent(QKeyEvent* e) +{ + if((e->key() == Key_F4 && e->state() == 0 ) || + (e->key() == Key_Down && (e->state() & AltButton)) || + (!editable() && e->key() == Key_Space)) { + // if we have at least one item in the list, we open the dropdown + if(selector()->listView()->firstChild()) + m_completion->show(); + e->ignore(); + return; + } + KComboBox::keyPressEvent(e); +} + +void KMyMoneyCombo::connectNotify(const char* signal) +{ + if(signal && !strcmp(signal, SIGNAL(createItem(const QString&,QString&)))) { + m_canCreateObjects = true; + } +} + +void KMyMoneyCombo::disconnectNotify(const char* signal) +{ + if(signal && !strcmp(signal, SIGNAL(createItem(const QString&,QString&)))) { + m_canCreateObjects = false; + } +} + +void KMyMoneyCombo::focusOutEvent(QFocusEvent* e) +{ + if(m_inFocusOutEvent) { + KComboBox::focusOutEvent(e); + return; + } + + m_inFocusOutEvent = true; + if(editable() && !currentText().isEmpty()) { + if(m_canCreateObjects) { + if(!m_completion->selector()->contains(currentText())) { + QString id; + // annouce that we go into a possible dialog to create an object + // This can be used by upstream widgets to disable filters etc. + emit objectCreation(true); + + emit createItem(currentText(), id); + + // Announce that we return from object creation + emit objectCreation(false); + + // update the field to a possibly created object + m_id = id; + setCurrentTextById(id); + + // make sure the completion does not show through + m_completion->hide(); + } + + // else if we cannot create objects, and the current text is not + // in the list, then we clear the text and the selection. + } else if(!m_completion->selector()->contains(currentText())) { + setCurrentText(QString()); + } + } + + KComboBox::focusOutEvent(e); + + // force update of hint and id if there is no text in the widget + if(editable() && currentText().isEmpty()) { + QString id = m_id; + m_id = QString(); + if(!id.isEmpty()) + emit itemSelected(m_id); + repaint(); + } + m_inFocusOutEvent = false; +} + +KMyMoneySelector* KMyMoneyCombo::selector(void) const +{ + return m_completion->selector(); +} + +kMyMoneyCompletion* KMyMoneyCombo::completion(void) const +{ + return m_completion; +} + +void KMyMoneyCombo::selectedItem(QString& id) const +{ + id = m_id; +} + +void KMyMoneyCombo::selectedItems(QStringList& list) const +{ + if(lineEdit() && lineEdit()->text().length() == 0) { + list.clear(); + } else { + m_completion->selector()->selectedItems(list); + } +} + +void KMyMoneyCombo::setSelectedItem(const QString& id) +{ + m_completion->selector()->setSelected(id, true); + blockSignals(true); + slotItemSelected(id); + blockSignals(false); + update(); +} + +QSize KMyMoneyCombo::sizeHint() const +{ + return KComboBox::sizeHint(); + + // I wanted to use the code below to adjust the size of the combo box + // according to the largest item in the selector list. Apparently that + // does not work too well in the enter and edit schedule dialog for + // the category combo box. So we just use the standard implementation for now. +#if 0 + constPolish(); + int i, w; + QFontMetrics fm = fontMetrics(); + + int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18; + int maxH = QMAX( fm.lineSpacing(), 14 ) + 2; + + w = selector()->optimizedWidth(); + if ( w > maxW ) + maxW = w; + + QSize sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this, + QSize(maxW, maxH)). + expandedTo(QApplication::globalStrut())); + + return sizeHint; +#endif +} + + + +KMyMoneyReconcileCombo::KMyMoneyReconcileCombo(QWidget* w, const char* name) : + KMyMoneyCombo(false, w, name) +{ + m_completion = new kMyMoneyCompletion(this, 0); + // connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SIGNAL(itemSelected(const QString&))); + + // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) + // selector()->newTopItem(i18n("Frozen"), QString(), "F"); + selector()->newTopItem(i18n("Reconciled"), QString(), "R"); + selector()->newTopItem(i18n("Cleared"), QString(), "C"); + selector()->newTopItem(i18n("Not reconciled"), QString(), " "); + selector()->newTopItem(" ", QString(), "U"); + + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(itemSelected(const QString&)), this, SLOT(slotSetState(const QString&))); +} + +void KMyMoneyReconcileCombo::slotSetState(const QString& state) +{ + setSelectedItem(state); +} + +void KMyMoneyReconcileCombo::removeDontCare(void) +{ + selector()->removeItem("U"); +} + +void KMyMoneyReconcileCombo::setState(MyMoneySplit::reconcileFlagE state) +{ + QString id; + switch(state) { + case MyMoneySplit::NotReconciled: + id = " "; + break; + case MyMoneySplit::Cleared: + id = "C"; + break; + case MyMoneySplit::Reconciled: + id = "R"; + break; + case MyMoneySplit::Frozen: + id = "F"; + break; + case MyMoneySplit::Unknown: + id = "U"; + break; + default: + kdDebug(2) << "Unknown reconcile state '" << state << "' in KMyMoneyComboReconcile::setState()\n"; + break; + } + setSelectedItem(id); +} + +MyMoneySplit::reconcileFlagE KMyMoneyReconcileCombo::state(void) const +{ + MyMoneySplit::reconcileFlagE state = MyMoneySplit::NotReconciled; + + QStringList list; + selector()->selectedItems(list); + if(!list.isEmpty()) { + if(list[0] == "C") + state = MyMoneySplit::Cleared; + if(list[0] == "R") + state = MyMoneySplit::Reconciled; + if(list[0] == "F") + state = MyMoneySplit::Frozen; + if(list[0] == "U") + state = MyMoneySplit::Unknown; + } + return state; +} + + +KMyMoneyComboAction::KMyMoneyComboAction(QWidget* w, const char* name) : + KMyMoneyCombo(false, w, name) +{ + m_completion = new kMyMoneyCompletion(this, 0); + QString num; + // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) + selector()->newTopItem(i18n("ATM"), QString(), num.setNum(KMyMoneyRegister::ActionAtm)); + selector()->newTopItem(i18n("Withdrawal"), QString(), num.setNum(KMyMoneyRegister::ActionWithdrawal)); + selector()->newTopItem(i18n("Transfer"), QString(), num.setNum(KMyMoneyRegister::ActionTransfer)); + selector()->newTopItem(i18n("Deposit"), QString(), num.setNum(KMyMoneyRegister::ActionDeposit)); + selector()->newTopItem(i18n("Cheque"), QString(), num.setNum(KMyMoneyRegister::ActionCheck)); + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(itemSelected(const QString&)), this, SLOT(slotSetAction(const QString&))); +} + +void KMyMoneyComboAction::protectItem(int id, bool protect) +{ + QString num; + selector()->protectItem(num.setNum(id), protect); +} + +void KMyMoneyComboAction::slotSetAction(const QString& act) +{ + setSelectedItem(act); + update(); + emit actionSelected(action()); +} + +void KMyMoneyComboAction::setAction(int action) +{ + if(action < 0 || action > 5) { + kdDebug(2) << "KMyMoneyComboAction::slotSetAction(" << action << ") invalid. Replaced with 2\n"; + action = 2; + } + QString act; + act.setNum(action); + setSelectedItem(act); +} + +int KMyMoneyComboAction::action(void) const +{ + QStringList list; + selector()->selectedItems(list); + if(!list.isEmpty()) { + return list[0].toInt(); + } + kdDebug(2) << "KMyMoneyComboAction::action(void): unknown selection\n"; + return 0; +} + +KMyMoneyCashFlowCombo::KMyMoneyCashFlowCombo(QWidget* w, const char* name, MyMoneyAccount::accountTypeE accountType) : + KMyMoneyCombo(false, w, name) +{ + m_completion = new kMyMoneyCompletion(this, 0); + QString num; + // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) + if(accountType == MyMoneyAccount::Income || accountType == MyMoneyAccount::Expense) { + // this is used for income/expense accounts to just show the reverse sense + selector()->newTopItem(i18n("Activity for expense categories", "Paid"), QString(), num.setNum(KMyMoneyRegister::Deposit)); + selector()->newTopItem(i18n("Activity for income categories", "Received"), QString(), num.setNum(KMyMoneyRegister::Payment)); + } else { + selector()->newTopItem(i18n("From"), QString(), num.setNum(KMyMoneyRegister::Deposit)); + selector()->newTopItem(i18n("Pay to"), QString(), num.setNum(KMyMoneyRegister::Payment)); + } + selector()->newTopItem(" ", QString(), num.setNum(KMyMoneyRegister::Unknown)); + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(itemSelected(const QString&)), this, SLOT(slotSetDirection(const QString&))); +} + +void KMyMoneyCashFlowCombo::setDirection(KMyMoneyRegister::CashFlowDirection dir) +{ + m_dir = dir; + QString num; + setSelectedItem(num.setNum(dir)); +} + +void KMyMoneyCashFlowCombo::slotSetDirection(const QString& id) +{ + QString num; + for(int i = KMyMoneyRegister::Deposit; i <= KMyMoneyRegister::Unknown; ++i) { + num.setNum(i); + if(num == id) { + m_dir = static_cast<KMyMoneyRegister::CashFlowDirection>(i); + break; + } + } + emit directionSelected(m_dir); + update(); +} + +void KMyMoneyCashFlowCombo::removeDontCare(void) +{ + QString num; + selector()->removeItem(num.setNum(KMyMoneyRegister::Unknown)); +} + + +KMyMoneyActivityCombo::KMyMoneyActivityCombo(QWidget* w, const char* name) : + KMyMoneyCombo(false, w, name), + m_activity(MyMoneySplit::UnknownTransactionType) +{ + m_completion = new kMyMoneyCompletion(this, 0); + QString num; + // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) + selector()->newTopItem(i18n("Split shares"), QString(), num.setNum(MyMoneySplit::SplitShares)); + selector()->newTopItem(i18n("Remove shares"), QString(), num.setNum(MyMoneySplit::RemoveShares)); + selector()->newTopItem(i18n("Add shares"), QString(), num.setNum(MyMoneySplit::AddShares)); + selector()->newTopItem(i18n("Yield"), QString(), num.setNum(MyMoneySplit::Yield)); + selector()->newTopItem(i18n("Reinvest dividend"), QString(), num.setNum(MyMoneySplit::ReinvestDividend)); + selector()->newTopItem(i18n("Dividend"), QString(), num.setNum(MyMoneySplit::Dividend)); + selector()->newTopItem(i18n("Sell shares"), QString(), num.setNum(MyMoneySplit::SellShares)); + selector()->newTopItem(i18n("Buy shares"), QString(), num.setNum(MyMoneySplit::BuyShares)); + + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(itemSelected(const QString&)), this, SLOT(slotSetActivity(const QString&))); +} + +void KMyMoneyActivityCombo::setActivity(MyMoneySplit::investTransactionTypeE activity) +{ + m_activity = activity; + QString num; + setSelectedItem(num.setNum(activity)); +} + +void KMyMoneyActivityCombo::slotSetActivity(const QString& id) +{ + QString num; + for(int i = MyMoneySplit::BuyShares; i <= MyMoneySplit::SplitShares; ++i) { + num.setNum(i); + if(num == id) { + m_activity = static_cast<MyMoneySplit::investTransactionTypeE>(i); + break; + } + } + emit activitySelected(m_activity); + update(); +} + +KMyMoneyPayeeCombo::KMyMoneyPayeeCombo(QWidget* parent, const char * name) : + KMyMoneyCombo(true, parent, name) +{ + m_completion = new kMyMoneyCompletion(this); + + // set to ascending sort + selector()->listView()->setSorting(0); + + connect(m_completion, SIGNAL(itemSelected(const QString&)), this, SLOT(slotItemSelected(const QString&))); + connect(this, SIGNAL(textChanged(const QString&)), m_completion, SLOT(slotMakeCompletion(const QString&))); +} + +void KMyMoneyPayeeCombo::loadPayees(const QValueList<MyMoneyPayee>& list) +{ + selector()->listView()->clear(); + QValueList<MyMoneyPayee>::const_iterator it; + for(it = list.begin(); it != list.end(); ++it) { + selector()->newTopItem((*it).name(), QString(), (*it).id()); + } +} + + +class KMyMoneyGeneralCombo::Private { +public: + QMap<QString, int> m_strings; + void insertItem(const QString& s, int id) { m_strings[s] = id; } + + int itemId(const QString& s) const { + QMap<QString, int>::const_iterator it; + it = m_strings.find(s); + if(it != m_strings.end()) + return *it; + return -1; + } + + const QString& itemText(int id) { + QMap<QString, int>::const_iterator it; + for(it = m_strings.begin(); it != m_strings.end(); ++it) { + if(*it == id) { + return it.key(); + } + } + return QString::null; + } +}; + +KMyMoneyGeneralCombo::KMyMoneyGeneralCombo(QWidget* w, const char* name) : + KComboBox(w, name), + d(new Private) +{ + connect(this, SIGNAL(highlighted(int)), this, SLOT(slotChangeItem(int))); +} + +KMyMoneyGeneralCombo::~KMyMoneyGeneralCombo() +{ + delete d; +} + +void KMyMoneyGeneralCombo::setItem(int id) +{ + setCurrentItem(id); +} + +int KMyMoneyGeneralCombo::item(void) const +{ + return currentItem(); +} + +void KMyMoneyGeneralCombo::setCurrentItem(int id) +{ + const QString& txt = d->itemText(id); + for(int idx = 0; idx < count(); ++idx) { + if(txt == text(idx)) { + KComboBox::setCurrentItem(idx); + break; + } + } +} + +int KMyMoneyGeneralCombo::currentItem(void) const +{ + return d->itemId(currentText()); +} + +void KMyMoneyGeneralCombo::clear(void) +{ + d->m_strings.clear(); + KComboBox::clear(); +} + +void KMyMoneyGeneralCombo::insertItem(const QString& txt, int id, int idx) +{ + d->insertItem(txt, id); + KComboBox::insertItem(txt, idx); +} + +void KMyMoneyGeneralCombo::removeItem(int id) +{ + const QString& txt = d->itemText(id); + for(int idx = 0; idx < count(); ++idx) { + if(txt == text(idx)) { + KComboBox::removeItem(idx); + break; + } + } +} + +void KMyMoneyGeneralCombo::slotChangeItem(int idx) +{ + emit itemSelected(d->itemId(text(idx))); +} + +KMyMoneyPeriodCombo::KMyMoneyPeriodCombo(QWidget* parent, const char* name) : + KMyMoneyGeneralCombo(parent, name) +{ + insertItem(i18n("All dates"), MyMoneyTransactionFilter::allDates); + insertItem(i18n("As of today"), MyMoneyTransactionFilter::asOfToday); + insertItem(i18n("Today"), MyMoneyTransactionFilter::today); + insertItem(i18n("Current month"), MyMoneyTransactionFilter::currentMonth); + insertItem(i18n("Current quarter"), MyMoneyTransactionFilter::currentQuarter); + insertItem(i18n("Current year"), MyMoneyTransactionFilter::currentYear); + insertItem(i18n("Current fiscal year"), MyMoneyTransactionFilter::currentFiscalYear); + insertItem(i18n("Month to date"), MyMoneyTransactionFilter::monthToDate); + insertItem(i18n("Year to date"), MyMoneyTransactionFilter::yearToDate); + insertItem(i18n("Year to month"), MyMoneyTransactionFilter::yearToMonth); + insertItem(i18n("Last month"), MyMoneyTransactionFilter::lastMonth); + insertItem(i18n("Last year"), MyMoneyTransactionFilter::lastYear); + insertItem(i18n("Last fiscal year"), MyMoneyTransactionFilter::lastFiscalYear); + insertItem(i18n("Last 7 days"), MyMoneyTransactionFilter::last7Days); + insertItem(i18n("Last 30 days"), MyMoneyTransactionFilter::last30Days); + insertItem(i18n("Last 3 months"), MyMoneyTransactionFilter::last3Months); + insertItem(i18n("Last quarter"), MyMoneyTransactionFilter::lastQuarter); + insertItem(i18n("Last 6 months"), MyMoneyTransactionFilter::last6Months); + insertItem(i18n("Last 11 months"), MyMoneyTransactionFilter::last11Months); + insertItem(i18n("Last 12 months"), MyMoneyTransactionFilter::last12Months); + insertItem(i18n("Next 7 days"), MyMoneyTransactionFilter::next7Days); + insertItem(i18n("Next 30 days"), MyMoneyTransactionFilter::next30Days); + insertItem(i18n("Next 3 months"), MyMoneyTransactionFilter::next3Months); + insertItem(i18n("Next quarter"), MyMoneyTransactionFilter::lastQuarter); + insertItem(i18n("Next 6 months"), MyMoneyTransactionFilter::next6Months); + insertItem(i18n("Next 12 months"), MyMoneyTransactionFilter::next12Months); + insertItem(i18n("Last 3 months to next 3 months"), MyMoneyTransactionFilter::last3ToNext3Months); + insertItem(i18n("User defined"), MyMoneyTransactionFilter::userDefined); +} + +void KMyMoneyPeriodCombo::setCurrentItem(MyMoneyTransactionFilter::dateOptionE id) +{ + if(id >= MyMoneyTransactionFilter::dateOptionCount) + id = MyMoneyTransactionFilter::userDefined; + + KMyMoneyGeneralCombo::setCurrentItem(id); +} + +MyMoneyTransactionFilter::dateOptionE KMyMoneyPeriodCombo::currentItem(void) const +{ + return static_cast<MyMoneyTransactionFilter::dateOptionE>(KMyMoneyGeneralCombo::currentItem()); +} + +QDate KMyMoneyPeriodCombo::start(MyMoneyTransactionFilter::dateOptionE id) +{ + QDate start, end; + MyMoneyTransactionFilter::translateDateRange(id, start, end); + return start; +} + +QDate KMyMoneyPeriodCombo::end(MyMoneyTransactionFilter::dateOptionE id) +{ + QDate start, end; + MyMoneyTransactionFilter::translateDateRange(id, start, end); + return end; +} + +#if 0 +void KMyMoneyPeriodCombo::dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id) +{ +} +#endif + +KMyMoneyOccurenceCombo::KMyMoneyOccurenceCombo(QWidget* parent, const char* name) : + KMyMoneyGeneralCombo(parent, name) +{ +} + +MyMoneySchedule::occurenceE KMyMoneyOccurenceCombo::currentItem(void) const +{ + return static_cast<MyMoneySchedule::occurenceE>(KMyMoneyGeneralCombo::currentItem()); +} + +KMyMoneyOccurencePeriodCombo::KMyMoneyOccurencePeriodCombo(QWidget* parent, const char* name) : + KMyMoneyOccurenceCombo(parent, name) +{ + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_ONCE)), MyMoneySchedule::OCCUR_ONCE); + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_DAILY)), MyMoneySchedule::OCCUR_DAILY); + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_WEEKLY)), MyMoneySchedule::OCCUR_WEEKLY); + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_EVERYHALFMONTH)), MyMoneySchedule::OCCUR_EVERYHALFMONTH); + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_MONTHLY)), MyMoneySchedule::OCCUR_MONTHLY); + insertItem(i18n(MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::OCCUR_YEARLY)), MyMoneySchedule::OCCUR_YEARLY); +} + +KMyMoneyFrequencyCombo::KMyMoneyFrequencyCombo(QWidget* parent, const char* name) : + KMyMoneyOccurenceCombo(parent, name) +{ + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_ONCE)), MyMoneySchedule::OCCUR_ONCE); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_DAILY)), MyMoneySchedule::OCCUR_DAILY); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_WEEKLY)), MyMoneySchedule::OCCUR_WEEKLY); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYOTHERWEEK)), MyMoneySchedule::OCCUR_EVERYOTHERWEEK); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYHALFMONTH)), MyMoneySchedule::OCCUR_EVERYHALFMONTH); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYTHREEWEEKS)), MyMoneySchedule::OCCUR_EVERYTHREEWEEKS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS)), MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYFOURWEEKS)), MyMoneySchedule::OCCUR_EVERYFOURWEEKS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_MONTHLY)), MyMoneySchedule::OCCUR_MONTHLY); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS)), MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYOTHERMONTH)), MyMoneySchedule::OCCUR_EVERYOTHERMONTH); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYTHREEMONTHS)), MyMoneySchedule::OCCUR_EVERYTHREEMONTHS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYFOURMONTHS)), MyMoneySchedule::OCCUR_EVERYFOURMONTHS); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_TWICEYEARLY)), MyMoneySchedule::OCCUR_TWICEYEARLY); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_YEARLY)), MyMoneySchedule::OCCUR_YEARLY); + insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYOTHERYEAR)), MyMoneySchedule::OCCUR_EVERYOTHERYEAR); +} + +int KMyMoneyFrequencyCombo::daysBetweenEvents(void) const +{ + return MyMoneySchedule::daysBetweenEvents(currentItem()); +} + +int KMyMoneyFrequencyCombo::eventsPerYear(void) const +{ + return MyMoneySchedule::eventsPerYear(currentItem()); +} +#include "kmymoneycombo.moc" diff --git a/kmymoney2/widgets/kmymoneycombo.h b/kmymoney2/widgets/kmymoneycombo.h new file mode 100644 index 0000000..c85847d --- /dev/null +++ b/kmymoney2/widgets/kmymoneycombo.h @@ -0,0 +1,467 @@ +/*************************************************************************** + kmymoneycombo.h - description + ------------------- + begin : Mon Mar 12 2007 + copyright : (C) 2007 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 KMYMONEYCOMBO_H +#define KMYMONEYCOMBO_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtimer.h> +#include <qmutex.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyutils.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/register.h> +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/transaction.h> +#include <kmymoney/mymoneypayee.h> +#include <kmymoney/mymoneytransactionfilter.h> +#include <kmymoney/mymoneyscheduled.h> + +class kMyMoneyCompletion; +class KMyMoneySelector; +class kMyMoneyLineEdit; + +/** + * @author Thomas Baumgart + */ +class KMyMoneyCombo : public KComboBox +{ + Q_OBJECT +public: + KMyMoneyCombo(QWidget *w = 0, const char *name=0); + KMyMoneyCombo(bool rw, QWidget *w = 0, const char *name=0); + + /** + * This method is used to turn on/off the hint display and to setup the appropriate text. + * The hint text is shown in a lighter color if the field is otherwise empty and does + * not have the keyboard focus. + * + * @param hint reference to text. If @a hint is empty, no hint will be shown. + */ + void setHint(const QString& hint) const; + + /** + * overridden for internal reasons. + * + * @param editable make combo box editable (@a true) or selectable only (@a false). + */ + void setEditable(bool editable); + + /** + * This method returns a pointer to the completion object of the combo box. + * + * @return pointer to kMyMoneyCompletion or derivative. + */ + kMyMoneyCompletion* completion(void) const; + + /** + * This method returns a pointer to the completion object's selector. + * + * @return pointer to KMyMoneySelector or derivative. + */ + KMyMoneySelector* selector(void) const; + + /** + * This method returns the ids of the currently selected items + */ + void selectedItems(QStringList& list) const; + + /** + * This method returns the id of the first selected item. + * Usage makes usually only sense when the selection mode + * of the associated KMyMoneySelector is QListView::Single. + * + * @sa KMyMoneySelector::setSelectionMode() + * + * @param id reference to QString containing the id. If no item + * is selected id will be empty. + */ + void selectedItem(QString& id) const KDE_DEPRECATED; + + /** + * This method returns the id of the first selected item. + * Usage makes usually only sense when the selection mode + * of the associated KMyMoneySelector is QListView::Single. + * + * @sa KMyMoneySelector::setSelectionMode() + * + * @return reference to QString containing the id. If no item + * is selected the QString will be empty. + */ + const QString& selectedItem(void) const { return m_id; } + + /** + * This method selects the item with the respective @a id. + * + * @param id reference to QString containing the id + */ + void setSelectedItem(const QString& id); + + /** + * This method checks if the position @a pos is part of the + * area of the drop down arrow. + */ + bool isInArrowArea(const QPoint& pos) const; + + void setSuppressObjectCreation(bool suppress) { m_canCreateObjects = !suppress; } + + /** + * overridden for internal reasons, no API change + */ + void setCurrentText(const QString& txt = QString()) { KComboBox::setCurrentText(txt); } + + /** + * overridden to set the background color of the lineedit as well + */ + void setPaletteBackgroundColor(const QColor& color); + + /** + * Overridden to support our own completion box + */ + QSize sizeHint() const; + +protected slots: + virtual void slotItemSelected(const QString& id); + +protected: + /** + * reimplemented to support our own popup widget + */ + void mousePressEvent(QMouseEvent *e); + + /** + * reimplemented to support our own popup widget + */ + void keyPressEvent(QKeyEvent *e); + + /** + * reimplemented to support our own popup widget + */ + void paintEvent(QPaintEvent *); + + /** + * reimplemented to support detection of new items + */ + void focusOutEvent(QFocusEvent* ); + + /** + * set the widgets text area based on the item with the given @a id. + */ + virtual void setCurrentTextById(const QString& id); + + /** + * Overridden for internal reasons, no API change + */ + void connectNotify(const char* signal); + + /** + * Overridden for internal reasons, no API change + */ + void disconnectNotify(const char* signal); + +protected: + /** + * This member keeps a pointer to the object's completion object + */ + kMyMoneyCompletion* m_completion; + + /** + * Use our own line edit to provide hint functionality + */ + kMyMoneyLineEdit* m_edit; + + /** + * The currently selected item + */ + QString m_id; + +signals: + void itemSelected(const QString& id); + void objectCreation(bool); + void createItem(const QString&, QString&); + +private: + QTimer m_timer; + QMutex m_focusMutex; + /** + * Flag to control object creation. Use setSuppressObjectCreation() + * to modify it's setting. Defaults to @a false. + */ + bool m_canCreateObjects; + + /** + * Flag to check whether a focusOutEvent processing is underway or not + */ + bool m_inFocusOutEvent; +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * reconciliation. + */ +class KMyMoneyReconcileCombo : public KMyMoneyCombo +{ + Q_OBJECT +public: + KMyMoneyReconcileCombo(QWidget *w = 0, const char *name=0); + + void setState(MyMoneySplit::reconcileFlagE state); + MyMoneySplit::reconcileFlagE state(void) const; + void removeDontCare(void); + +protected slots: + void slotSetState(const QString&); +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * actions (Deposit, Withdrawal, etc.). + * + * @deprecated + */ +class KMyMoneyComboAction : public KMyMoneyCombo +{ + Q_OBJECT +public: + KMyMoneyComboAction(QWidget *w = 0, const char *name=0); + + void setAction(int state); + int action(void) const; + void protectItem(int id, bool protect); + +protected slots: + void slotSetAction(const QString&); + +signals: + void actionSelected(int); +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * actions (Deposit, Withdrawal, etc.). + */ +class KMyMoneyCashFlowCombo : public KMyMoneyCombo +{ + Q_OBJECT +public: + /** + * Create a combo box that contains the entries "Pay to", "From" and + * " " for don't care. + */ + KMyMoneyCashFlowCombo(QWidget *w = 0, const char *name=0, MyMoneyAccount::accountTypeE type = MyMoneyAccount::Asset); + + void setDirection(KMyMoneyRegister::CashFlowDirection dir); + KMyMoneyRegister::CashFlowDirection direction(void) const { return m_dir; } + void removeDontCare(void); + +protected slots: + void slotSetDirection(const QString& id); + +signals: + void directionSelected(KMyMoneyRegister::CashFlowDirection); + +private: + KMyMoneyRegister::CashFlowDirection m_dir; +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible activities + * for investment transactions (buy, sell, dividend, etc.) + */ +class KMyMoneyActivityCombo : public KMyMoneyCombo +{ + Q_OBJECT +public: + /** + * Create a combo box that contains the entries "Buy", "Sell" etc. + */ + KMyMoneyActivityCombo(QWidget *w = 0, const char *name=0); + + void setActivity(MyMoneySplit::investTransactionTypeE activity); + MyMoneySplit::investTransactionTypeE activity(void) const { return m_activity; } + +protected slots: + void slotSetActivity(const QString& id); + +signals: + void activitySelected(MyMoneySplit::investTransactionTypeE); + +private: + MyMoneySplit::investTransactionTypeE m_activity; +}; + +/** + * This class implements a text based payee selector. + * When initially used, the widget has the functionality of a KComboBox object. + * Whenever a key is pressed, the set of loaded payees is searched for + * payees names which match the currently entered text. + * + * If any match is found a list selection box is opened and the user can use + * the up/down, page-up/page-down keys or the mouse to navigate in the list. If + * a payee is selected, the selection box is closed. Other key-strokes are + * directed to the parent object to manipulate the text. The visible contents of + * the selection box is updated with every key-stroke. + * + * This object is a replacement of the kMyMoneyPayee object and should be used + * for new code. + * + * @author Thomas Baumgart + */ +class KMyMoneyPayeeCombo : public KMyMoneyCombo +{ + Q_OBJECT +public: + KMyMoneyPayeeCombo(QWidget* parent = 0, const char* name = 0); + + void loadPayees(const QValueList<MyMoneyPayee>& list); +}; + +class KMyMoneyGeneralCombo : public KComboBox +{ + Q_OBJECT +public: + KMyMoneyGeneralCombo(QWidget* parent = 0, const char* name = 0); + virtual ~KMyMoneyGeneralCombo(); + + void insertItem(const QString& txt, int id, int idx = -1); + + void setItem(int id) KDE_DEPRECATED; // replace with setCurrentItem(id) + int item(void) const KDE_DEPRECATED; // replace with currentItem() + + void setCurrentItem(int id); + int currentItem(void) const; + + void removeItem(int id); + +public slots: + void clear(void); + +signals: + void itemSelected(int id); + +protected: + // prevent the caller to use the standard KComboBox insertItem function with a default idx + void insertItem(const QString&); + +protected slots: + void slotChangeItem(int idx); + +private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + + +/** + * This class implements a time period selector + * @author Thomas Baumgart + */ +class KMyMoneyPeriodCombo : public KMyMoneyGeneralCombo +{ + Q_OBJECT +public: + KMyMoneyPeriodCombo(QWidget* parent = 0, const char* name = 0); + + MyMoneyTransactionFilter::dateOptionE currentItem(void) const; + void setCurrentItem(MyMoneyTransactionFilter::dateOptionE id); + + /** + * This function returns the actual start date for the given + * period definition given by @p id. For user defined periods + * the returned value is QDate() + */ + static QDate start(MyMoneyTransactionFilter::dateOptionE id); + + /** + * This function returns the actual end date for the given + * period definition given by @p id. For user defined periods + * the returned value is QDate() + */ + static QDate end(MyMoneyTransactionFilter::dateOptionE id); + + // static void dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id); +}; + +/** + * This class implements an occurence selector + * as a parent class for both OccurencePeriod and Frequency combos + * + * @author Colin Wright + */ +class KMyMoneyOccurenceCombo : public KMyMoneyGeneralCombo +{ + Q_OBJECT +public: + KMyMoneyOccurenceCombo(QWidget* parent = 0, const char* name = 0); + + MyMoneySchedule::occurenceE currentItem(void) const; +}; + +/** + * This class implements an occurence period selector + * + * @author Colin Wright + */ +class KMyMoneyOccurencePeriodCombo : public KMyMoneyOccurenceCombo +{ + Q_OBJECT +public: + KMyMoneyOccurencePeriodCombo(QWidget* parent = 0, const char* name = 0); +}; + +/** + * This class implements a payment frequency selector + * @author Thomas Baumgart + */ +class KMyMoneyFrequencyCombo : public KMyMoneyOccurenceCombo +{ + Q_OBJECT +public: + KMyMoneyFrequencyCombo(QWidget* parent = 0, const char* name = 0); + + /** + * This method returns the number of events for the selected payment + * frequency (eg for yearly the return value is 1 and for monthly it + * is 12). In case, the frequency cannot be converted (once, every other year, etc.) + * the method returns 0. + */ + int eventsPerYear(void) const; + /** + * This method returns the number of days between two events of + * the selected frequency. The return value for months is based + * on 30 days and the year is 360 days long. + */ + int daysBetweenEvents(void) const; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycompletion.cpp b/kmymoney2/widgets/kmymoneycompletion.cpp new file mode 100644 index 0000000..f9bc7a3 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycompletion.cpp @@ -0,0 +1,304 @@ +/*************************************************************************** + kmymoneycompletion.cpp - description + ------------------- + begin : Mon Apr 26 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qapplication.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneycompletion.h" +#include <kmymoney/kmymoneyselector.h> +#include <kmymoney/kmymoneylistviewitem.h> +#include "./kmymoneycombo.h" + +const int kMyMoneyCompletion::MAX_ITEMS = 16; + +kMyMoneyCompletion::kMyMoneyCompletion(QWidget *parent, const char *name ) : + QVBox(parent, name, WType_Popup) +{ + m_selector = new KMyMoneySelector(this); + m_selector->listView()->setFocusProxy(this); + + m_parent = parent; + setFocusProxy((parent) ? parent : (QWidget*) NoFocus); + setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + connectSignals(m_selector, m_selector->listView()); +} + +void kMyMoneyCompletion::connectSignals(QWidget* widget, KListView* lv) +{ + m_widget = widget; + m_lv = lv; + connect(lv, SIGNAL(executed(QListViewItem*,const QPoint&,int)), this, SLOT(slotItemSelected(QListViewItem*,const QPoint&,int))); +} + +kMyMoneyCompletion::~kMyMoneyCompletion() +{ +} + +void kMyMoneyCompletion::adjustSize(void) +{ + QListViewItemIterator it(m_lv, QListViewItemIterator::Visible); + int count = 0; + while(it.current()) { + ++count; + ++it; + } + adjustSize(count); +} + +void kMyMoneyCompletion::adjustSize(const int count) +{ + int w = m_widget->sizeHint().width(); + if(m_parent && w < m_parent->width()) + w = m_parent->width(); + + QFontMetrics fm(font()); + if(w < fm.maxWidth()*15) + w = fm.maxWidth()*15; + + int h = 0; + QListViewItemIterator it(m_lv, QListViewItemIterator::Visible); + QListViewItem* item = it.current(); + if(item) + h = item->height() * (count > MAX_ITEMS ? MAX_ITEMS : count); + + // the offset of 4 in the next statement avoids the + // display of a scroll bar if count < MAX_ITEMS. + resize(w, h+4); + + if(m_parent) { + // the code of this basic block is taken from KCompletionBox::show() + // and modified to our local needs + + // this is probably better, once kde switches to requiring qt3.1 + // QRect screenSize = QApplication::desktop()->availableGeometry(d->m_parent); + // for now use this since it's qt3.0.x-safe + QRect screenSize = QApplication::desktop()->screenGeometry(QApplication::desktop()->screenNumber(m_parent)); + + QPoint orig = m_parent->mapToGlobal( QPoint(0, m_parent->height()) ); + int x = orig.x(); + int y = orig.y(); + + if ( x + width() > screenSize.right() ) + x = screenSize.right() - width(); + + // check for the maximum height here to avoid flipping + // of the completion box from top to bottom of the + // edit widget. The offset (y) is certainly based + // on the actual height. + if(item) { + if ((y + item->height()*MAX_ITEMS) > screenSize.bottom() ) + y = y - height() - m_parent->height(); + } + + move( x, y); + } +} + +void kMyMoneyCompletion::show(bool presetSelected) +{ + if(!m_id.isEmpty() && presetSelected) + m_selector->setSelected(m_id); + + adjustSize(); + + if(m_parent) { + m_parent->installEventFilter(this); + // make sure to install the filter for the combobox lineedit as well + // We have do this here because QObject::installEventFilter() is not + // declared virtual and we have no chance to override it in KMyMoneyCombo + KMyMoneyCombo* c = dynamic_cast<KMyMoneyCombo*>(m_parent); + if(c && c->lineEdit()) { + c->lineEdit()->installEventFilter(this); + } + } + + QVBox::show(); +} + +void kMyMoneyCompletion::hide(void) +{ + if(m_parent) { + m_parent->removeEventFilter(this); + // make sure to uninstall the filter for the combobox lineedit as well + // We have do this here because QObject::installEventFilter() is not + // declared virtual and we have no chance to override it in KMyMoneyCombo + KMyMoneyCombo* c = dynamic_cast<KMyMoneyCombo*>(m_parent); + if(c && c->lineEdit()) { + c->lineEdit()->removeEventFilter(this); + } + } + QVBox::hide(); +} + +bool kMyMoneyCompletion::eventFilter(QObject* o, QEvent* e) +{ + int type = e->type(); + + KMyMoneyCombo *c = dynamic_cast<KMyMoneyCombo*>(m_parent); + QListViewItem* item; + if(o == m_parent || (c && o == c->lineEdit())) { + if(isVisible()) { + if(type == QEvent::KeyPress) { + QKeyEvent* ev = static_cast<QKeyEvent*> (e); + QKeyEvent evt(QEvent::KeyPress, + Key_Down, 0, ev->state(), QString::null, + ev->isAutoRepeat(), ev->count()); + QKeyEvent evbt(QEvent::KeyPress, + Key_Up, 0, ev->state(), QString::null, + ev->isAutoRepeat(), ev->count()); + + switch(ev->key()) { + case Key_Tab: + case Key_BackTab: + slotItemSelected(m_lv->currentItem(), QPoint(0,0), 0); + break; + + case Key_Down: + case Key_Next: + item = m_lv->currentItem(); + while(item) { + item = item->itemBelow(); + if(item && selector()->match(m_lastCompletion, item)) + break; + } + if(item) { + m_lv->setCurrentItem(item); + selector()->ensureItemVisible(item); + } + ev->accept(); + return true; + + case Key_Up: + case Key_Prior: + item = m_lv->currentItem(); + while(item) { + item = item->itemAbove(); + if(item && selector()->match(m_lastCompletion, item)) + break; + } + if(item) { + m_lv->setCurrentItem(item); + // make sure, we always see a possible (non-selectable) group item + if(item->itemAbove()) + item = item->itemAbove(); + selector()->ensureItemVisible(item); + } + ev->accept(); + return true; + + case Key_Escape: + hide(); + ev->accept(); + return true; + + case Key_Enter: + case Key_Return: + slotItemSelected(m_lv->currentItem(), QPoint(0,0), 0); + ev->accept(); + return true; + + case Key_Home: + case Key_End: + if(ev->state() & ControlButton) { + item = m_lv->currentItem(); + if(ev->key() == Key_Home) { + while(item && item->itemAbove()) { + item = item->itemAbove(); + } + while(item && !selector()->match(m_lastCompletion, item)) { + item = item->itemBelow(); + } + } else { + while(item && item->itemBelow()) { + item = item->itemBelow(); + } + while(item && !selector()->match(m_lastCompletion, item)) { + item = item->itemAbove(); + } + } + if(item) { + m_lv->setCurrentItem(item); + // make sure, we always see a possible (non-selectable) group item + if(item->itemAbove()) + item = item->itemAbove(); + selector()->ensureItemVisible(item); + } + ev->accept(); + return true; + } + break; + + default: + break; + + } + } + } + } + return QVBox::eventFilter(o, e); +} + +void kMyMoneyCompletion::slotMakeCompletion(const QString& txt) +{ + int cnt = selector()->slotMakeCompletion(txt.stripWhiteSpace()); + + if(m_parent && m_parent->isVisible() && !isVisible() && cnt) + show(false); + else { + if(cnt != 0) { + adjustSize(); + } else { + hide(); + } + } +} + +void kMyMoneyCompletion::slotItemSelected(QListViewItem *item, const QPoint&, int) +{ + KMyMoneyListViewItem* it_v = static_cast<KMyMoneyListViewItem*>(item); + if(it_v && it_v->isSelectable()) { + QString id = it_v->id(); + // hide the widget, so we can debug the slots that are connect + // to the signal we emit very soon + hide(); + m_id = id; + emit itemSelected(id); + } +} + +void kMyMoneyCompletion::setSelected(const QString& id) +{ + m_id = id; + m_selector->setSelected(id, true); +} + +#include "kmymoneycompletion.moc" diff --git a/kmymoney2/widgets/kmymoneycompletion.h b/kmymoney2/widgets/kmymoneycompletion.h new file mode 100644 index 0000000..1efea63 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycompletion.h @@ -0,0 +1,122 @@ +/*************************************************************************** + kmymoneycompletion.h - description + ------------------- + begin : Mon Apr 26 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYCOMPLETION_H +#define KMYMONEYCOMPLETION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qvbox.h> +#include <qregexp.h> +class QListViewItem; + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KListView; + +// ---------------------------------------------------------------------------- +// Project Includes + +class KMyMoneySelector; + +/** + * @author Thomas Baumgart + */ + +class kMyMoneyCompletion : public QVBox +{ + Q_OBJECT +public: + + kMyMoneyCompletion(QWidget *parent=0, const char *name=0); + virtual ~kMyMoneyCompletion(); + + /** + * Re-implemented for internal reasons. API is unaffected. + */ + virtual void show(void) { show(true); } + + + /** + * Re-implemented for internal reasons. API is unaffected. + */ + virtual void hide(void); + + /** + * This method sets the current account with id @p id as + * the current selection. + * + * @param id id of account to be selected + */ + void setSelected(const QString& id); + + virtual KMyMoneySelector* selector(void) const { return m_selector; } + +public slots: + void slotMakeCompletion(const QString& txt); + + void slotItemSelected(QListViewItem *item, const QPoint& pos, int col); + +protected: + /** + * Reimplemented from kMyMoneyAccountSelector to get events from the viewport (to hide + * this widget on mouse-click, Escape-presses, etc. + */ + virtual bool eventFilter( QObject *, QEvent * ); + + /** + * This method resizes the widget to show a maximum of @p count + * or @a MAX_ITEMS items. + * + * @param count maximum number to be shown if < MAX_ITEMS + */ + void adjustSize(const int count); + + /** + * This method counts the number of items currently visible and + * calls adjustSize(count). + */ + void adjustSize(void); + + void connectSignals(QWidget *widget, KListView* lv); + + void show(bool presetSelected); + +signals: + void itemSelected(const QString& id); + +protected: + QWidget* m_parent; + QWidget* m_widget; + QString m_id; + KListView* m_lv; + KMyMoneySelector* m_selector; + QRegExp m_lastCompletion; + + static const int MAX_ITEMS; + +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneycurrencyselector.cpp b/kmymoney2/widgets/kmymoneycurrencyselector.cpp new file mode 100644 index 0000000..009dc2b --- /dev/null +++ b/kmymoney2/widgets/kmymoneycurrencyselector.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** + kmymoneycurrencyselector.cpp - description + ------------------- + begin : Tue Apr 6 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpixmap.h> +#include <qbitmap.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kstandarddirs.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneycurrencyselector.h" + +KMyMoneySecuritySelector::KMyMoneySecuritySelector(QWidget *parent, const char *name ) : + KComboBox(parent, name), + m_displayItem(FullName), + m_displayOnly(false), + m_displayType(TypeAll) +{ + // update(QString()); +} + +KMyMoneySecuritySelector::KMyMoneySecuritySelector(displayTypeE type, QWidget *parent, const char *name ) : + KComboBox(parent,name), + m_displayItem(FullName), + m_displayOnly(false), + m_displayType(type) +{ + // update(QString()); +} + +KMyMoneySecuritySelector::~KMyMoneySecuritySelector() +{ +} + +void KMyMoneySecuritySelector::selectDisplayItem(KMyMoneySecuritySelector::displayItemE item) +{ + m_displayItem = item; + update(QString()); +} + +void KMyMoneySecuritySelector::update(const QString& id) +{ + MyMoneySecurity curr = MyMoneyFile::instance()->baseCurrency(); + QString baseCurrency = curr.id(); + + if(!id.isEmpty()) + curr = m_currency; + + this->clear(); + m_list.clear(); + if(m_displayType & TypeCurrencies) + m_list += MyMoneyFile::instance()->currencyList(); + if(m_displayType & TypeSecurities) + m_list += MyMoneyFile::instance()->securityList(); + + // sort + qHeapSort(m_list); + + QValueList<MyMoneySecurity>::ConstIterator it; + + // construct a transparent 16x16 pixmap + QPixmap empty(16, 16); + empty.setMask(QBitmap(16, 16, true)); + + int itemId = 0; + int m_selectedItemId = 0; + for(it = m_list.begin(); it != m_list.end(); ++it) { + QString display; + switch(m_displayItem) { + default: + case FullName: + if((*it).isCurrency()) { + display = QString("%2 (%1)").arg((*it).id()).arg((*it).name()); + } else + display = QString("%2 (%1)").arg((*it).tradingSymbol()).arg((*it).name()); + break; + break; + + case Symbol: + if((*it).isCurrency()) + display = (*it).id(); + else + display = (*it).tradingSymbol(); + break; + } + if((*it).id() == baseCurrency) { + insertItem(QPixmap( locate("icon","hicolor/16x16/apps/kmymoney2.png")), + display, itemId); + } else { + insertItem(empty, display, itemId); + } + + if(curr.id() == (*it).id()) { + m_selectedItemId = itemId; + m_currency = (*it); + } + + itemId++; + } + setCurrentItem(m_selectedItemId); +} + +void KMyMoneySecuritySelector::setDisplayOnly(const bool disp) +{ + if(disp == m_displayOnly) + return; + + switch(disp) { + case true: + connect(this, SIGNAL(activated(int)), this, SLOT(slotSetInitialCurrency())); + break; + case false: + disconnect(this, SIGNAL(activated(int)), this, SLOT(slotSetInitialCurrency())); + break; + } + m_displayOnly = disp; +} + +void KMyMoneySecuritySelector::slotSetInitialSecurity(void) +{ + setCurrentItem(m_selectedItemId); +} + +const MyMoneySecurity& KMyMoneySecuritySelector::security(void) const +{ + return m_list[currentItem()]; +} + +void KMyMoneySecuritySelector::setSecurity(const MyMoneySecurity& currency) +{ + m_currency = currency; + update(QString("x")); +} + +KMyMoneyCurrencySelector::KMyMoneyCurrencySelector(QWidget *parent, const char *name ) : + KMyMoneySecuritySelector(TypeCurrencies, parent, name) +{ +} + +#include "kmymoneycurrencyselector.moc" diff --git a/kmymoney2/widgets/kmymoneycurrencyselector.h b/kmymoney2/widgets/kmymoneycurrencyselector.h new file mode 100644 index 0000000..e733eb2 --- /dev/null +++ b/kmymoney2/widgets/kmymoneycurrencyselector.h @@ -0,0 +1,90 @@ +/*************************************************************************** + kmymoneycurrencyselector.h - description + ------------------- + begin : Tue Apr 6 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYCURRENCYSELECTOR_H +#define KMYMONEYCURRENCYSELECTOR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyfile.h> + +/** + * @author Thomas Baumgart + */ + +class KMyMoneySecuritySelector : public KComboBox +{ + Q_OBJECT +public: + enum displayItemE { + Symbol = 0, + FullName + }; + + enum displayTypeE { + TypeCurrencies = 0x01, + TypeSecurities = 0x02, + TypeAll = 0x03 + }; + + KMyMoneySecuritySelector(QWidget *parent=0, const char *name=0); + KMyMoneySecuritySelector(displayTypeE type = TypeAll, QWidget *parent=0, const char *name=0); + virtual ~KMyMoneySecuritySelector(); + + const MyMoneySecurity& security(void) const; + void setSecurity(const MyMoneySecurity& currency); + void selectDisplayItem(KMyMoneySecuritySelector::displayItemE item); + void setDisplayOnly(const bool disp); + + void update(const QString& id); + +public slots: + void slotSetInitialSecurity(void); + +private: + MyMoneySecurity m_currency; + displayItemE m_displayItem; + int m_selectedItemId; + bool m_displayOnly; + displayTypeE m_displayType; + QValueList<MyMoneySecurity> m_list; +}; + +class KMyMoneyCurrencySelector : public KMyMoneySecuritySelector +{ +public: + KMyMoneyCurrencySelector(QWidget *parent=0, const char *name=0); + virtual ~KMyMoneyCurrencySelector() {} +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneydateinput.cpp b/kmymoney2/widgets/kmymoneydateinput.cpp new file mode 100644 index 0000000..e1b4be8 --- /dev/null +++ b/kmymoney2/widgets/kmymoneydateinput.cpp @@ -0,0 +1,348 @@ +/*************************************************************************** + kmymoneydateinput.cpp + ------------------- + copyright : (C) 2000 by Michael Edwardes + email : mte@users.sourceforge.net + 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpainter.h> +#include <qdrawutil.h> +#include <qpoint.h> +#include <qvalidator.h> +#include <qtimer.h> +#include <qstyle.h> +#include <qlayout.h> +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qpixmap.h> +#include <qtimer.h> +#include <qlabel.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include "kdecompat.h" +#include <kglobal.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kpushbutton.h> +#include <kshortcut.h> +#include <kpassivepopup.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneydateinput.h" + +namespace { + const int DATE_POPUP_TIMEOUT = 1500; +} + +bool KMyMoneyDateEdit::event(QEvent* e) +{ + // make sure that we keep the current date setting of a kMyMoneyDateInput object + // across the QDateEdit::event(FocusOutEvent) + bool rc; + + kMyMoneyDateInput* p = dynamic_cast<kMyMoneyDateInput*>(parentWidget()); + if(e->type() == QEvent::FocusOut && p) { + QDate d = p->date(); + rc = QDateEdit::event(e); + if(d.isValid()) + d = p->date(); + p->loadDate(d); + } else { + rc = QDateEdit::event(e); + } + return rc; +} + +kMyMoneyDateInput::kMyMoneyDateInput(QWidget *parent, const char *name, Qt::AlignmentFlags flags) + : QHBox(parent,name) +{ + m_qtalignment = flags; + m_date = QDate::currentDate(); + + dateEdit = new KMyMoneyDateEdit(m_date, this, "dateEdit"); + setFocusProxy(dateEdit); + focusWidget()->installEventFilter(this); // To get dateEdit's FocusIn/Out and some KeyPress events + dateEdit->installEventFilter(this); // To get dateEdit's FocusIn/Out and some KeyPress events + + m_datePopup = new KPassivePopup(dateEdit, "datePopup"); + m_datePopup->setTimeout(DATE_POPUP_TIMEOUT); + m_datePopup->setView(new QLabel(KGlobal::locale()->formatDate(m_date), m_datePopup, "datePopupLabel")); + + m_dateFrame = new QVBox(this, 0, WType_Popup); + m_dateFrame->setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + m_dateFrame->setLineWidth(3); + m_dateFrame->hide(); + + QString dateFormat = KGlobal::locale()->dateFormatShort().lower(); + QString order, separator; + for(unsigned i = 0; i < dateFormat.length(); ++i) { + // DD.MM.YYYY is %d.%m.%y + // dD.mM.YYYY is %e.%n.%y + // SHORTWEEKDAY, dD SHORTMONTH YYYY is %a, %e %b %Y + if(dateFormat[i] == 'y' || dateFormat[i] == 'm' || dateFormat[i] == 'n' || dateFormat[i] == 'd' || dateFormat[i] == 'e') { + if(dateFormat[i] == 'n') + dateFormat[i] = 'm'; + if(dateFormat[i] == 'e') + dateFormat[i] = 'd'; + order += dateFormat[i]; + } else if(dateFormat[i] != '%' && separator.isEmpty()) + separator = dateFormat[i]; + if(order.length() == 3) + break; + } + + // see if we find a known format. If it's unknown, then we use YMD (international) + // set m_focusDatePart to the day position (0-2) + if(order == "mdy") { + dateEdit->setOrder(QDateEdit::MDY); + m_focusDatePart = 1; + } else if(order == "dmy") { + dateEdit->setOrder(QDateEdit::DMY); + m_focusDatePart = 0; + } else if(order == "ydm") { + dateEdit->setOrder(QDateEdit::YDM); + m_focusDatePart = 1; + } else { + dateEdit->setOrder(QDateEdit::YMD); + m_focusDatePart = 2; + separator = '-'; + } + dateEdit->setSeparator(separator); + + m_datePicker = new KDatePicker(m_dateFrame, m_date); +#if KDE_IS_VERSION(3,1,0) + // Let the date picker have a close button (Added in 3.1) + m_datePicker->setCloseButton(true); +#endif + + // the next line is a try to add an icon to the button + m_dateButton = new KPushButton(QIconSet(QPixmap(KGlobal::iconLoader()->iconPath("date", -KIcon::SizeSmall))), QString(""), this); + m_dateButton->setMinimumWidth(30); + + connect(m_dateButton,SIGNAL(clicked()),SLOT(toggleDatePicker())); + connect(dateEdit, SIGNAL(valueChanged(const QDate&)), this, SLOT(slotDateChosenRef(const QDate&))); + connect(m_datePicker, SIGNAL(dateSelected(QDate)), this, SLOT(slotDateChosen(QDate))); + connect(m_datePicker, SIGNAL(dateEntered(QDate)), this, SLOT(slotDateChosen(QDate))); + connect(m_datePicker, SIGNAL(dateSelected(QDate)), m_dateFrame, SLOT(hide())); +} + +void kMyMoneyDateInput::markAsBadDate(bool bad, const QColor& color) +{ + if(dateEdit->focusProxy()) { + dateEdit->focusProxy()->setPaletteForegroundColor(paletteForegroundColor()); + if(bad) + dateEdit->focusProxy()->setPaletteForegroundColor(color); + } +} + +void kMyMoneyDateInput::show(void) +{ + // don't forget the standard behaviour ;-) + QHBox::show(); + + // If the widget is shown, the size must be fixed a little later + // to be appropriate. I saw this in some other places and the only + // way to solve this problem is to postpone the setup of the size + // to the time when the widget is on the screen. + QTimer::singleShot(50, this, SLOT(fixSize())); +} + +void kMyMoneyDateInput::fixSize(void) +{ + // According to a hint in the documentation of KDatePicker::sizeHint() + // 28 pixels should be added in each direction to obtain a better + // display of the month button. I decided, (22,14) is good + // enough and save some space on the screen (ipwizard) + m_dateFrame->setFixedSize(m_datePicker->sizeHint() + QSize(22, 14)); + + dateEdit->setMinimumWidth(dateEdit->minimumSizeHint().width() + 6); +} + +kMyMoneyDateInput::~kMyMoneyDateInput() +{ + delete m_dateFrame; + delete m_datePopup; +} + +void kMyMoneyDateInput::toggleDatePicker() +{ + int w = m_dateFrame->width(); + int h = m_dateFrame->height(); + + if(m_dateFrame->isVisible()) + { + m_dateFrame->hide(); + } + else + { + QPoint tmpPoint = mapToGlobal(m_dateButton->geometry().bottomRight()); + + // usually, the datepicker widget is shown underneath the dateEdit widget + // if it does not fit on the screen, we show it above this widget + + if(tmpPoint.y() + h > QApplication::desktop()->height()) { + tmpPoint.setY(tmpPoint.y() - h - m_dateButton->height()); + } + + if((m_qtalignment == Qt::AlignRight && tmpPoint.x()+w <= QApplication::desktop()->width()) + || (tmpPoint.x()-w < 0) ) + { + m_dateFrame->setGeometry(tmpPoint.x()-width(), tmpPoint.y(), w, h); + } + else + { + tmpPoint.setX(tmpPoint.x() - w); + m_dateFrame->setGeometry(tmpPoint.x(), tmpPoint.y(), w, h); + } + + if(m_date.isValid()) + { + m_datePicker->setDate(m_date); + } + else + { + m_datePicker->setDate(QDate::currentDate()); + } + m_dateFrame->show(); + } +} + + +void kMyMoneyDateInput::resizeEvent(QResizeEvent* ev) +{ + m_dateButton->setMaximumHeight(ev->size().height()); + m_dateButton->setMaximumWidth(ev->size().height()); + dateEdit->setMaximumHeight(ev->size().height()); + + // qDebug("Received resize-event %d,%d", ev->size().width(), ev->size().height()); +} + + +/** Overriding QWidget::keyPressEvent + * + * increments/decrements the date upon +/- or Up/Down key input + * sets the date to current date when the 'T' key is pressed + */ +void kMyMoneyDateInput::keyPressEvent(QKeyEvent * k) +{ + KShortcut today(i18n("Enter todays date into date input widget", "T")); + + switch(k->key()) { + case Key_Equal: + case Key_Plus: + slotDateChosen(m_date.addDays(1)); + break; + + case Key_Minus: + slotDateChosen(m_date.addDays(-1)); + break; + + default: + if(today.contains(KKey(k)) || k->key() == Key_T) { + slotDateChosen(QDate::currentDate()); + } + break; + } +} + +/** + * This function receives all events that are sent to focusWidget(). + * Some KeyPress events are intercepted and passed to keyPressEvent. + * Otherwise they would be consumed by QDateEdit. + */ +bool kMyMoneyDateInput::eventFilter(QObject *, QEvent *e) +{ + if (e->type() == QEvent::FocusIn) { + m_datePopup->show(); + // The cast to the base class is needed since setFocusSection + // is protected in QDateEdit. This causes some logic in + // QDateEdit::setFocusSection not to be executed but this does + // not hurt here, because the widget just receives focus. + static_cast<QDateTimeEditBase *>(dateEdit)->setFocusSection(m_focusDatePart); + } + else if (e->type() == QEvent::FocusOut) + m_datePopup->hide(); + else if (e->type() == QEvent::KeyPress) { + if (QKeyEvent *k = dynamic_cast<QKeyEvent*>(e)) { + if (k->key() == Key_Minus) { + keyPressEvent(k); + return true; + } + } + } + + return false; // Don't filter the event +} + +void kMyMoneyDateInput::slotDateChosenRef(const QDate& date) +{ + if(date.isValid()) { + emit dateChanged(date); + m_date = date; + + QLabel *lbl = static_cast<QLabel*>(m_datePopup->view()); + lbl->setText(KGlobal::locale()->formatDate(date)); + lbl->adjustSize(); + if(m_datePopup->isVisible() || hasFocus()) + m_datePopup->show(); // Repaint + } +} + +void kMyMoneyDateInput::slotDateChosen(QDate date) +{ + if(date.isValid()) { + // the next line implies a call to slotDateChosenRef() above + dateEdit->setDate(date); + } +} + +QDate kMyMoneyDateInput::date(void) const +{ + return dateEdit->date(); +} + +void kMyMoneyDateInput::setDate(QDate date) +{ + slotDateChosen(date); +} + +void kMyMoneyDateInput::loadDate(const QDate& date) +{ + m_date = m_prevDate = date; + + blockSignals(true); + dateEdit->setDate(date); + m_date = date; + blockSignals(false); +} + +void kMyMoneyDateInput::resetDate(void) +{ + setDate(m_prevDate); +} + +QWidget* kMyMoneyDateInput::focusWidget(void) const +{ + QWidget* w = dateEdit; + while(w->focusProxy()) + w = w->focusProxy(); + return w; +} + +#include "kmymoneydateinput.moc" diff --git a/kmymoney2/widgets/kmymoneydateinput.h b/kmymoney2/widgets/kmymoneydateinput.h new file mode 100644 index 0000000..4561d63 --- /dev/null +++ b/kmymoney2/widgets/kmymoneydateinput.h @@ -0,0 +1,123 @@ +/*************************************************************************** + kmymoneydateinput.h + ------------------- + copyright : (C) 2000 by Michael Edwardes + email : mte@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 KMYMONEYDATEINPUT_H +#define KMYMONEYDATEINPUT_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qlineedit.h> +#include <qdatetime.h> +#include <qdatetimeedit.h> +#include <qvbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kdatepicker.h> +class KPushButton; +class KPassivePopup; + +// ---------------------------------------------------------------------------- +// Project Includes + +// Ideas neatly taken from korganizer +// Respective authors are credited. +// Some ideas/code have been borrowed from Calendar-0.13 (phoenix.bmedesign.com/~qt) + +/** + * Provided to be able to catch the focusOut events before the contents gets changed + */ +class KMyMoneyDateEdit : public QDateEdit +{ + Q_OBJECT +public: + KMyMoneyDateEdit(const QDate& date, QWidget *parent=0, const char *name=0) : QDateEdit(date, parent, name) {} + +protected: + /** reimplemented for internal reasons */ + bool event(QEvent* e); +}; + +/** + * This class provides the general widget used for date selection + * throughout the KMyMoney project. It provides an QDateEdit widget + * which is based on an edit field with spin boxes and adds a QPushButton + * to open a KDatePicker. + */ +class kMyMoneyDateInput : public QHBox +{ + Q_OBJECT + +public: + kMyMoneyDateInput(QWidget *parent=0, const char *name=0, Qt::AlignmentFlags flags=Qt::AlignLeft); + ~kMyMoneyDateInput(); + + // Replace calls to this with the new date() method + // QDate getQDate(void) KDE_DEPRECATED; + + QDate date(void) const; + void setDate(QDate date); + void loadDate(const QDate& date); + void resetDate(void); + QWidget* focusWidget(void) const; + virtual void setRange(const QDate & min, const QDate & max) { dateEdit->setRange(min, max); } + void markAsBadDate(bool bad = false, const QColor& = QColor()); + +public slots: + virtual void show(void); + +signals: + void dateChanged(const QDate& date); + +protected: + /** + * - increments/decrements the date upon +/- key input + * - increments/decrements the date upon Up/Down key input + * - sets the date to current date when the 'T' key is pressed. + * The actual key for this to happen might be overridden through + * an i18n package. The 'T'-key is always possible. + */ + void keyPressEvent(QKeyEvent * k); + void resizeEvent(QResizeEvent*); + + /** To intercept events sent to focusWidget() */ + bool eventFilter(QObject *o, QEvent *e); + +protected slots: + void slotDateChosen(QDate date); + void toggleDatePicker(); + +private slots: + void slotDateChosenRef(const QDate& date); + void fixSize(void); + +private: + QDateEdit *dateEdit; + KDatePicker *m_datePicker; + QDate m_date; // The date ! + QDate m_prevDate; + Qt::AlignmentFlags m_qtalignment; + QVBox *m_dateFrame; + KPushButton *m_dateButton; + KPassivePopup *m_datePopup; + int m_focusDatePart; +}; + +#endif + diff --git a/kmymoney2/widgets/kmymoneydatetbl.cpp b/kmymoney2/widgets/kmymoneydatetbl.cpp new file mode 100644 index 0000000..e48e01e --- /dev/null +++ b/kmymoney2/widgets/kmymoneydatetbl.cpp @@ -0,0 +1,698 @@ +/*************************************************************************** + kmymoneydatetbl.cpp - description + ------------------- + begin : Thu Jul 3 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@users.sourceforge.net> + ***************************************************************************/ + + /**************************************************************************** + Contains code from the KDateTable class ala kdelibs-3.1.2. Original license: + + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qglobal.h> +#include <qdatetime.h> +#include <qstring.h> +#include <qpen.h> +#include <qpainter.h> +#include <qdialog.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include "kdecompat.h" +#include <kdatetbl.h> // Use the classes available for maximum re-use +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kapplication.h> +#include <klocale.h> +#include <kdebug.h> +#include <knotifyclient.h> +#if KDE_IS_VERSION(3,2,0) +#include <kcalendarsystem.h> +#endif + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneydatetbl.h" + +kMyMoneyDateTbl::kMyMoneyDateTbl(QWidget *parent, QDate date_, const char* name, WFlags f) + : QGridView(parent, name, f) +{ + // call this first to make sure that member variables are initialized + setType(MONTHLY); + + setFontSize(10); + + if(!date_.isValid()) + { + kdDebug() << "kMyMoneyDateTbl ctor: WARNING: Given date is invalid, using current date." << endl; + date_=QDate::currentDate(); + } + setFocusPolicy( QWidget::StrongFocus ); + + + viewport()->setEraseColor(KGlobalSettings::baseColor()); + + setDate(date_); // this initializes firstday, numdays, numDaysPrevMonth + + // So we can emit hoverDate +// QApplication::setGlobalMouseTracking(true); + viewport()->setMouseTracking(true); +} + +void +kMyMoneyDateTbl::paintCell(QPainter *painter, int row, int col) +{ + QRect rect; + QString text; + QPen pen; + int w=cellWidth(); + int h=cellHeight(); + QBrush brushBlue(KGlobalSettings::activeTitleColor()); + QBrush brushLightblue(KGlobalSettings::baseColor()); + QFont font=KGlobalSettings::generalFont(); + + // ----- + font.setPointSize(fontsize); + +#if KDE_VERSION < 310 + int firstWeekDay = KGlobal::locale()->weekStartsMonday() ? 1 : 0; +#else + int firstWeekDay = KGlobal::locale()->weekStartDay(); +#endif + + if (row==0) + { // we are drawing the headline + if (m_type == MONTHLY) + { + font.setBold(true); + painter->setFont(font); + bool normalday = true; + QString daystr; + if ( col+firstWeekDay < 8 ) + daystr = WEEK_DAY_NAME(col+firstWeekDay, true); + else + daystr = WEEK_DAY_NAME(col+firstWeekDay-7, true); + + if ( daystr==i18n("Sunday", "Sun") || daystr==i18n("Saturday", "Sat") ) + normalday=false; + + if (!normalday) + { + painter->setPen(KGlobalSettings::baseColor()); + painter->setBrush(brushLightblue); + painter->drawRect(0, 0, w, h); + painter->setPen(KGlobalSettings::activeTitleColor()); + } else { + painter->setPen(KGlobalSettings::activeTitleColor()); + painter->setBrush(brushBlue); + painter->drawRect(0, 0, w, h); + painter->setPen(KGlobalSettings::activeTextColor()); + } + painter->drawText(0, 0, w, h-1, AlignCenter, + daystr, -1, &rect); + painter->setPen(KGlobalSettings::textColor()); + painter->moveTo(0, h-1); + painter->lineTo(w-1, h-1); + + if(rect.width()>maxCell.width()) + maxCell.setWidth(rect.width()); + + if(rect.height()>maxCell.height()) + maxCell.setHeight(rect.height()); + } + else if (m_type == WEEKLY) + { + painter->setPen(KGlobalSettings::activeTitleColor()); + painter->setBrush(brushBlue); + painter->drawRect(0, 0, w, h); + painter->setPen(KGlobalSettings::activeTextColor()); + + int year=date.year(); + QString headerText; +#if QT_VERSION > 0x030005 + // FIXME: Shouldn't that be i18n()'ed as well + QString weekStr = QString::number(date.weekNumber(&year)); + QString yearStr = QString::number(year); + headerText = i18n("Week %1 for year %2.") + .arg(weekStr) + .arg(yearStr); +#else + // FIXME: include code to display the same as for KDE >= 3.0.5 + QString weekStr = QString::number(weekNumber(date, &year)); + QString yearStr = QString::number(year); + headerText = i18n("Week %1 for year %2.") + .arg(weekStr) + .arg(yearStr); +#endif + + painter->drawText(0, 0, w, h-1, AlignCenter, headerText, -1, &rect); + + maxCell.setWidth(width()); + + if(rect.height()>maxCell.height()) + maxCell.setHeight(rect.height()); + } + else if (m_type == QUARTERLY) + { + int athird = width()/3; + + painter->setPen(KGlobalSettings::activeTitleColor()); + painter->setBrush(brushBlue); + painter->setPen(/*KGlobalSettings::activeTextColor()*/black); + + if (col == 0) + { + painter->drawRect(0, 0, athird, h); + painter->drawText(0, 0, athird, h-1, AlignCenter, "Month 1", -1, &rect); + + painter->drawRect(athird, 0, athird, h); + painter->drawText(athird, 0, athird, h-1, AlignCenter, "Month 2", -1, &rect); + + painter->drawRect(athird*2, 0, athird, h); + painter->drawText(athird*2, 0, athird, h-1, AlignCenter, "Month 3", -1, &rect); + } + } + } + else + { + int pos; + + QDate drawDate(date); + + if (m_type == MONTHLY) + { + pos=7*(row-1)+col; + if ( firstWeekDay < 4 ) + pos += firstWeekDay; + else + pos += firstWeekDay - 7; + + if (pos<firstday || (firstday+numdays<=pos)) + { // we are either + // painting a day of the previous month or + // painting a day of the following month + + if (pos<firstday) + { // previous month + drawDate = drawDate.addMonths(-1); + text.setNum(numDaysPrevMonth+pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } else { // following month + drawDate = drawDate.addMonths(1); + text.setNum(pos-firstday-numdays+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } else { // paint a day of the current month + text.setNum(pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } + else if (m_type == WEEKLY) + { + // TODO: Handle other start weekdays than Monday + text = QDate::shortDayName(row); + text += " "; + + int dayOfWeek = date.dayOfWeek(); + int diff; + + if (row < dayOfWeek) + { + diff = -(dayOfWeek - row); + } + else + { + diff = row - dayOfWeek; + } + + drawDate = date.addDays(diff); + } + else if (m_type == QUARTERLY) + { + } + + drawCellContents(painter, row, col, drawDate); + } +} + +void +kMyMoneyDateTbl::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Prior ) { + setDate(date.addMonths(-1)); + return; + } + if ( e->key() == Qt::Key_Next ) { + setDate(date.addMonths(1)); + return; + } + + if ( e->key() == Qt::Key_Up ) { + if ( date.day() > 7 ) { + setDate(date.addDays(-7)); + return; + } + } + if ( e->key() == Qt::Key_Down ) { + if ( date.day() <= date.daysInMonth()-7 ) { + setDate(date.addDays(7)); + return; + } + } + if ( e->key() == Qt::Key_Left ) { + if ( date.day() > 1 ) { + setDate(date.addDays(-1)); + return; + } + } + if ( e->key() == Qt::Key_Right ) { + if ( date.day() < date.daysInMonth() ) { + setDate(date.addDays(1)); + return; + } + } + + if ( e->key() == Qt::Key_Minus ) { + setDate(date.addDays(-1)); + return; + } + if ( e->key() == Qt::Key_Plus ) { + setDate(date.addDays(1)); + return; + } + if ( e->key() == Qt::Key_N ) { + setDate(QDate::currentDate()); + return; + } + + KNotifyClient::beep(); +} + +void +kMyMoneyDateTbl::viewportResizeEvent(QResizeEvent * e) +{ + if (e) + QGridView::viewportResizeEvent(e); + + setCellWidth(viewport()->width()/m_colCount); + setCellHeight(viewport()->height()/m_rowCount); +} + +void +kMyMoneyDateTbl::setFontSize(int size) +{ + int count; + QFontMetrics metrics(fontMetrics()); + QRect rect; + + // ----- store rectangles: + fontsize=size; + + // ----- find largest day name: + maxCell.setWidth(0); + maxCell.setHeight(0); + + for(count=0; count<m_colCount; ++count) + { + rect=metrics.boundingRect(WEEK_DAY_NAME(count+1, true)); + maxCell.setWidth(QMAX(maxCell.width(), rect.width())); + maxCell.setHeight(QMAX(maxCell.height(), rect.height())); + } + + if (m_type == WEEKLY) + { + // Re-size to width + maxCell.setWidth(width()); + } + + // ----- compare with a real wide number and add some space: + rect=metrics.boundingRect(QString::fromLatin1("88")); + maxCell.setWidth(QMAX(maxCell.width()+2, rect.width())); + maxCell.setHeight(QMAX(maxCell.height()+4, rect.height())); +} + +void +kMyMoneyDateTbl::wheelEvent ( QWheelEvent * e ) +{ + setDate(date.addMonths( -(int)(e->delta()/120)) ); + e->accept(); +} + +void +kMyMoneyDateTbl::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if (e->type()!=QEvent::MouseButtonRelease) + { // the KDatePicker only reacts on mouse press events: + return; + } + + if(!isEnabled()) + { + KNotifyClient::beep(); + return; + } + +#if KDE_VERSION < 310 + int dayoff = KGlobal::locale()->weekStartsMonday() ? 1 : 0; +#else + int dayoff = KGlobal::locale()->weekStartDay(); +#endif + + // ----- + int row, col, pos, temp; + QPoint mouseCoord; + + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + if(row<1 || col<0) + { // the user clicked on the frame of the table + return; + } + + if (m_type == MONTHLY) + { + // Rows and columns are zero indexed. The (row - 1) below is to avoid counting + // the row with the days of the week in the calculation. We however want pos + // to be "1 indexed", hence the "+ 1" at the end of the sum. + pos = (7 * (row - 1)) + col + 1; + + // This gets pretty nasty below. firstday is a locale independant index for + // the first day of the week. dayoff is the day of the week that the week + // starts with for the selected locale. Monday = 1 .. Sunday = 7 + // Strangely, in some cases dayoff is in fact set to 8, hence all of the + // "dayoff % 7" sections below. + + if (pos + dayoff % 7 <= firstday) + { // this day is in the previous month + setDate(date.addDays(-1 * (date.day() + firstday - pos - dayoff % 7))); + return; + } + + if (firstday + numdays < pos + dayoff % 7) + { // this date is in the next month + setDate(date.addDays(pos - firstday - date.day() + dayoff % 7)); + return; + } + temp = firstday + date.day() - dayoff % 7 - 1; + + setDate(QDate(date.year(), date.month(), pos - firstday + dayoff % 7)); + + updateCell(temp/7+1, temp%7); // Update the previously selected cell + } + else if (m_type == WEEKLY) + { + int dayOfWeek = date.dayOfWeek(); + int diff; + + if (row < dayOfWeek) + { + diff = -(dayOfWeek - row); + } + else + { + diff = row - dayOfWeek; + } + + setDate(date.addDays(diff)); + updateCell(dayOfWeek, 0); + } + else if (m_type == QUARTERLY) + { + } + + updateCell(row, col); // Update the selected cell + + emit(tableClicked()); +} + +bool +kMyMoneyDateTbl::setDate(const QDate& date_) +{ + bool changed=false; + QDate temp; + // ----- + if(!date_.isValid()) + { + kdDebug() << "kMyMoneyDateTbl::setDate: refusing to set invalid date." << endl; + return false; + } + + if(date!=date_) + { + date=date_; + changed=true; + } + + temp.setYMD(date.year(), date.month(), 1); + firstday=temp.dayOfWeek(); + + if (firstday==1) + firstday=8; + + numdays=date.daysInMonth(); + + if (date.month()==1) + { // set to december of previous year + temp.setYMD(date.year()-1, 12, 1); + } else { // set to previous month + temp.setYMD(date.year(), date.month()-1, 1); + } + + numDaysPrevMonth=temp.daysInMonth(); + + if (changed) + { + repaintContents(false); + } + + emit(dateChanged(date)); + return true; +} + +const QDate& +kMyMoneyDateTbl::getDate() const +{ + return date; +} + +// what are those repaintContents() good for? (pfeiffer) +void kMyMoneyDateTbl::focusInEvent( QFocusEvent *e ) +{ +// repaintContents(false); + QGridView::focusInEvent( e ); +} + +void kMyMoneyDateTbl::focusOutEvent( QFocusEvent *e ) +{ +// repaintContents(false); + QGridView::focusOutEvent( e ); +} + +QSize +kMyMoneyDateTbl::sizeHint() const +{ + if (maxCell.height()>0 && maxCell.width()>0) + { + return QSize(maxCell.width()*numCols()+2*frameWidth(), + (maxCell.height()+2)*numRows()+2*frameWidth()); + } else { + kdDebug() << "kMyMoneyDateTbl::sizeHint: obscure failure - " << endl; + return QSize(-1, -1); + } +} + + +void kMyMoneyDateTbl::setType(calendarType type) +{ + if (type == WEEKLY) + { + m_rowCount = 8; + m_colCount = 1; + m_type = WEEKLY; + } + else if (type == QUARTERLY) + { + m_rowCount = 7; + m_colCount = 21; + m_type = QUARTERLY; + } + else // default to monthly + { + m_rowCount = m_colCount = 7; + m_type = MONTHLY; + } + + setNumRows(m_rowCount); + setNumCols(m_colCount); + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + + viewportResizeEvent(NULL); +} + +void kMyMoneyDateTbl::contentsMouseMoveEvent(QMouseEvent* e) +{ + int row, col, pos; + QPoint mouseCoord; + + mouseCoord = e->pos(); + row = rowAt(mouseCoord.y()); + col = columnAt(mouseCoord.x()); + if (row<1 || col<0) + { + return; + } + +#if KDE_VERSION < 310 + int firstWeekDay = KGlobal::locale()->weekStartsMonday() ? 1 : 0; +#else + int firstWeekDay = KGlobal::locale()->weekStartDay(); +#endif + + QDate drawDate(date); + QString text; + + if (m_type == MONTHLY) + { + pos=7*(row-1)+col; + if ( firstWeekDay < 4 ) + pos += firstWeekDay; + else + pos += firstWeekDay - 7; + + if (pos<firstday || (firstday+numdays<=pos)) + { // we are either + // painting a day of the previous month or + // painting a day of the following month + + if (pos<firstday) + { // previous month + drawDate = drawDate.addMonths(-1); + text.setNum(numDaysPrevMonth+pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } else { // following month + drawDate = drawDate.addMonths(1); + text.setNum(pos-firstday-numdays+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } else { // paint a day of the current month + text.setNum(pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } + else if (m_type == WEEKLY) + { + // TODO: Handle other start weekdays than Monday + text = QDate::shortDayName(row); + text += " "; + + int dayOfWeek = date.dayOfWeek(); + int diff; + + if (row < dayOfWeek) + { + diff = -(dayOfWeek - row); + } + else + { + diff = row - dayOfWeek; + } + + drawDate = date.addDays(diff); + } + else if (m_type == QUARTERLY) + { + } + + if (m_drawDateOrig != drawDate) + { + m_drawDateOrig = drawDate; + emit hoverDate(drawDate); + } + + QGridView::contentsMouseMoveEvent(e); +} + +#if QT_VERSION <= 0x030005 +// The following code is borrowed from QT 3.2 QDate::weekNumber() +// and slightly modified +int kMyMoneyDateTbl::weekNumber(const QDate& date, int *yearNumber) const +{ + if ( !date.isValid() ) + return 0; + + int dow = date.dayOfWeek(); + int doy = date.dayOfYear(); + int currYear = date.year(); + int jan1WeekDay = QDate( currYear, 1, 1 ).dayOfWeek(); + int yearNum; + int weekNum; + + if ( doy <= (8 - jan1WeekDay) && jan1WeekDay > 4 ) { + yearNum = currYear - 1; + weekNum = 52; + if ( jan1WeekDay == 5 || + (jan1WeekDay == 6 && QDate::leapYear(yearNum)) ) + weekNum++; + } else { + int totalDays = 365; + if ( QDate::leapYear(currYear) ) + totalDays++; + + if ( (totalDays - doy < 4 - dow) + || (jan1WeekDay == 7 && totalDays - doy < 3) ) { + yearNum = currYear + 1; + weekNum = 1; + } else { + int j = doy + ( 7 - dow ) + ( jan1WeekDay - 1 ); + yearNum = currYear; + weekNum = j / 7; + if ( jan1WeekDay > 4 ) + weekNum--; + } + } + if ( yearNumber ) + *yearNumber = yearNum; + return weekNum; + +} +#endif + +#include "kmymoneydatetbl.moc" diff --git a/kmymoney2/widgets/kmymoneydatetbl.h b/kmymoney2/widgets/kmymoneydatetbl.h new file mode 100644 index 0000000..7ff6610 --- /dev/null +++ b/kmymoney2/widgets/kmymoneydatetbl.h @@ -0,0 +1,189 @@ +/*************************************************************************** + kmymoneydatetbl.h - description + ------------------- + begin : Thu Jul 3 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@users.sourceforge.net> + ***************************************************************************/ + +/**************************************************************************** + Contains code from the KDateTable class ala kdelibs-3.1.2. Original license: + + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/*************************************************************************** + * * + * 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 KMYMONEYDATETBL_H +#define KMYMONEYDATETBL_H + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qgridview.h> +#include <qdatetime.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include "kdecompat.h" + +// ---------------------------------------------------------------------------- +// Project Includes + + + +/** + * @author Michael Edwardes + */ +class kMyMoneyDateTbl : public QGridView { + Q_OBJECT +public: + enum calendarType { WEEKLY, + MONTHLY, + QUARTERLY }; + +public: + /** + * The constructor. + */ + kMyMoneyDateTbl(QWidget *parent=0, + QDate date=QDate::currentDate(), + const char* name=0, WFlags f=0); + /** + * Returns a recommended size for the widget. + * To save some time, the size of the largest used cell content is + * calculated in each paintCell() call, since all calculations have + * to be done there anyway. The size is stored in maxCell. The + * sizeHint() simply returns a multiple of maxCell. + */ + virtual QSize sizeHint() const; + /** + * Set the font size of the date table. + */ + virtual void setFontSize(int size); + /** + * Select and display this date. + */ + virtual bool setDate(const QDate&); + virtual const QDate& getDate() const; + + virtual void setType(calendarType type); + virtual calendarType type(void) const { return m_type; } + +signals: + /** + * The selected date changed. + */ + void dateChanged(QDate); + /** + * A date has been selected by clicking on the table. + */ + void tableClicked(); + + /** + * + **/ + virtual void hoverDate(QDate); + +protected: + /** + * Paint a cell. + */ + virtual void paintCell(QPainter*, int, int); + /** + * Handle the resize events. + */ + virtual void viewportResizeEvent(QResizeEvent *); + /** + * React on mouse clicks that select a date. + */ + virtual void contentsMouseReleaseEvent(QMouseEvent *); + virtual void wheelEvent( QWheelEvent * e ); + virtual void keyPressEvent( QKeyEvent *e ); + virtual void focusInEvent( QFocusEvent *e ); + virtual void focusOutEvent( QFocusEvent *e ); + + virtual void drawCellContents(QPainter *painter, int row, int col, const QDate& theDate) = 0; + + virtual void contentsMouseMoveEvent(QMouseEvent* e); + + /** + * The font size of the displayed text. + */ + int fontsize; + /** + * The currently selected date. + */ + QDate date; + /** + * The day of the first day in the month [1..7]. + */ + int firstday; + /** + * The number of days in the current month. + */ + int numdays; + /** + * The number of days in the previous month. + */ + int numDaysPrevMonth; + /** + * unused + * ### remove in KDE 4.0 + */ + bool unused_hasSelection; + /** + * Save the size of the largest used cell content. + */ + QRect maxCell; + + /** + * Type related variables + **/ + calendarType m_type; + int m_colCount; + int m_rowCount; + + /// + QDate m_drawDateOrig; + +private: +#if QT_VERSION <= 0x030005 + int weekNumber(const QDate&, int *yr) const; +#endif + +#if KDE_IS_VERSION(3,2,0) + #define WEEK_DAY_NAME(a,b) KGlobal::locale()->calendar()->weekDayName(a,b) +#else + #define WEEK_DAY_NAME(a,b) KGlobal::locale()->weekDayName(a,b) +#endif +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyedit.cpp b/kmymoney2/widgets/kmymoneyedit.cpp new file mode 100644 index 0000000..87be674 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyedit.cpp @@ -0,0 +1,559 @@ +/*************************************************************************** + kmymoneyedit.cpp + ------------------- + copyright : (C) 2000 by Michael Edwardes, + 2004 by Thomas Baumgart + email : mte@users.sourceforge.net + 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qwidget.h> +#include <qvbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <klineedit.h> +#include <kiconloader.h> +#include <kconfig.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneylineedit.h> +#include "kmymoneyedit.h" +#include "kmymoneycalculator.h" +#include "../mymoney/mymoneymoney.h" + +kMyMoneyMoneyValidator::kMyMoneyMoneyValidator(QObject * parent, const char * name) : + QDoubleValidator(parent, name) +{ +} + +kMyMoneyMoneyValidator::kMyMoneyMoneyValidator( double bottom, double top, int decimals, + QObject * parent, const char * name ) : + QDoubleValidator(bottom, top, decimals, parent, name) +{ +} + +/* + * The code of the following function is taken from kdeui/knumvalidator.cpp + * and adjusted to always use the monetary symbols defined in the KDE control center + */ +QValidator::State kMyMoneyMoneyValidator::validate( QString & input, int & _p ) const +{ + QString s = input; + KLocale * l = KGlobal::locale(); + // ok, we have to re-format the number to have: + // 1. decimalSymbol == '.' + // 2. negativeSign == '-' + // 3. positiveSign == <empty> + // 4. thousandsSeparator() == <empty> (we don't check that there + // are exactly three decimals between each separator): + QString d = l->monetaryDecimalSymbol(), + n = l->negativeSign(), + p = l->positiveSign(), + t = l->monetaryThousandsSeparator(); + // first, delete p's and t's: + if ( !p.isEmpty() ) + for ( int idx = s.find( p ) ; idx >= 0 ; idx = s.find( p, idx ) ) + s.remove( idx, p.length() ); + + + if ( !t.isEmpty() ) + for ( int idx = s.find( t ) ; idx >= 0 ; idx = s.find( t, idx ) ) + s.remove( idx, t.length() ); + + // then, replace the d's and n's + if ( ( !n.isEmpty() && n.find('.') != -1 ) || + ( !d.isEmpty() && d.find('-') != -1 ) ) { + // make sure we don't replace something twice: + kdWarning() << "KDoubleValidator: decimal symbol contains '-' or " + "negative sign contains '.' -> improve algorithm" << endl; + return Invalid; + } + + if ( !d.isEmpty() && d != "." ) + for ( int idx = s.find( d ) ; idx >= 0 ; idx = s.find( d, idx + 1 ) ) + s.replace( idx, d.length(), "."); + + if ( !n.isEmpty() && n != "-" ) + for ( int idx = s.find( n ) ; idx >= 0 ; idx = s.find( n, idx + 1 ) ) + s.replace( idx, n.length(), "-" ); + + // Take care of monetary parens around the value if selected via + // the locale settings. + // If the lead-in or lead-out paren is present, remove it + // before passing the string to the QDoubleValidator + if(l->negativeMonetarySignPosition() == KLocale::ParensAround + || l->positiveMonetarySignPosition() == KLocale::ParensAround) { + QRegExp regExp("^(\\()?([\\d-\\.]*)(\\))?$"); + if(s.find(regExp) != -1) { + s = regExp.cap(2); + } + } + + // check for non numeric values (QDoubleValidator allows an 'e', we don't) + QRegExp nonNumeric("[^\\d-\\.]+"); + if(s.find(nonNumeric) != -1) + return Invalid; + + // check for minus sign trailing the number + QRegExp trailingMinus("^([^-]*)\\w*-$"); + if(s.find(trailingMinus) != -1) { + s = QString("-%1").arg(trailingMinus.cap(1)); + } + + // check for the maximum allowed number of decimal places + int decPos = s.find('.'); + if(decPos != -1) { + if(decimals() == 0) + return Invalid; + if(((int)(s.length()) - decPos) > decimals()) + return Invalid; + } + + // If we have just a single minus sign, we are done + if(s == QString("-")) + return Acceptable; + + QValidator::State rc = QDoubleValidator::validate( s, _p ); + + if(rc == Acceptable) { + // If the numeric value is acceptable, we check if the parens + // are ok. If only the lead-in is present, the return value + // is intermediate, if only the lead-out is present then it + // definitely is invalid. Nevertheless, we check for parens + // only, if the locale settings have it enabled. + if(l->negativeMonetarySignPosition() == KLocale::ParensAround + || l->positiveMonetarySignPosition() == KLocale::ParensAround) { + int tmp = input.contains('(') - input.contains(')'); + if(tmp > 0) + rc = Intermediate; + else if(tmp < 0) + rc = Invalid; + } + } + return rc; +} + +kMyMoneyEdit::kMyMoneyEdit(QWidget *parent, const char *name, const int prec) + : QHBox(parent, name) +{ + m_prec = prec; + if(prec < -1 || prec > 20) + m_prec = KGlobal::locale()->fracDigits(); + init(); +} + +kMyMoneyEdit::kMyMoneyEdit(const MyMoneySecurity& sec, QWidget *parent, const char *name) + : QHBox(parent, name) +{ + m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); + init(); +} + +// converted image from kde3.5.1/share/apps/kdevdesignerpart/pics/designer_resetproperty.png +static const uchar resetButtonImage[] = { + 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, + 0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52, + 0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06, + 0x08,0x06,0x00,0x00,0x00,0x0F,0x0E,0x84, + 0x76,0x00,0x00,0x00,0x06,0x62,0x4B,0x47, + 0x44,0x00,0xFF,0x00,0xFF,0x00,0xFF,0xA0, + 0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70, + 0x48,0x59,0x73,0x00,0x00,0x0B,0x13,0x00, + 0x00,0x0B,0x13,0x01,0x00,0x9A,0x9C,0x18, + 0x00,0x00,0x00,0x07,0x74,0x49,0x4D,0x45, + 0x07,0xD6,0x06,0x10,0x09,0x36,0x0C,0x58, + 0x91,0x11,0x7C,0x00,0x00,0x00,0x64,0x49, + 0x44,0x41,0x54,0x78,0xDA,0x65,0xC9,0xA1, + 0x0D,0x02,0x41,0x18,0x84,0xD1,0xF7,0x5F, + 0x13,0x04,0x9A,0x39,0x43,0x68,0x81,0x02, + 0x10,0xB8,0x13,0x74,0x80,0xC1,0x21,0x76, + 0x1D,0xDD,0xD0,0x01,0x65,0x10,0x34,0x9A, + 0x0C,0x66,0x83,0x61,0x92,0x2F,0x23,0x5E, + 0x25,0x01,0xBD,0x6A,0xC6,0x1D,0x9B,0x25, + 0x79,0xC2,0x34,0xE0,0x30,0x00,0x56,0xBD, + 0x6A,0x0D,0xD5,0x38,0xE1,0xEA,0x7F,0xE7, + 0x4A,0xA2,0x57,0x1D,0x71,0xC1,0x07,0xBB, + 0x81,0x8F,0x09,0x96,0xE4,0x86,0x3D,0xDE, + 0x78,0x8D,0x48,0xF2,0xAB,0xB1,0x1D,0x9F, + 0xC6,0xFC,0x05,0x46,0x68,0x28,0x6B,0x58, + 0xEE,0x72,0x0A,0x00,0x00,0x00,0x00,0x49, + 0x45,0x4E,0x44,0xAE,0x42,0x60,0x82 +}; + +void kMyMoneyEdit::init(void) +{ + allowEmpty = false; + m_edit = new kMyMoneyLineEdit(this, 0, true); + m_edit->installEventFilter(this); + setFocusProxy(m_edit); + + // Yes, just a simple double validator ! + kMyMoneyMoneyValidator *validator = new kMyMoneyMoneyValidator(this); + m_edit->setValidator(validator); + m_edit->setAlignment(AlignRight | AlignVCenter); + + m_calculatorFrame = new QVBox(this, 0, WType_Popup); + + m_calculatorFrame->setFrameStyle(QFrame::PopupPanel | QFrame::Raised); + m_calculatorFrame->setLineWidth(3); + + m_calculator = new kMyMoneyCalculator(m_calculatorFrame); + m_calculatorFrame->setFixedSize(m_calculator->width()+3, m_calculator->height()+3); + m_calculatorFrame->hide(); + + m_calcButton = new KPushButton(QIconSet(QPixmap(KGlobal::iconLoader()->iconPath("kcalc", -KIcon::SizeSmall))), QString(""), this); + m_calcButton->setFixedWidth( m_calcButton->sizeHint().width() ); + m_calcButton->setFixedHeight(m_edit->sizeHint().height()); + m_calcButton->setFocusProxy(m_edit); + + QPixmap pixmap; + pixmap.loadFromData(resetButtonImage, sizeof(resetButtonImage), "PNG", 0); + m_resetButton = new KPushButton(QIconSet(pixmap), QString(""), this); + m_resetButton->setFixedWidth( m_resetButton->sizeHint().width() ); + m_resetButton->setFixedHeight(m_edit->sizeHint().height()); + m_resetButton->setEnabled(false); + m_resetButton->setFocusProxy(m_edit); + + KConfig *kconfig = KGlobal::config(); + kconfig->setGroup("General Options"); + if(kconfig->readBoolEntry("DontShowCalculatorButton", false) == true) + setCalculatorButtonVisible(false); + + setSpacing(0); + + connect(m_edit, SIGNAL(textChanged(const QString&)), this, SLOT(theTextChanged(const QString&))); + connect(m_calculator, SIGNAL(signalResultAvailable()), this, SLOT(slotCalculatorResult())); + connect(m_calcButton, SIGNAL(clicked()), this, SLOT(slotCalculatorOpen())); + connect(m_resetButton, SIGNAL(clicked()), this, SLOT(resetText())); +} + +void kMyMoneyEdit::setValidator(const QValidator* v) +{ + m_edit->setValidator(v); +} + +kMyMoneyEdit::~kMyMoneyEdit() +{ + delete m_calculatorFrame; +} + +KLineEdit* kMyMoneyEdit::lineedit(void) const +{ + return m_edit; +} + +void kMyMoneyEdit::setPrecision(const int prec) +{ + if(prec >= -1 && prec <= 20) { + if(prec != m_prec) { + m_prec = prec; + // update current display + setValue(value()); + } + } +} + +bool kMyMoneyEdit::isValid(void) const +{ + return !(m_edit->text().isEmpty()); +} + +MyMoneyMoney kMyMoneyEdit::value(void) const +{ + QString txt = m_edit->text(); + ensureFractionalPart(txt); + MyMoneyMoney money(txt); + if(m_prec != -1) + money = money.convert(MyMoneyMoney::precToDenom(m_prec)); + return money; +} + +void kMyMoneyEdit::setValue(const MyMoneyMoney& value) +{ + // load the value into the widget but don't use thousandsSeparators + QString txt = value.formatMoney("", m_prec, false); + loadText(txt); +} + +void kMyMoneyEdit::loadText(const QString& txt) +{ + m_edit->setText(txt); + if(isEnabled() && !txt.isEmpty()) + ensureFractionalPart(); + m_text = m_edit->text(); + m_resetButton->setEnabled(false); +} + +void kMyMoneyEdit::clearText(void) +{ + m_text = QString(); + m_edit->setText(m_text); +} + +void kMyMoneyEdit::resetText(void) +{ + m_edit->setText(m_text); + m_resetButton->setEnabled(false); +} + +void kMyMoneyEdit::theTextChanged(const QString & theText) +{ + KLocale * l = KGlobal::locale(); + QString d = l->monetaryDecimalSymbol(); + QString l_text = theText; + QString nsign, psign; + if(l->negativeMonetarySignPosition() == KLocale::ParensAround + || l->positiveMonetarySignPosition() == KLocale::ParensAround) { + nsign = psign = "("; + } else { + nsign = l->negativeSign(); + psign = l->positiveSign(); + } + + int i = 0; + if(isEnabled()) { + QValidator::State state = m_edit->validator()->validate( l_text, i); + if(state == QValidator::Intermediate) { + if(l_text.length() == 1) { + if(l_text != d && l_text != nsign && l_text != psign && l_text != "-") + state = QValidator::Invalid; + } + } + if (state==QValidator::Invalid) + m_edit->setText(previousText); + else { + previousText = l_text; + emit textChanged(m_edit->text()); + m_resetButton->setEnabled(true); + } + } +} + +void kMyMoneyEdit::ensureFractionalPart(void) +{ + QString s(m_edit->text()); + ensureFractionalPart(s); + m_edit->setText(s); +} + +void kMyMoneyEdit::ensureFractionalPart(QString& s) const +{ + + KLocale* locale = KGlobal::locale(); + QString decimalSymbol = locale->monetaryDecimalSymbol(); + if(decimalSymbol.isEmpty()) + decimalSymbol = "."; + + // If text contains no 'monetaryDecimalSymbol' then add it + // followed by the required number of 0s + if (!s.isEmpty()) { + if(m_prec > 0) { + if (!s.contains(decimalSymbol)) { + s += decimalSymbol; + for (int i=0; i < m_prec; i++) + s += "0"; + } + } else if(m_prec == 0) { + while(s.contains(decimalSymbol)) { + int pos = s.findRev(decimalSymbol); + if(pos != -1) { + s.truncate(pos); + } + } + } else if(s.contains(decimalSymbol)) { // m_prec == -1 && fraction + // no trailing zeroes + while(s.endsWith("0")) { + s.truncate(s.length()-1); + } + // no trailing decimalSymbol + if(s.endsWith(decimalSymbol)) + s.truncate(s.length()-1); + } + } +} + +bool kMyMoneyEdit::eventFilter(QObject * /* o */ , QEvent *e ) +{ + bool rc = false; + + // we want to catch some keys that are usually handled by + // the base class (e.g. '+', '-', etc.) + if(e->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast<QKeyEvent *> (e); + + rc = true; + switch(k->key()) { + case Qt::Key_Plus: + case Qt::Key_Minus: + if(m_edit->hasSelectedText()) { + m_edit->cut(); + } + if(m_edit->text().length() == 0) { + rc = false; + break; + } + // in case of '-' we do not enter the calculator when + // the current position is the beginning and there is + // no '-' sign at the first position. + if(k->key() == Qt::Key_Minus) { + if(m_edit->cursorPosition() == 0 && m_edit->text()[0] != '-') { + rc = false; + break; + } + } + // otherwise, tricky fall through here! + + case Qt::Key_Slash: + case Qt::Key_Asterisk: + case Qt::Key_Percent: + if(m_edit->hasSelectedText()) { + // remove the selected text + m_edit->cut(); + } + calculatorOpen(k); + break; + + default: + rc = false; + break; + } + + } else if(e->type() == QEvent::FocusOut) { + if(!m_edit->text().isEmpty() || !allowEmpty) + ensureFractionalPart(); + + if(MyMoneyMoney(m_edit->text()) != MyMoneyMoney(m_text) + && !m_calculator->isVisible()) { + emit valueChanged(m_edit->text()); + } + m_text = m_edit->text(); + } + return rc; +} + +void kMyMoneyEdit::slotCalculatorOpen(void) +{ + calculatorOpen(0); +} + +void kMyMoneyEdit::calculatorOpen(QKeyEvent* k) +{ + m_calculator->setInitialValues(m_edit->text(), k); + + int h = m_calculatorFrame->height(); + int w = m_calculatorFrame->width(); + + // usually, the calculator widget is shown underneath the MoneyEdit widget + // if it does not fit on the screen, we show it above this widget + QPoint p = mapToGlobal(QPoint(0,0)); + if(p.y() + height() + h > QApplication::desktop()->height()) + p.setY(p.y() - h); + else + p.setY(p.y() + height()); + + // usually, it is shown left aligned. If it does not fit, we align it + // to the right edge of the widget + if(p.x() + w > QApplication::desktop()->width()) + p.setX(p.x() + width() - w); + + QRect r = m_calculator->geometry(); + r.moveTopLeft(p); + m_calculatorFrame->setGeometry(r); + m_calculatorFrame->show(); + m_calculator->setFocus(); +} + +void kMyMoneyEdit::slotCalculatorResult(void) +{ + QString result; + if(m_calculator != 0) { + m_calculatorFrame->hide(); + m_edit->setText(m_calculator->result()); + ensureFractionalPart(); + emit valueChanged(m_edit->text()); + m_text = m_edit->text(); + } +} + +QWidget* kMyMoneyEdit::focusWidget(void) const +{ + QWidget* w = m_edit; + while(w->focusProxy()) + w = w->focusProxy(); + return w; +} + +void kMyMoneyEdit::setCalculatorButtonVisible(const bool show) +{ + m_calcButton->setShown(show); +} + +void kMyMoneyEdit::setResetButtonVisible(const bool show) +{ + m_resetButton->setShown(show); +} + +void kMyMoneyEdit::setAllowEmpty(bool allowed) +{ + allowEmpty = allowed; +} + +bool kMyMoneyEdit::isCalculatorButtonVisible(void) const +{ + return m_calcButton->isVisible(); +} + +bool kMyMoneyEdit::isResetButtonVisible(void) const +{ + return m_resetButton->isVisible(); +} + +bool kMyMoneyEdit::isEmptyAllowed(void) const +{ + return allowEmpty; +} + +void kMyMoneyEdit::setHint(const QString& hint) const +{ + if(m_edit) + m_edit->setHint(hint); +} + +bool kMyMoneyEdit::isReadOnly(void) const +{ + if(m_edit) + return m_edit->isReadOnly(); + return false; +} + +void kMyMoneyEdit::setReadOnly(bool readOnly) +{ + // we use the QLineEdit::setReadOnly() method directly to avoid + // changing the background between readonly and read/write mode + // as it is done by the KLineEdit code. + if(m_edit) + m_edit->QLineEdit::setReadOnly(readOnly); +} + +#include "kmymoneyedit.moc" diff --git a/kmymoney2/widgets/kmymoneyedit.h b/kmymoney2/widgets/kmymoneyedit.h new file mode 100644 index 0000000..6415a0c --- /dev/null +++ b/kmymoney2/widgets/kmymoneyedit.h @@ -0,0 +1,243 @@ +/*************************************************************************** + kmymoneyedit.h + ------------------- + copyright : (C) 2000 by Michael Edwardes + email : mte@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 KMYMONEYEDIT_H +#define KMYMONEYEDIT_H + +#include "kdecompat.h" + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qhbox.h> +#include <qvalidator.h> + +class QVBox; +class QWidget; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klineedit.h> +class KPushButton; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneylineedit.h> +#include <kmymoney/mymoneysecurity.h> +class MyMoneyMoney; +class kMyMoneyCalculator; + +#if KDE_VERSION <= KDE_MAKE_VERSION(3,1,0) + #define KDoubleValidator QDoubleValidator +#endif + +/** + * This class is derived from KDoubleValidator and uses + * the monetary symbols instead of the numeric symbols. + * Also, it always accepts localized input. + * + * @author Thomas Baumgart + */ +class kMyMoneyMoneyValidator : public QDoubleValidator +{ + Q_OBJECT + +public: + /** + * Constuct a locale-aware KDoubleValidator with default range + * (whatever QDoubleValidator uses for that) and parent @p + * parent + */ + kMyMoneyMoneyValidator( QObject * parent, const char * name=0 ); + /** + * Constuct a locale-aware KDoubleValidator for range [@p bottom,@p + * top] and a precision of @p decimals decimals after the decimal + * point. + */ + kMyMoneyMoneyValidator( double bottom, double top, int decimals, + QObject * parent, const char * name=0 ); + /** + * Destructs the validator. + */ + virtual ~kMyMoneyMoneyValidator() {} + + /** Overloaded for internal reasons. The API is not affected. */ + virtual QValidator::State validate( QString & input, int & pos ) const; +}; + +/** + * This class represents a widget to enter monetary values. + * It has an edit field and a button to select a popup + * calculator. The result of the calculator (if used) is + * stored in the edit field. + * + * @author Michael Edwardes, Thomas Baumgart + */ +class kMyMoneyEdit : public QHBox +{ + Q_OBJECT + Q_PROPERTY(bool calculatorButtonVisibility READ isCalculatorButtonVisible WRITE setCalculatorButtonVisible); + Q_PROPERTY(bool resetButtonVisibility READ isResetButtonVisible WRITE setResetButtonVisible); + Q_PROPERTY(bool allowEmpty READ isEmptyAllowed WRITE setAllowEmpty); + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly ) + +private: + QString previousText; // keep track of what has been typed + QString m_text; // keep track of what was the original value + kMyMoneyCalculator* m_calculator; + QVBox* m_calculatorFrame; + kMyMoneyLineEdit* m_edit; + KPushButton* m_calcButton; + KPushButton* m_resetButton; + int m_prec; + bool calculatorButtonVisibility; + bool allowEmpty; + +private: + /** + * Internal helper function for value() and ensureFractionalPart(void). + */ + void ensureFractionalPart(QString& txt) const; + +protected: + /** + * This method ensures that the text version contains a + * fractional part. + */ + void ensureFractionalPart(void); + + /** + * This method opens the calculator and replays the key + * event pointed to by @p ev. If @p ev is 0, then no key + * event is replayed. + * + * @param ev pointer to QKeyEvent that started the calculator. + */ + void calculatorOpen(QKeyEvent* ev); + + /** + * Helper method for constructors. + */ + void init(void); + +protected slots: + void theTextChanged(const QString & text); + void slotCalculatorResult(void); + void slotCalculatorOpen(void); + +public: + kMyMoneyEdit(QWidget *parent=0, const char *name=0, const int prec = -2); + kMyMoneyEdit(const MyMoneySecurity& eq, QWidget *parent=0, const char *name=0); + ~kMyMoneyEdit(); + + /** + * @deprecated Use value() instead + */ + // MyMoneyMoney getMoneyValue(void) KDE_DEPRECATED; + + MyMoneyMoney value(void) const; + + void setValue(const MyMoneyMoney& value); + + bool isValid(void) const; + + virtual bool eventFilter(QObject * , QEvent * ); + + /** + * This method returns the value of the edit field in "numerator/denominator" format. + * If you want to get the text of the edit field, use lineedit()->text() instead. + */ + QString text(void) const { return value().toString(); }; + + void setMinimumWidth(int w) { m_edit->setMinimumWidth(w); }; + + /** + * Set the number of fractional digits that should be shown + * + * @param prec number of fractional digits. + * + * @note should be used prior to calling loadText() + * @sa precision + */ + void setPrecision(const int prec); + + /** + * return the number of fractional digits + * @sa setPrecision + */ + int precision(void) { return m_prec; }; + + QWidget* focusWidget(void) const; + + /** + * This method allows to modify the behavior of the widget + * such that it accepts an empty value (all blank) or not. + * The default is to not accept an emtpy input and to + * convert an empty field into 0.00 upon loss of focus. + * + * @param allowed if @a true, empty input is allowed, if @a false + * emtpy input will be converted to 0.00 + */ + void setAllowEmpty(bool allowed = true); + + /** Overloaded for internal reasons. The API is not affected. */ + void setValidator(const QValidator* v); + + bool isCalculatorButtonVisible(void) const; + + bool isResetButtonVisible(void) const; + + bool isEmptyAllowed(void) const; + + KLineEdit* lineedit(void) const; + + void setHint(const QString& hint) const; + + bool isReadOnly(void) const; + +public slots: + void loadText(const QString& text); + void resetText(void); + void clearText(void); + + void setText(const QString& txt) { setValue(MyMoneyMoney(txt)); }; + + /** + * This method allows to show/hide the calculator button of the widget. + * The parameter @p show controls the behavior. Default is to show the + * button. + * + * @param show if true, button is shown, if false it is hidden + */ + void setCalculatorButtonVisible(const bool show); + + void setResetButtonVisible(const bool show); + + void setReadOnly(bool readOnly); + +signals: // Signals + /** + * This signal is sent, when the focus leaves this widget and + * the amount has been changed by user during this session. + */ + void valueChanged(const QString& text); + + void textChanged(const QString& text); +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyforecastlistviewitem.cpp b/kmymoney2/widgets/kmymoneyforecastlistviewitem.cpp new file mode 100644 index 0000000..684997e --- /dev/null +++ b/kmymoney2/widgets/kmymoneyforecastlistviewitem.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + kmymoneylistviewitem - description + ------------------- + begin : Sun Nov 25 2007 + copyright : (C) 2007 by Alvaro Soliverez + email : asoliverez@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpalette.h> +#include <qpen.h> +#include <qcolor.h> +#include <qpainter.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyforecastlistviewitem.h" + +#include <kmymoney/kmymoneyglobalsettings.h> + +KMyMoneyForecastListViewItem::KMyMoneyForecastListViewItem (QListView* parent, QListViewItem* after, bool isNegative) : + KListViewItem(parent, after), + m_negative(isNegative) +{ +} + +KMyMoneyForecastListViewItem::~KMyMoneyForecastListViewItem() +{ +} + +void KMyMoneyForecastListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) +{ + QColorGroup _cg = cg; + QColor textColour; + if(m_negative == true) { + textColour = KMyMoneyGlobalSettings::listNegativeValueColor(); //if the item is marked is marked as negative, all columns will be painted negative + } else { + textColour = m_columnsColor[column]; //otherwise, respect the color for each column + } + _cg.setColor(QColorGroup::Text, textColour); + + KListViewItem::paintCell(p, _cg, column, width, alignment); +} + +void KMyMoneyForecastListViewItem::setNegative(bool isNegative) +{ + m_negative = isNegative; +} + +void KMyMoneyForecastListViewItem::setText( int column, const QString &text, const bool &negative) +{ + //if negative set the map to negative color according to KMyMoneySettings + if(negative) { + m_columnsColor[column] = KMyMoneyGlobalSettings::listNegativeValueColor(); + } else { + m_columnsColor[column] = QColorGroup::Text; + } + + KListViewItem::setText(column, text); +} diff --git a/kmymoney2/widgets/kmymoneyforecastlistviewitem.h b/kmymoney2/widgets/kmymoneyforecastlistviewitem.h new file mode 100644 index 0000000..7e48945 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyforecastlistviewitem.h @@ -0,0 +1,65 @@ +/*************************************************************************** + kmymoneyforecastlistviewitem.h + ------------------- + begin : Sun Nov 25 2007 + copyright : (C) 2007 by Alvaro Soliverez + email : asoliverez@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 KMYMONEYFORECASTLISTVIEWITEM_H +#define KMYMONEYFORECASTLISTVIEWITEM_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + * This class implements a derived version of a KListViewItem that + * allows printing negative numbers in red + * + * @author Alvaro Soliverez + */ +class KMyMoneyForecastListViewItem : public KListViewItem +{ +public: + + KMyMoneyForecastListViewItem(QListView* parent, QListViewItem* after, bool isNegative); + + ~KMyMoneyForecastListViewItem(); + + void setNegative(bool isNegative); + + void setText( int column, const QString &text, const bool &negative = false ); + + /** + * use my own paint method + */ + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); + + +private: + + bool m_negative; + + QMap<int, QColor> m_columnsColor; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneygpgconfig.cpp b/kmymoney2/widgets/kmymoneygpgconfig.cpp new file mode 100644 index 0000000..f168e69 --- /dev/null +++ b/kmymoney2/widgets/kmymoneygpgconfig.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** + kmymoneyonlinequoteconfig.cpp - description + ------------------- + begin : Thu Dec 30 2004 + copyright : (C) 2004 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qcheckbox.h> +#include <qgroupbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> +#include <klineedit.h> +#include <kled.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneygpgconfig.h" +#include "libkgpgfile/kgpgfile.h" + +#define RECOVER_KEY_ID "0xD2B08440" + +kMyMoneyGPGConfig::kMyMoneyGPGConfig(QWidget *parent, const char *name ) + : kMyMoneyGPGConfigDecl(parent, name), + m_checkCount(0) +{ + m_idGroup->setEnabled(KGPGFile::GPGAvailable()); + m_recoveryGroup->setEnabled(KGPGFile::keyAvailable(RECOVER_KEY_ID)); + + m_userKeyFound->off(); + m_recoverKeyFound->off(); + + connect(m_useEncryption, SIGNAL(toggled(bool)), this, SLOT(slotStatusChanged(bool))); + connect(m_userId, SIGNAL(textChanged(const QString&)), this, SLOT(slotIdChanged(const QString&))); +} + +void kMyMoneyGPGConfig::resetConfig(void) +{ + m_useEncryption->setChecked(m_resetUseEncryption); + m_userId->setText(m_resetUserId); + m_recover->setChecked(m_resetRecover); + slotStatusChanged(m_resetUseEncryption); + writeConfig(); +} + +void kMyMoneyGPGConfig::readConfig(void) +{ + KConfig *config = KGlobal::config(); + config->setGroup("General Options"); + m_resetUseEncryption = config->readBoolEntry("WriteDataEncrypted", false); + m_resetRecover = config->readBoolEntry("EncryptRecover", false); + m_resetUserId = config->readEntry("GPG-Recipient"); + + resetConfig(); +} + +void kMyMoneyGPGConfig::writeConfig(void) +{ + KConfig *config = KGlobal::config(); + config->setGroup("General Options"); + config->writeEntry("WriteDataEncrypted", m_useEncryption->isChecked()); + config->writeEntry("EncryptRecover", m_recover->isChecked()); + config->writeEntry("GPG-Recipient", m_userId->text()); +} + +void kMyMoneyGPGConfig::slotIdChanged(const QString& /*txt*/) +{ + // this looks a bit awkward. Here's why: KGPGFile::keyAvailable() starts + // an external task and processes UI events while it waits for the external + // process to finish. Thus, the first time we get here, the external process + // is started and the user my press a second key which calls this routine + // again. + // + // The second invocation is counted, but the check is not started until the + // first one finishes. Once the external process finishes, we check if we + // were called in the meantime and restart the check. + if(++m_checkCount == 1) { + while(1) { + if(m_userId->text().stripWhiteSpace().length() > 0) { + m_userKeyFound->setState((KLed::State) (KGPGFile::keyAvailable(m_userId->text()) ? KLed::On : KLed::Off)); + if(m_checkCount > 1) { + m_checkCount = 1; + continue; + } + } else { + m_userKeyFound->setState(KLed::Off); + } + break; + } + --m_checkCount; + } +} + +void kMyMoneyGPGConfig::slotStatusChanged(bool state) +{ + if(state) { + m_idGroup->setEnabled(KGPGFile::GPGAvailable()); + m_recoveryGroup->setEnabled(KGPGFile::GPGAvailable()); + m_recoverKeyFound->setState((KLed::State) (KGPGFile::keyAvailable(RECOVER_KEY_ID) ? KLed::On : KLed::Off)); + if(m_userId->text().isEmpty()) + m_userKeyFound->setState(KLed::Off); + else + m_userKeyFound->setState((KLed::State) (KGPGFile::keyAvailable(m_userId->text()) ? KLed::On : KLed::Off)); + } else { + m_idGroup->setEnabled(false); + m_recoveryGroup->setEnabled(false); + m_recoverKeyFound->setState(KLed::Off); + m_userKeyFound->setState(KLed::Off); + } +} + +#include "kmymoneygpgconfig.moc" diff --git a/kmymoney2/widgets/kmymoneygpgconfig.h b/kmymoney2/widgets/kmymoneygpgconfig.h new file mode 100644 index 0000000..9d2bdc7 --- /dev/null +++ b/kmymoney2/widgets/kmymoneygpgconfig.h @@ -0,0 +1,62 @@ +/*************************************************************************** + kmymoneygpgconfig.h - description + ------------------- + begin : Mon Jan 3 2005 + copyright : (C) 2005 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYGPGCONFIG_H +#define KMYMONEYGPGCONFIG_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../widgets/kmymoneygpgconfigdecl.h" + +/** + * @author Thomas Baumgart + */ + +/** + * This class provides the necessary user interface to + * setup the parameters required for data encryption + */ +class kMyMoneyGPGConfig : public kMyMoneyGPGConfigDecl +{ + Q_OBJECT +public: + kMyMoneyGPGConfig(QWidget* parent, const char *name); + virtual ~kMyMoneyGPGConfig() {} + + void writeConfig(void); + void readConfig(void); + void resetConfig(void); + +protected slots: + void slotIdChanged(const QString& txt); + void slotStatusChanged(bool); + +private: + QString m_resetUserId; + bool m_resetUseEncryption; + bool m_resetRecover; + int m_checkCount; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneygpgconfigdecl.ui b/kmymoney2/widgets/kmymoneygpgconfigdecl.ui new file mode 100644 index 0000000..4aa12eb --- /dev/null +++ b/kmymoney2/widgets/kmymoneygpgconfigdecl.ui @@ -0,0 +1,197 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>kMyMoneyGPGConfigDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyGPGConfigDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>490</height> + </rect> + </property> + <property name="caption"> + <string>GPG encryption settings</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This page allows you to set the parameters for encrypted file storage of your <b>KMyMoney</b> data based on <b>GPG</b>.<p> +Acces to the settings is disabled if <b>GPG</b> could not be detected on your system. In this case, please make sure that <b>GPG</b> is working properly for the current user.<p> +The <i>Recovery encryption</i> group is only accessible, if the necessary key for <b>kmymoney-recover@users.sourceforge.net</b> with id 0x8AFDDC8E is found.</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_useEncryption</cstring> + </property> + <property name="text"> + <string>Use GPG encryption</string> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>m_idGroup</cstring> + </property> + <property name="title"> + <string>User identification</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Enter the id of the key you want to use for data encryption. This can either be an e-mail address or the hexadecimal key id. In case of the key id don't forget the leading <i>0x</i>.</string> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>User ID</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>m_userId</cstring> + </property> + </widget> + <widget class="KLed"> + <property name="name"> + <cstring>m_userKeyFound</cstring> + </property> + <property name="shape"> + <enum>Circular</enum> + </property> + <property name="look"> + <enum>Sunken</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>This symbol denotes, if the key for the given user id has been found in your keyring. It is green when found, dark otherwise.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>m_recoveryGroup</cstring> + </property> + <property name="title"> + <string>Recovery encryption</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_recover</cstring> + </property> + <property name="text"> + <string>Also encrypt with kmymoney-recover key</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>51</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KLed"> + <property name="name"> + <cstring>m_recoverKeyFound</cstring> + </property> + <property name="shape"> + <enum>Circular</enum> + </property> + <property name="look"> + <enum>Sunken</enum> + </property> + <property name="whatsThis" stdset="0"> + <string>This symbol denotes, if the KMyMoney recovery key has been found in your keyring. It is green when found, dark otherwise.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>You can specify to encrypt the data also with the KMyMoney recover key. Only the core KMyMoney developers are in posession of the respective private key required to read back such encrypted data.<p> + +This mechanism is provided for the case that you have lost your key and cannot access your data anymore. With this option activated, the KMyMoney developers can decrypt the data and supply you with it in a readable form. Please be prepared, that you have to answer a few detailed questions about the contents of your data before we will send it out.</string> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneylineedit.cpp b/kmymoney2/widgets/kmymoneylineedit.cpp new file mode 100644 index 0000000..18e2e31 --- /dev/null +++ b/kmymoney2/widgets/kmymoneylineedit.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** + kmymoneylineedit.cpp - description + ------------------- + begin : Wed May 9 2001 + copyright : (C) 2001 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@ctv.es> + Felix Rodriguez <frodriguez@mail.wesleyan.edu> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qrect.h> +#include <qpainter.h> +#include <qpalette.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneylineedit.h" + +kMyMoneyLineEdit::kMyMoneyLineEdit(QWidget *w, const char* name, bool forceMonetaryDecimalSymbol, int alignment) : + KLineEdit(w, name), + m_forceMonetaryDecimalSymbol(forceMonetaryDecimalSymbol) +{ + setAlignment(alignment); +} + +kMyMoneyLineEdit::~kMyMoneyLineEdit() +{ +} + +void kMyMoneyLineEdit::resetText(void) +{ + setText(m_text); +} + +void kMyMoneyLineEdit::loadText(const QString& text) +{ + m_text = text; + setText(text); +} + +void kMyMoneyLineEdit::focusOutEvent(QFocusEvent *ev) +{ + // if the current text is not in the list of + // possible completions, we have a new payee + // and signal that to the outside world. + if(text() != m_text) { + emit lineChanged(text()); + } + KLineEdit::focusOutEvent(ev); + + // force update of hint + if(text().isEmpty()) + repaint(); +} + +void kMyMoneyLineEdit::keyReleaseEvent(QKeyEvent* k) +{ + if(m_forceMonetaryDecimalSymbol) { + if(k->state() & Qt::Keypad) { + if(k->key() == Qt::Key_Comma + || k->key() == Qt::Key_Period) { + if(KGlobal::locale()->monetaryDecimalSymbol() == ",") { + QKeyEvent newk(k->type(), Qt::Key_Comma, ',', k->state(), ",", k->isAutoRepeat(), k->count()); + KLineEdit::keyReleaseEvent(&newk); + k->ignore(); + return; + } + + if(KGlobal::locale()->monetaryDecimalSymbol() == ".") { + QKeyEvent newk(k->type(), Qt::Key_Comma, ',', k->state(), ".", k->isAutoRepeat(), k->count()); + KLineEdit::keyReleaseEvent(&newk); + k->ignore(); + return; + } + } + } + } + KLineEdit::keyReleaseEvent(k); +} + +void kMyMoneyLineEdit::keyPressEvent(QKeyEvent* k) +{ + if(m_forceMonetaryDecimalSymbol) { + if(k->state() & Qt::Keypad) { + if(k->key() == Qt::Key_Comma + || k->key() == Qt::Key_Period) { + if(KGlobal::locale()->monetaryDecimalSymbol() == ",") { + QKeyEvent newk(k->type(), Qt::Key_Comma, ',', k->state(), ",", k->isAutoRepeat(), k->count()); + KLineEdit::keyPressEvent(&newk); + k->ignore(); + return; + } + + if(KGlobal::locale()->monetaryDecimalSymbol() == ".") { + QKeyEvent newk(k->type(), Qt::Key_Period, '.', k->state(), ".", k->isAutoRepeat(), k->count()); + KLineEdit::keyPressEvent(&newk); + k->ignore(); + return; + } + } + } + } + KLineEdit::keyPressEvent(k); +} + +void kMyMoneyLineEdit::drawContents( QPainter *p) +{ + KLineEdit::drawContents(p); + + if(text().isEmpty() && !m_hint.isEmpty() && !hasFocus()) { + const int innerMargin = 1; + + // the following 5 lines are taken from QLineEdit::drawContents() + QRect cr = contentsRect(); + QFontMetrics fm = fontMetrics(); + QRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2, + cr.width() - 2*innerMargin, fm.height() ); + QPoint topLeft = lineRect.topLeft() - QPoint(0, -fm.ascent()); + + p->save(); + QFont f = p->font(); + f.setItalic(true); + f.setWeight(QFont::Light); + p->setFont(f); + p->setPen(palette().disabled().text()); + + p->drawText(topLeft, m_hint); + + p->restore(); + } +} + +#include "kmymoneylineedit.moc" diff --git a/kmymoney2/widgets/kmymoneylineedit.h b/kmymoney2/widgets/kmymoneylineedit.h new file mode 100644 index 0000000..2351082 --- /dev/null +++ b/kmymoney2/widgets/kmymoneylineedit.h @@ -0,0 +1,135 @@ +/*************************************************************************** + kmymoneylineedit.h - description + ------------------- + begin : Wed May 9 2001 + copyright : (C) 2001 by Michael Edwardes + 2006 by Thomas Baumgart + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@ctv.es> + Felix Rodriguez <frodriguez@mail.wesleyan.edu> + Thomas Baumgart <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 KMYMONEYLINEEDIT_H +#define KMYMONEYLINEEDIT_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klineedit.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + * This class represents a special verson of a KLineEdit object that + * supports the display of a hint if the display area is empty. It also + * overrides the comma key on the numeric keypad with the currently + * selected monetaryDecimalSymbol if selected during creation of the object. + * + * @author Michael Edwardes + * @author Thomas Baumgart + */ +class kMyMoneyLineEdit : public KLineEdit +{ + Q_OBJECT +public: + /** + * @param w pointer to parent + * @param name pointer to name of object + * @param forceMonetaryDecimalSymbol if @a true, the numeric keypad comma key will have a fixed + * value and does not follow the keyboard layout (default: @p false) + * @param alignment Controls the alignment of the text. Default is Qt::AlignLeft | Qt::AlignVCenter. + * See Qt::AlignmentFlags for other possible values. + */ + kMyMoneyLineEdit(QWidget *w = 0, const char* name = 0, bool forceMonetaryDecimalSymbol = false, int alignment = (AlignLeft | AlignVCenter)); + ~kMyMoneyLineEdit(); + + /** + * This method is used to set the value of the widget back to + * the one passed using loadText(). + */ + void resetText(void); + + /** + * This method is used to turn on/off the hint display + */ + void setHint(const QString& hint) { m_hint = hint; }; + + +public slots: + void loadText(const QString& text); + +signals: + /** + * This signal is emitted when the focus leaves the object and the text + * has been changed. The new text is passed as @a str. + */ + void lineChanged(const QString& str); + +protected: + void focusOutEvent(QFocusEvent *ev); + + /** reimplemented to support the hint display */ + void drawContents( QPainter *); + + /** + * Overridden so that the period key on the numeric keypad always sends + * out the currently selected monetary decimal symbol instead of the + * key defined by the keymap. + * + * Example: If you have set the keymap (keyboard layout) as English, then + * the numeric keypad will send a period but if you have set the keymap to + * be German, the same key will send a comma. + * + * @param ev pointer to current QKeyEvent + */ + void keyPressEvent(QKeyEvent* ev); + + /** + * Overridden so that the period key on the numeric keypad always sends + * out the currently selected monetary decimal symbol instead of the + * key defined by the keymap. + * + * Example: If you have set the keymap (keyboard layout) as English, then + * the numeric keypad will send a period but if you have set the keymap to + * be German, the same key will send a comma. + * + * @param ev pointer to current QKeyEvent + */ + void keyReleaseEvent(QKeyEvent* ev); + +private: + /** + * This member keeps the initial value. It is used during + * resetText() to set the widgets text back to this initial value + * and as comparison during focusOutEvent() to emit the lineChanged + * signal if the current text is different. + */ + QString m_text; + + /** + * This member tells what to display as hint as long as the field is empty + */ + QString m_hint; + + /** + * This member keeps the status if overriding the numeric keypad comma key + * is requested or not. + */ + bool m_forceMonetaryDecimalSymbol; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneylistviewitem.cpp b/kmymoney2/widgets/kmymoneylistviewitem.cpp new file mode 100644 index 0000000..26367ad --- /dev/null +++ b/kmymoney2/widgets/kmymoneylistviewitem.cpp @@ -0,0 +1,135 @@ +/*************************************************************************** + kmymoneylistviewitem - description + ------------------- + begin : Wed Jun 28 2006 + copyright : (C) 2000-2006 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpalette.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneylistviewitem.h" +#include "kmymoneychecklistitem.h" +#include "../kmymoneyglobalsettings.h" + +KMyMoneyListViewItem::KMyMoneyListViewItem(QListView* parent, const QString& txt, const QString& key, const QString& id) : + KListViewItem(parent, txt), + m_key(key), + m_id(id), + m_isOdd(0), + m_isKnown(0) +{ + if(key.isEmpty()) + m_key = txt; +} + +KMyMoneyListViewItem::KMyMoneyListViewItem(QListViewItem* parent, const QString& txt, const QString& key, const QString& id) : + KListViewItem(parent, txt), + m_key(key), + m_id(id), + m_isOdd(0), + m_isKnown(0) +{ + if(key.isEmpty()) + m_key = txt; +} + +KMyMoneyListViewItem::~KMyMoneyListViewItem() +{ +} + +QString KMyMoneyListViewItem::key(int column, bool ascending) const +{ + Q_UNUSED(ascending); + + if(column == 0) + return m_key[0] + text(0); + return m_key.mid(1); +} + + +void KMyMoneyListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) +{ + QColorGroup _cg = cg; + _cg.setColor(QColorGroup::Base, backgroundColor()); + + // make sure to bypass KListViewItem::paintCell() as + // we don't like it's logic - that's why we do this + // here ;-) (ipwizard) + QListViewItem::paintCell(p, _cg, column, width, alignment); +} + +const QColor KMyMoneyListViewItem::backgroundColor() +{ + return isAlternate() ? KMyMoneyGlobalSettings::listBGColor() : KMyMoneyGlobalSettings::listColor(); +} + +bool KMyMoneyListViewItem::isAlternate(void) +{ +// logic taken from KListViewItem::isAlternate() + KMyMoneyCheckListItem* ciAbove; + KMyMoneyListViewItem* liAbove; + ciAbove = dynamic_cast<KMyMoneyCheckListItem*> (itemAbove()); + liAbove = dynamic_cast<KMyMoneyListViewItem*> (itemAbove()); + + m_isKnown = ciAbove ? ciAbove->m_isKnown : (liAbove ? liAbove->m_isKnown : true); + if(m_isKnown) { + m_isOdd = ciAbove ? !ciAbove->m_isOdd : (liAbove ? !liAbove->m_isOdd : false); + } else { + KMyMoneyCheckListItem* clItem; + KMyMoneyListViewItem* liItem; + bool previous = true; + if(QListViewItem::parent()) { + clItem = dynamic_cast<KMyMoneyCheckListItem *>(QListViewItem::parent()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(QListViewItem::parent()); + if(clItem) + previous = clItem->m_isOdd; + else + previous = liItem->m_isOdd; + clItem = dynamic_cast<KMyMoneyCheckListItem *>(QListViewItem::parent()->firstChild()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(QListViewItem::parent()->firstChild()); + } else { + clItem = dynamic_cast<KMyMoneyCheckListItem *>(listView()->firstChild()); + liItem = dynamic_cast<KMyMoneyListViewItem*>(listView()->firstChild()); + } + while(clItem || liItem) { + if(clItem) { + clItem->m_isOdd = previous = !previous; + clItem->m_isKnown = true; + liItem = dynamic_cast<KMyMoneyListViewItem *>(clItem->nextSibling()); + clItem = dynamic_cast<KMyMoneyCheckListItem *>(clItem->nextSibling()); + } else if(liItem) { + liItem->m_isOdd = previous = !previous; + liItem->m_isKnown = true; + clItem = dynamic_cast<KMyMoneyCheckListItem *>(liItem->nextSibling()); + liItem = dynamic_cast<KMyMoneyListViewItem *>(liItem->nextSibling()); + } + } + } + return m_isOdd; +} + +#include "kmymoneylistviewitem.moc" diff --git a/kmymoney2/widgets/kmymoneylistviewitem.h b/kmymoney2/widgets/kmymoneylistviewitem.h new file mode 100644 index 0000000..64f7562 --- /dev/null +++ b/kmymoney2/widgets/kmymoneylistviewitem.h @@ -0,0 +1,88 @@ +/*************************************************************************** + kmymoneylistviewitem.h + ------------------- + begin : Wed Jun 28 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYLISTVIEWITEM_H +#define KMYMONEYLISTVIEWITEM_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +class KMyMoneyCheckListItem; + +/** + * This class implements a derived version of a QListViewItem that + * allows the storage of an engine object id with the object + * + * @author Thomas Baumgart + */ +class KMyMoneyListViewItem : public QObject, public KListViewItem +{ + friend class KMyMoneyCheckListItem; + + Q_OBJECT +public: + KMyMoneyListViewItem(QListView *parent, const QString& txt, const QString& key, const QString& id); + KMyMoneyListViewItem(QListViewItem *parent, const QString& txt, const QString& key, const QString& id); + ~KMyMoneyListViewItem(); + + const QString& id(void) const { return m_id; }; + + /** + * use my own paint method + */ + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); + + /** + * use my own backgroundColor method + */ + const QColor backgroundColor(); + + /** + * This method returns a const reference to the key passed to the constructor. The column + * defines what is returned: For @a column equals 0, the first character passed as @a key to + * the constructor concatenated with the value returned by text(0) is returned. For @a column + * equals to 1, the @a key as passed to the constructor except the first character is returned. + */ + QString key(int column, bool ascending) const; + + + /** + * Reimplemented for internal reasons + */ + bool isAlternate(void); + +private: + QString m_key; + QString m_id; + // copied from KListViewItem() + unsigned int m_isOdd : 1; + unsigned int m_isKnown : 1; + unsigned int m_unused : 30; + +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyonlinequoteconfig.cpp b/kmymoney2/widgets/kmymoneyonlinequoteconfig.cpp new file mode 100644 index 0000000..125cc13 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyonlinequoteconfig.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + kmymoneyonlinequoteconfig.cpp - description + ------------------- + begin : Thu Dec 30 2004 + copyright : (C) 2004 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qregexp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> +#include <klistview.h> +#include <kiconloader.h> +#include <kguiitem.h> +#include <kpushbutton.h> +#include <klineedit.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneyonlinequoteconfig.h" +#include "../converter/webpricequote.h" + +kMyMoneyOnlineQuoteConfig::kMyMoneyOnlineQuoteConfig(QWidget *parent, const char *name ) + : kMyMoneyOnlineQuoteConfigDecl(parent, name) +{ +#if 1 + QStringList groups = WebPriceQuote::quoteSources(); + + loadList(true /*updateResetList*/); + + m_updateButton->setEnabled(false); + + KIconLoader* il = KGlobal::iconLoader(); + KGuiItem updateButtenItem( i18n("&Update" ), + QIconSet(il->loadIcon("button_ok", KIcon::Small, KIcon::SizeSmall)), + i18n("Accepts the entered data and stores it"), + i18n("Use this to accept the modified data.")); + m_updateButton->setGuiItem(updateButtenItem); + + KGuiItem deleteButtenItem( i18n( "&Delete" ), + QIconSet(il->loadIcon("editdelete", KIcon::Small, KIcon::SizeSmall)), + i18n("Delete the selected source entry"), + i18n("Use this to delete the selected online source entry")); + m_deleteButton->setGuiItem(deleteButtenItem); + + KGuiItem newButtenItem( i18n( "&New..." ), + QIconSet(il->loadIcon("filenew", KIcon::Small, KIcon::SizeSmall)), + i18n("Create a new source entry for online quotes"), + i18n("Use this to create a new entry for online quotes")); + m_newButton->setGuiItem(newButtenItem); + + connect(m_updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateEntry())); + connect(m_newButton, SIGNAL(clicked()), this, SLOT(slotNewEntry())); + + connect(m_quoteSourceList, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotLoadWidgets(QListViewItem*))); + connect(m_quoteSourceList, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotLoadWidgets(QListViewItem*))); + connect(m_quoteSourceList, SIGNAL(itemRenamed(QListViewItem*,const QString&,int)), this, SLOT(slotEntryRenamed(QListViewItem*,const QString&,int))); + + connect(m_editURL, SIGNAL(textChanged(const QString&)), this, SLOT(slotEntryChanged())); + connect(m_editSymbol, SIGNAL(textChanged(const QString&)), this, SLOT(slotEntryChanged())); + connect(m_editDate, SIGNAL(textChanged(const QString&)), this, SLOT(slotEntryChanged())); + connect(m_editDateFormat, SIGNAL(textChanged(const QString&)), this, SLOT(slotEntryChanged())); + connect(m_editPrice, SIGNAL(textChanged(const QString&)), this, SLOT(slotEntryChanged())); + + // FIXME deleting a source is not yet implemented + m_deleteButton->setEnabled(false); +#endif +} + +void kMyMoneyOnlineQuoteConfig::loadList(const bool updateResetList) +{ + QStringList groups = WebPriceQuote::quoteSources(); + + if(updateResetList) + m_resetList.clear(); + m_quoteSourceList->clear(); + QStringList::Iterator it; + for(it = groups.begin(); it != groups.end(); ++it) { + new QListViewItem(m_quoteSourceList, *it); + if(updateResetList) + m_resetList += WebPriceQuoteSource(*it); + } + + QListViewItem* first = m_quoteSourceList->firstChild(); + if(first) + m_quoteSourceList->setSelected(first, true); + slotLoadWidgets(first); + + m_newButton->setEnabled(m_quoteSourceList->findItem(i18n("New Quote Source"), 0) == 0); +} + +void kMyMoneyOnlineQuoteConfig::resetConfig(void) +{ + QStringList::ConstIterator it; + QStringList groups = WebPriceQuote::quoteSources(); + + // delete all currently defined entries + for(it = groups.begin(); it != groups.end(); ++it) { + WebPriceQuoteSource(*it).remove(); + } + + // and write back the one's from the reset list + QValueList<WebPriceQuoteSource>::ConstIterator itr; + for(itr = m_resetList.begin(); itr != m_resetList.end(); ++itr) { + (*itr).write(); + } + + loadList(); +} + +void kMyMoneyOnlineQuoteConfig::slotLoadWidgets(QListViewItem* item) +{ + m_editURL->setEnabled(true); + m_editSymbol->setEnabled(true); + m_editPrice->setEnabled(true); + m_editDate->setEnabled(true); + m_editURL->setText(QString()); + m_editSymbol->setText(QString()); + m_editPrice->setText(QString()); + m_editDate->setText(QString()); + m_editDateFormat->setText(QString()); + + if(item) { + m_currentItem = WebPriceQuoteSource(item->text(0)); + m_editURL->setText(m_currentItem.m_url); + m_editSymbol->setText(m_currentItem.m_sym); + m_editPrice->setText(m_currentItem.m_price); + m_editDate->setText(m_currentItem.m_date); + m_editDateFormat->setText(m_currentItem.m_dateformat); + + } else { + m_editURL->setEnabled(false); + m_editSymbol->setEnabled(false); + m_editPrice->setEnabled(false); + m_editDate->setEnabled(false); + m_editDateFormat->setEnabled(false); + } + + m_updateButton->setEnabled(false); + +} + +void kMyMoneyOnlineQuoteConfig::slotEntryChanged(void) +{ + bool modified = m_editURL->text() != m_currentItem.m_url + || m_editSymbol->text() != m_currentItem.m_sym + || m_editDate->text() != m_currentItem.m_date + || m_editDateFormat->text() != m_currentItem.m_dateformat + || m_editPrice->text() != m_currentItem.m_price; + + m_updateButton->setEnabled(modified); +} + +void kMyMoneyOnlineQuoteConfig::slotUpdateEntry(void) +{ + m_currentItem.m_url = m_editURL->text(); + m_currentItem.m_sym = m_editSymbol->text(); + m_currentItem.m_date = m_editDate->text(); + m_currentItem.m_dateformat = m_editDateFormat->text(); + m_currentItem.m_price = m_editPrice->text(); + m_currentItem.write(); + slotEntryChanged(); +} + +void kMyMoneyOnlineQuoteConfig::slotNewEntry(void) +{ + WebPriceQuoteSource newSource(i18n("New Quote Source")); + newSource.write(); + loadList(); + QListViewItem* item = m_quoteSourceList->findItem(i18n("New Quote Source"), 0); + if(item) { + m_quoteSourceList->setSelected(item, true); + slotLoadWidgets(item); + } +} + +void kMyMoneyOnlineQuoteConfig::slotEntryRenamed(QListViewItem* item, const QString& text, int /* col */) +{ + int nameCount = 0; + QListViewItemIterator it(m_quoteSourceList); + while(it.current()) { + if(it.current()->text(0) == text) + ++nameCount; + ++it; + } + + // Make sure we get a non-empty and unique name + if(text.length() > 0 && nameCount == 1) { + m_currentItem.rename(text); + } else { + item->setText(0, m_currentItem.m_name); + } + m_newButton->setEnabled(m_quoteSourceList->findItem(i18n("New Quote Source"), 0) == 0); +} + +#include "kmymoneyonlinequoteconfig.moc" diff --git a/kmymoney2/widgets/kmymoneyonlinequoteconfig.h b/kmymoney2/widgets/kmymoneyonlinequoteconfig.h new file mode 100644 index 0000000..f00cbdb --- /dev/null +++ b/kmymoney2/widgets/kmymoneyonlinequoteconfig.h @@ -0,0 +1,59 @@ +/*************************************************************************** + kmymoneyonlinequoteconfig.h - description + ------------------- + begin : Thu Dec 30 2004 + copyright : (C) 2004 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYONLINEQUOTECONFIG_H +#define KMYMONEYONLINEQUOTECONFIG_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../widgets/kmymoneyonlinequoteconfigdecl.h" +#include "../converter/webpricequote.h" + +class kMyMoneyOnlineQuoteConfig : public kMyMoneyOnlineQuoteConfigDecl +{ + Q_OBJECT +public: + kMyMoneyOnlineQuoteConfig(QWidget* parent, const char *name); + virtual ~kMyMoneyOnlineQuoteConfig() {} + + void writeConfig(void) {} + void readConfig(void) {} + void resetConfig(void); + +protected slots: + void slotUpdateEntry(void); + void slotLoadWidgets(QListViewItem* item); + void slotEntryChanged(void); + void slotNewEntry(void); + void slotEntryRenamed(QListViewItem* item, const QString& text, int col); + +protected: + void loadList(const bool updateResetList = false); + +private: + QValueList<WebPriceQuoteSource> m_resetList; + WebPriceQuoteSource m_currentItem; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyonlinequoteconfigdecl.ui b/kmymoney2/widgets/kmymoneyonlinequoteconfigdecl.ui new file mode 100644 index 0000000..0131edd --- /dev/null +++ b/kmymoney2/widgets/kmymoneyonlinequoteconfigdecl.ui @@ -0,0 +1,231 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>kMyMoneyOnlineQuoteConfigDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyOnlineQuoteConfigDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>594</width> + <height>487</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>Online Quotes</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_quoteSourceList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="defaultRenameAction"> + <enum>Accept</enum> + </property> + <property name="itemsRenameable"> + <bool>true</bool> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupParsing</cstring> + </property> + <property name="title"> + <string>Details</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Date</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Symbol</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_editSymbol</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Regular Expression to extract the symbol from the downloaded data</string> + </property> + </widget> + <widget class="KLineEdit" row="2" column="1"> + <property name="name"> + <cstring>m_editPrice</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Regular Expression to extract the price from the downloaded data</string> + </property> + </widget> + <widget class="KLineEdit" row="4" column="1"> + <property name="name"> + <cstring>m_editDateFormat</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Regular Expression to extract the date from the downloaded data</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_editURL</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>URL to be used to download the quote</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Enter the URL from which stock quotes will be fetched. <b>%1</b> will be replaced with the symbol for the security being quoted. For currency conversions, <b>%2</b> will be replaced with the currency to be quoted and <b>%1</b> with the currency the quote is based on.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Price</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1"> + <property name="name"> + <cstring>m_editDate</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Regular Expression to extract the date from the downloaded data</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>Date Format</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>URL</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string><i>Enter regular expressions which can be used to parse the data returned from the URL entered above. The symbol, price, and date must be found in the quote data to be usable. You may also try the KMyMoney user's mailinglist at <a href="mailto:kmymoney2-user@lists.sourceforge.net">kmymoney2-user@lists.sourceforge.net</a> to find what settings work for other users in your country.</i></string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_newButton</cstring> + </property> + <property name="text"> + <string>New</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_deleteButton</cstring> + </property> + <property name="text"> + <string>Delete</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>240</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_updateButton</cstring> + </property> + <property name="text"> + <string>Update</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneypriceview.cpp b/kmymoney2/widgets/kmymoneypriceview.cpp new file mode 100644 index 0000000..1b884d2 --- /dev/null +++ b/kmymoney2/widgets/kmymoneypriceview.cpp @@ -0,0 +1,343 @@ +/*************************************************************************** + kmymoneypriceview.cpp - description + ------------------- + begin : Wed Mar 24 2004 + copyright : (C) 2000-2004 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qheader.h> +#include <qcursor.h> +#include <qtimer.h> +#include <qcheckbox.h> +#include <qgroupbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kglobal.h> +#include <klistview.h> +#include <kiconloader.h> +#include <kconfig.h> +#include <kmessagebox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneypriceview.h" +#include <kmymoney/mymoneysecurity.h> +#include <kmymoney/mymoneyfile.h> +#include "../kmymoneyglobalsettings.h" +#if 0 +#include "../widgets/kmymoneycurrencyselector.h" +#include "../dialogs/kupdatestockpricedlg.h" +#include "../dialogs/kcurrencycalculator.h" +#include "../dialogs/kequitypriceupdatedlg.h" +#include "../kmymoneyutils.h" +#include "../mymoney/mymoneyfile.h" +#endif + +#define COMMODITY_COL 0 +#define CURRENCY_COL 1 +#define DATE_COL 2 +#define PRICE_COL 3 +#define SOURCE_COL 4 + +KMyMoneyPriceItem::KMyMoneyPriceItem(KListView *view, const MyMoneyPrice& pr) : + KMyMoneyListViewItem(view, QString(), QString(), QString()), + m_pr(pr) +{ + MyMoneySecurity from, to; + KConfig *kconfig = KGlobal::config(); + kconfig->setGroup("General Options"); + int prec = kconfig->readNumEntry("PricePrecision", 4); + + if(!m_pr.isValid()) + m_pr = MyMoneyFile::instance()->price(m_pr.from(), m_pr.to(), m_pr.date()); + + if(m_pr.isValid()) { + QString priceBase = m_pr.to(); + from = MyMoneyFile::instance()->security(m_pr.from()); + to = MyMoneyFile::instance()->security(m_pr.to()); + if(!to.isCurrency()) { + from = MyMoneyFile::instance()->security(m_pr.to()); + to = MyMoneyFile::instance()->security(m_pr.from()); + priceBase = m_pr.from(); + } + + setText(COMMODITY_COL, (from.isCurrency()) ? from.id() : from.tradingSymbol()); + setText(CURRENCY_COL, to.id()); + setText(DATE_COL, KGlobal::locale()->formatDate(m_pr.date(), true)); + setText(PRICE_COL, m_pr.rate(priceBase).formatMoney("", prec)); + setText(SOURCE_COL, m_pr.source()); + } +} + +int KMyMoneyPriceItem::compare(QListViewItem* i, int col, bool ascending) const +{ + KMyMoneyPriceItem* item = static_cast<KMyMoneyPriceItem*>(i); + int rc = 0; + + switch(col) { + case DATE_COL: // date + if(m_pr.date() > item->m_pr.date()) + rc = 1; + else if(m_pr.date() < item->m_pr.date()) + rc = -1; + break; + + case PRICE_COL: // value + if(m_pr.rate(QString()) > item->m_pr.rate(QString())) + rc = 1; + else if(m_pr.rate(QString()) < item->m_pr.rate(QString())) + rc = -1; + break; + + default: + rc = QListViewItem::compare(i, col, ascending); + break; + } + return rc; +} + +KMyMoneyPriceView::KMyMoneyPriceView(QWidget *parent, const char *name ) : + KListView(parent,name), + m_contextMenu(0), + m_showAll(false) +{ + addColumn(i18n("Commodity")); + addColumn(i18n("Currency")); + addColumn(i18n("Date")); + addColumn(i18n("Price")); + addColumn(i18n("Source")); + setAllColumnsShowFocus(true); + setMultiSelection(false); + setColumnWidthMode(0, QListView::Maximum); + setColumnWidthMode(1, QListView::Maximum); + setShowSortIndicator(true); + setSorting(COMMODITY_COL); + + header()->setFont(KMyMoneyGlobalSettings::listHeaderFont()); + + KIconLoader *kiconloader = KGlobal::iconLoader(); + + m_contextMenu = new KPopupMenu(this); + m_contextMenu->insertTitle(i18n("Price Options")); + m_contextMenu->insertItem(kiconloader->loadIcon("filenew", KIcon::Small), + i18n("New..."), + this, SIGNAL(newPrice())); + + m_contextMenu->insertItem(kiconloader->loadIcon("edit", KIcon::Small), + i18n("Edit..."), + this, SIGNAL(editPrice())); + + m_contextMenu->insertItem(kiconloader->loadIcon("connect_creating", KIcon::Small), + i18n("Online Price Update..."), + this, SIGNAL(onlinePriceUpdate())); + + m_contextMenu->insertItem(kiconloader->loadIcon("delete", KIcon::Small), + i18n("Delete..."), + this, SIGNAL(deletePrice())); + + connect(this, SIGNAL(rightButtonClicked(QListViewItem* , const QPoint&, int)), + this, SLOT(slotListClicked(QListViewItem*, const QPoint&, int))); + + // connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotReloadWidget())); + + // slotReloadWidget(); + + // If the widget is shown, the size must be fixed a little later + // to be appropriate. I saw this in some other places and the only + // way to solve this problem is to postpone the setup of the size + // to the time when the widget is on the screen. + resize(width()-1, height()-1); + QTimer::singleShot(50, this, SLOT(slotTimerDone())); +} + +KMyMoneyPriceView::~KMyMoneyPriceView() +{ +} + +void KMyMoneyPriceView::slotTimerDone(void) +{ + // the resize operation does the trick to adjust + // all widgets in the view to the size they should + // have and show up correctly. Don't ask me, why + // this is, but it cured the problem (ipwizard). + resize(width()+1, height()+1); +} + +#if 0 +void KMyMoneyPriceView::slotReloadWidget(void) +{ + m_priceHistory->clear(); + + MyMoneyPriceList list = MyMoneyFile::instance()->priceList(); + MyMoneyPriceList::ConstIterator it_l; + for(it_l = list.begin(); it_l != list.end(); ++it_l) { + MyMoneyPriceEntries::ConstIterator it_e; + if(m_showAll) { + for(it_e = (*it_l).begin(); it_e != (*it_l).end(); ++it_e) { + new kMyMoneyPriceItem(m_priceHistory, *it_e); + } + } else { + if((*it_l).count() > 0) { + it_e = (*it_l).end(); + --it_e; + new kMyMoneyPriceItem(m_priceHistory, *it_e); + } + } + } +} +#endif + +void KMyMoneyPriceView::resizeEvent(QResizeEvent* e) +{ + int w = visibleWidth()/5; + + setColumnWidth(0, w); + setColumnWidth(1, w); + setColumnWidth(2, w); + setColumnWidth(3, w); + setColumnWidth(4, w); + resizeContents(visibleWidth(), contentsHeight()); + + KListView::resizeEvent(e); +} + +void KMyMoneyPriceView::slotListClicked(QListViewItem* item, const QPoint&, int) +{ + int editId = m_contextMenu->idAt(2); + int updateId = m_contextMenu->idAt(3); + int delId = m_contextMenu->idAt(4); + + m_contextMenu->setItemEnabled(editId, item != 0); + m_contextMenu->setItemEnabled(delId, item != 0); + + KMyMoneyPriceItem* priceitem = dynamic_cast<KMyMoneyPriceItem*>(item); + if(priceitem) { + MyMoneySecurity security; + security = MyMoneyFile::instance()->security(priceitem->price().from()); + m_contextMenu->setItemEnabled(updateId, security.isCurrency() ); + + // Modification of automatically added entries is not allowed + if(priceitem->price().source() == "KMyMoney") { + m_contextMenu->setItemEnabled(editId, false); + m_contextMenu->setItemEnabled(updateId, false); + m_contextMenu->setItemEnabled(delId, false); + } + } + else + m_contextMenu->setItemEnabled(updateId, false ); + + m_contextMenu->exec(QCursor::pos()); +} + +#if 0 +void KMyMoneyPriceView::slotNewPrice(void) +{ + KUpdateStockPriceDlg dlg(this); + kMyMoneyPriceItem* item = dynamic_cast<kMyMoneyPriceItem*>(m_priceHistory->selectedItem()); + if(item) { + MyMoneySecurity security; + security = MyMoneyFile::instance()->security(item->price().from()); + dlg.m_security->setSecurity(security); + security = MyMoneyFile::instance()->security(item->price().to()); + dlg.m_currency->setSecurity(security); + } + if(dlg.exec()) { + MyMoneyPrice price(dlg.m_security->security().id(), dlg.m_currency->security().id(), dlg.date(), MyMoneyMoney(1,1)); + kMyMoneyPriceItem* p = new kMyMoneyPriceItem(m_priceHistory, price); + m_priceHistory->setSelected(p, true); + // If the user cancels the following operation, we delete the new item + // and re-select any previously selected one + if(slotEditPrice() == QDialog::Rejected) { + delete p; + if(item) + m_priceHistory->setSelected(item, true); + } + } +} + +int KMyMoneyPriceView::slotEditPrice(void) +{ + int rc = QDialog::Rejected; + kMyMoneyPriceItem* item = dynamic_cast<kMyMoneyPriceItem*>(m_priceHistory->selectedItem()); + if(item) { + MyMoneySecurity from(MyMoneyFile::instance()->security(item->price().from())); + MyMoneySecurity to(MyMoneyFile::instance()->security(item->price().to())); + signed64 fract = MyMoneyMoney::precToDenom(KMyMoneyGlobalSettings::pricePrecision()); + + KCurrencyCalculator calc(from, + to, + MyMoneyMoney(1,1), + item->price().rate(), + item->price().date(), + fract, + this, "currencyCalculator"); + // we always want to update the price, that's why we're here + calc.m_updateButton->setChecked(true); + calc.m_updateButton->hide(); + + rc = calc.exec(); + } + return rc; +} + +void KMyMoneyPriceView::slotDeletePrice(void) +{ + kMyMoneyPriceItem* item = dynamic_cast<kMyMoneyPriceItem*>(m_priceHistory->selectedItem()); + if(item) { + if(KMessageBox::questionYesNo(this, i18n("Do you really want to delete the selected price entry?"), i18n("Delete price information"), KStdGuiItem::yes(), KStdGuiItem::no(), "DeletePrice") == KMessageBox::Yes) { + MyMoneyFileTransaction ft; + try { + MyMoneyFile::instance()->removePrice(item->price()); + ft.commit(); + } catch(MyMoneyException *e) { + qDebug("Cannot delete price"); + delete e; + } + } + } +} + +void KMyMoneyPriceView::slotShowAllPrices(bool enabled) +{ + if(m_showAll != enabled) { + m_showAll = enabled; + slotReloadWidget(); + } +} + +void KMyMoneyPriceView::slotOnlinePriceUpdate(void) +{ + KMyMoneyPriceItem* item = dynamic_cast<KMyMoneyPriceItem*>(m_priceHistory->selectedItem()); + if(item) + { + KEquityPriceUpdateDlg dlg(this, (item->text(COMMODITY_COL)+" "+item->text(CURRENCY_COL)).utf8()); + if(dlg.exec() == QDialog::Accepted) + dlg.storePrices(); + } +} + +#endif + +#include "kmymoneypriceview.moc" diff --git a/kmymoney2/widgets/kmymoneypriceview.h b/kmymoney2/widgets/kmymoneypriceview.h new file mode 100644 index 0000000..56907ac --- /dev/null +++ b/kmymoney2/widgets/kmymoneypriceview.h @@ -0,0 +1,87 @@ +/*************************************************************************** + kmymoneypriceview.h - description + ------------------- + begin : Wed Mar 24 2004 + copyright : (C) 2004 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYPRICEVIEW_H +#define KMYMONEYPRICEVIEW_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +// class QListViewItem; + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kpopupmenu.h> +#include <klistview.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneylistviewitem.h> +//#include <kmymoney/mymoneymoney.h> +#include <kmymoney/mymoneyprice.h> + +/** + * @author Thomas Baumgart + */ + +class KMyMoneyPriceItem : public KMyMoneyListViewItem +{ +public: + KMyMoneyPriceItem(KListView *, const MyMoneyPrice& pr); + ~KMyMoneyPriceItem() {} + + int compare(QListViewItem *p, int col, bool ascending) const; + + const MyMoneyPrice& price(void) const { return m_pr; }; + +private: + MyMoneyPrice m_pr; +}; + + +class KMyMoneyPriceView : public KListView +{ + Q_OBJECT +public: + KMyMoneyPriceView(QWidget *parent=0, const char *name=0); + ~KMyMoneyPriceView(); + +protected: + /// the resize event + virtual void resizeEvent(QResizeEvent*); + +protected slots: + void slotListClicked(QListViewItem* item, const QPoint&, int); + +private slots: + void slotTimerDone(void); + +signals: + void newPrice(void); + void deletePrice(void); + void editPrice(void); + void onlinePriceUpdate(void); + +private: + KPopupMenu* m_contextMenu; + bool m_showAll; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyreportconfigtab1decl.ui b/kmymoney2/widgets/kmymoneyreportconfigtab1decl.ui new file mode 100644 index 0000000..0d94537 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyreportconfigtab1decl.ui @@ -0,0 +1,123 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>kMyMoneyReportConfigTab1Decl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyReportConfigTab1Decl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>172</height> + </rect> + </property> + <property name="caption"> + <string>Report Tab</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>On this tab, you set the basic properties of this report.</p></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout35</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Report Name</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_editName</cstring> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose a name for this report.</p></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout36</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Comment</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_editComment</cstring> + </property> + <property name="toolTip" stdset="0"> + <string><p>Enter a comment to help you remember the details of this report.</p></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkCurrency</cstring> + </property> + <property name="text"> + <string>Convert values to base currency</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to convert all values in the report to your base currency.</p><p>Leave it unchecked if you'd like to see values in their original currency.</p><p>If currencies are not converted, then subtotals will not be shown.</p></string> + <comment>Convert 'em!!</comment> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkFavorite</cstring> + </property> + <property name="text"> + <string>Mark as a favorite report</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to notate this report as one of your favorites.</p><p>All your favorite reports are grouped in one place on the report list for easy access.</p></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneyreportconfigtab2decl.ui b/kmymoney2/widgets/kmymoneyreportconfigtab2decl.ui new file mode 100644 index 0000000..1ad7cf0 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyreportconfigtab2decl.ui @@ -0,0 +1,282 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>kMyMoneyReportConfigTab2Decl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyReportConfigTab2Decl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>278</height> + </rect> + </property> + <property name="caption"> + <string>Rows/Columns Tab</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>On this tab, you configure how you'd like the rows and columns to be selected and organized.</p></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Detail</string> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + <item> + <property name="text"> + <string>Top-Level</string> + </property> + </item> + <item> + <property name="text"> + <string>Groups</string> + </property> + </item> + <item> + <property name="text"> + <string>Totals</string> + </property> + </item> + <property name="name"> + <cstring>m_comboDetail</cstring> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose what kind of accounts to display as the rows of this report.</p></string> + </property> + </widget> + <widget class="QFrame" row="0" column="2"> + <property name="name"> + <cstring>m_budgetFrame</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Budget</string> + </property> + </widget> + <widget class="KMyMoneyGeneralCombo"> + <property name="name"> + <cstring>m_comboBudget</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Columns</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Daily</string> + </property> + </item> + <item> + <property name="text"> + <string>Weekly</string> + </property> + </item> + <item> + <property name="text"> + <string>Monthly</string> + </property> + </item> + <item> + <property name="text"> + <string>Bi-Monthly</string> + </property> + </item> + <item> + <property name="text"> + <string>Quarterly</string> + </property> + </item> + <item> + <property name="text"> + <string>Yearly</string> + </property> + </item> + <property name="name"> + <cstring>m_comboColumns</cstring> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose how large of a time period each column should encompass</p></string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Income & Expenses</string> + </property> + </item> + <item> + <property name="text"> + <string>Assets & Liabilities</string> + </property> + </item> + <property name="name"> + <cstring>m_comboRows</cstring> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose what kind of accounts to display as the rows of this report.</p></string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Rows</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="1"> + <property name="name"> + <cstring>m_checkTotalColumn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Show totals column</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="text"> + <string>Average days</string> + </property> + </widget> + <widget class="QSpinBox" row="4" column="1"> + <property name="name"> + <cstring>m_movingAverageDays</cstring> + </property> + <property name="maxValue"> + <number>366</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer12_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>94</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkScheduled</cstring> + </property> + <property name="text"> + <string>Include scheduled transactions</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkTransfers</cstring> + </property> + <property name="text"> + <string>Include transfers</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkUnused</cstring> + </property> + <property name="text"> + <string>Include unused accounts/categories</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneyreportconfigtab3decl.ui b/kmymoney2/widgets/kmymoneyreportconfigtab3decl.ui new file mode 100644 index 0000000..2284c1f --- /dev/null +++ b/kmymoney2/widgets/kmymoneyreportconfigtab3decl.ui @@ -0,0 +1,437 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>kMyMoneyReportConfigTab3Decl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyReportConfigTab3Decl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>617</width> + <height>267</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Rows/Columns Tab</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>On this tab, you configure how you'd like the rows and columns to be selected and organized.</p></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + </font> + </property> + <property name="text"> + <string>Organize by:</string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Categories</string> + </property> + </item> + <item> + <property name="text"> + <string>Top Categories</string> + </property> + </item> + <item> + <property name="text"> + <string>Payees</string> + </property> + </item> + <item> + <property name="text"> + <string>Accounts</string> + </property> + </item> + <item> + <property name="text"> + <string>Top Accounts</string> + </property> + </item> + <item> + <property name="text"> + <string>Month</string> + </property> + </item> + <item> + <property name="text"> + <string>Week</string> + </property> + </item> + <property name="name"> + <cstring>m_comboOrganizeBy</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose how to group the transactions in this report</p></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer10_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>358</width> + <height>16</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Show Columns</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Choose which columns should be shown in the report.</p><p>The date and transaction amount are always shown.</p></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>2</number> + </property> + <widget class="QCheckBox" row="1" column="1"> + <property name="name"> + <cstring>m_checkMemo</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Memo</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Memo column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="3"> + <property name="name"> + <cstring>m_checkShares</cstring> + </property> + <property name="text"> + <string>Shares</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Shares column for investments</p></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="3"> + <property name="name"> + <cstring>m_checkPrice</cstring> + </property> + <property name="text"> + <string>Price</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Price column for investments</p></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="2"> + <property name="name"> + <cstring>m_checkReconciled</cstring> + </property> + <property name="text"> + <string>Reconciled</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Reconciled column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="2"> + <property name="name"> + <cstring>m_checkAccount</cstring> + </property> + <property name="text"> + <string>Account</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Account column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>m_checkNumber</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Number</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Number column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>m_checkPayee</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Payee</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Payee column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>m_checkCategory</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Category</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Category column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="4"> + <property name="name"> + <cstring>m_checkAction</cstring> + </property> + <property name="text"> + <string>Action</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Action column</p></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="4"> + <property name="name"> + <cstring>m_checkBalance</cstring> + </property> + <property name="text"> + <string>Balance</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show the Running balance column</p></string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>205</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>m_checkLoans</cstring> + </property> + <property name="text"> + <string>Include only Loan accounts</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>m_checkInvestments</cstring> + </property> + <property name="text"> + <string>Include only Investment accounts</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Check this box to include only those categories which have been marked to "Include on Tax Reports"</p></string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="1"> + <property name="name"> + <cstring>m_checkHideSplitDetails</cstring> + </property> + <property name="text"> + <string>Hide Split Transaction Details</string> + </property> + <property name="toolTip" stdset="0"> + <string>Do not display the individual transactions that make up a split transaction</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>m_checkTax</cstring> + </property> + <property name="text"> + <string>Include only Tax categories</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Check this box to include only those categories which have been marked to "Include on Tax Reports"</p></string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<connections> + <connection> + <sender>m_checkTax</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkInvestments</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>m_checkTax</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkLoans</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>m_checkInvestments</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkTax</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>m_checkInvestments</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkLoans</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>m_checkLoans</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkTax</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>m_checkLoans</sender> + <signal>toggled(bool)</signal> + <receiver>m_checkInvestments</receiver> + <slot>setDisabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneyreportconfigtabchartdecl.ui b/kmymoney2/widgets/kmymoneyreportconfigtabchartdecl.ui new file mode 100644 index 0000000..2aa9126 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyreportconfigtabchartdecl.ui @@ -0,0 +1,214 @@ +<!DOCTYPE UI><UI version="3.0"> +<class>kMyMoneyReportConfigTabChartDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyReportConfigTabChartDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>174</height> + </rect> + </property> + <property name="caption"> + <string>Chart Tab</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string><p>On this tab, you configure the chart drawn forthis report.</p></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Chart Type</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select what form you would like the chart to be drawn as.</p></string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Line</string> + </property> + </item> + <item> + <property name="text"> + <string>Bar</string> + </property> + </item> + <item> + <property name="text"> + <string>Stacked Bar</string> + </property> + </item> + <item> + <property name="text"> + <string>Pie</string> + </property> + </item> + <item> + <property name="text"> + <string>Ring</string> + </property> + </item> + <property name="name"> + <cstring>m_comboType</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>121</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkGridLines</cstring> + </property> + <property name="text"> + <string>Show grid lines</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to show horizontal and vertical grid lines on the chart.</p></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkValues</cstring> + </property> + <property name="text"> + <string>Draw values on chart</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to draw the numeric values for data points next to their plot location.</p></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkShowChart</cstring> + </property> + <property name="text"> + <string>Show as chart by default</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select this option to cause the report to be shown as a chart when you first open the report. Otherwise, it will come up as a text report.</p></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Line width</string> + </property> + <property name="toolTip" stdset="0"> + <string><p>Select what width should be used to draw the line on the chart</p></string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>m_lineWidth</cstring> + </property> + <property name="maxValue"> + <number>10</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>121</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneyreportcontroldecl.ui b/kmymoney2/widgets/kmymoneyreportcontroldecl.ui new file mode 100644 index 0000000..2d0e0e3 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyreportcontroldecl.ui @@ -0,0 +1,150 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>kMyMoneyReportControlDecl</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kMyMoneyReportControlDecl</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>33</height> + </rect> + </property> + <property name="caption"> + <string>ReportControl</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>1</number> + </property> + <property name="spacing"> + <number>1</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonChart</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Chart</string> + </property> + <property name="toolTip" stdset="0"> + <string>Show the chart version of this report</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonConfigure</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Configure</string> + </property> + <property name="toolTip" stdset="0"> + <string>Configure this report</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonNew</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>New</string> + </property> + <property name="toolTip" stdset="0"> + <string>Create a new report based on this one</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCopy</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Copy</string> + </property> + <property name="toolTip" stdset="0"> + <string>Copy this report to the clipboard</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonExport</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Export</string> + </property> + <property name="toolTip" stdset="0"> + <string>Export this report as an HTML or CSV file</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonDelete</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Delete</string> + </property> + <property name="toolTip" stdset="0"> + <string>Permanently delete this report</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonClose</cstring> + </property> + <property name="minimumSize"> + <size> + <width>75</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Close</string> + </property> + <property name="toolTip" stdset="0"> + <string>Close this window</string> + </property> + </widget> + </hbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/kmymoneyscheduledcalendar.cpp b/kmymoney2/widgets/kmymoneyscheduledcalendar.cpp new file mode 100644 index 0000000..6c17a33 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyscheduledcalendar.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + kmymoneyscheduledcalendar.cpp - description + ------------------- + begin : Wed Jul 2 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qpushbutton.h> +#include <qkeysequence.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include "../widgets/kmymoneyscheduledcalendar.h" +#include "../mymoney/mymoneyfile.h" + +kMyMoneyScheduledCalendar::kMyMoneyScheduledCalendar(QWidget *parent, const char *name ) + : kMyMoneyCalendar(parent,name) +{ + QPushButton *pb1 = new QPushButton(i18n("Select Schedules"), this); + + kpopupmenu = new KPopupMenu(this); + kpopupmenu->setCheckable(true); + kpopupmenu->insertItem(i18n("Bills"), 0); + kpopupmenu->insertItem(i18n("Deposits"), 1); + kpopupmenu->insertItem(i18n("Transfers"), 2); + kpopupmenu->connectItem(0, this, SLOT(slotSetViewBills())); + kpopupmenu->connectItem(1, this, SLOT(slotSetViewDeposits())); + kpopupmenu->connectItem(2, this, SLOT(slotSetViewTransfers())); + kpopupmenu->setItemChecked(0, true); + kpopupmenu->setItemChecked(1, true); + kpopupmenu->setItemChecked(2, true); + pb1->setPopup(kpopupmenu); + + m_scheduledDateTable = new kMyMoneyScheduledDateTbl(this); + setDateTable((kMyMoneyDateTbl*)m_scheduledDateTable); + + setUserButton1(true, pb1); + + init( QDate::currentDate() ); + + connect(m_scheduledDateTable, SIGNAL(enterClicked(const MyMoneySchedule&, const QDate&)), + this, SIGNAL(enterClicked(const MyMoneySchedule&, const QDate&))); + connect(m_scheduledDateTable, SIGNAL(skipClicked(const MyMoneySchedule&, const QDate&)), + this, SIGNAL(skipClicked(const MyMoneySchedule&, const QDate&))); +} + +kMyMoneyScheduledCalendar::~kMyMoneyScheduledCalendar() +{ +} + +void kMyMoneyScheduledCalendar::slotSetViewBills() +{ + kpopupmenu->setItemChecked(0, ((kpopupmenu->isItemChecked(0)) ? false : true)); + m_scheduledDateTable->filterBills(!kpopupmenu->isItemChecked(0)); +} + +void kMyMoneyScheduledCalendar::slotSetViewDeposits() +{ + kpopupmenu->setItemChecked(1, ((kpopupmenu->isItemChecked(1)) ? false : true)); + m_scheduledDateTable->filterDeposits(!kpopupmenu->isItemChecked(1)); +} + +void kMyMoneyScheduledCalendar::slotSetViewTransfers() +{ + kpopupmenu->setItemChecked(2, ((kpopupmenu->isItemChecked(2)) ? false : true)); + m_scheduledDateTable->filterTransfers(!kpopupmenu->isItemChecked(2)); +} + +#include "kmymoneyscheduledcalendar.moc" diff --git a/kmymoney2/widgets/kmymoneyscheduledcalendar.h b/kmymoney2/widgets/kmymoneyscheduledcalendar.h new file mode 100644 index 0000000..1543b5a --- /dev/null +++ b/kmymoney2/widgets/kmymoneyscheduledcalendar.h @@ -0,0 +1,87 @@ +/*************************************************************************** + kmymoneyscheduledcalendar.h - description + ------------------- + begin : Wed Jul 2 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYSCHEDULEDCALENDAR_H +#define KMYMONEYSCHEDULEDCALENDAR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../widgets/kmymoneycalendar.h" +#include "../widgets/kmymoneyscheduleddatetbl.h" + +class KPopupMenu; +class kMyMoneyDateTbl; + +/** + * A representation of a calendar. + * + * Uses the base class kMyMoneyCalendar to actually render + * the calendar. + * + * @author Michael Edwardes 2003 + * +**/ +class kMyMoneyScheduledCalendar : public kMyMoneyCalendar { + Q_OBJECT + +public: + /** + * Standard constructor. + **/ + kMyMoneyScheduledCalendar(QWidget *parent=0, const char *name=0); + + /** + * Standard destructor. + **/ + ~kMyMoneyScheduledCalendar(); + + /** + * Dynamically set the Date Table + **/ + void setDateTable(kMyMoneyDateTbl* tbl) { table = tbl; } + + void refresh() { m_scheduledDateTable->refresh(); } + + void setFilterAccounts(const QStringList& list) { m_scheduledDateTable->setFilterAccounts(list); } + +signals: + void enterClicked(const MyMoneySchedule&, const QDate&); + void skipClicked(const MyMoneySchedule&, const QDate&); + +protected slots: + void slotSetViewBills(); + void slotSetViewDeposits(); + void slotSetViewTransfers(); + +private: + KPopupMenu* kpopupmenu; + kMyMoneyScheduledDateTbl *m_scheduledDateTable; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyscheduleddatetbl.cpp b/kmymoney2/widgets/kmymoneyscheduleddatetbl.cpp new file mode 100644 index 0000000..870abd5 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyscheduleddatetbl.cpp @@ -0,0 +1,530 @@ +/*************************************************************************** + kmymoneyscheduleddatetbl.cpp - description + ------------------- + begin : Thu Jul 3 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@users.sourceforge.net> + ***************************************************************************/ + /**************************************************************************** + Contains code from the KDateTable class ala kdelibs-3.1.2. Original license: + + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qstring.h> +#include <qpen.h> +#include <qpainter.h> +#include <qdialog.h> +#include <qdrawutil.h> +#include <qcursor.h> +#include <qapplication.h> + +// ---------------------------------------------------------------------------- +// KDE Includes +#include "kdecompat.h" +#include <kglobal.h> +#include <kglobalsettings.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes +#include "kmymoneyscheduleddatetbl.h" +#include "../mymoney/mymoneyfile.h" + +kMyMoneyScheduledDateTbl::kMyMoneyScheduledDateTbl(QWidget *parent, QDate date_, const char* name, WFlags f ) + : kMyMoneyDateTbl(parent, date_, name, f), + m_filterBills(false), m_filterDeposits(false), m_filterTransfers(false) +{ + connect(&briefWidget, SIGNAL(enterClicked(const MyMoneySchedule&, const QDate&)), this, SIGNAL(enterClicked(const MyMoneySchedule&, const QDate&))); + connect(&briefWidget, SIGNAL(skipClicked(const MyMoneySchedule&, const QDate&)), this, SIGNAL(skipClicked(const MyMoneySchedule&, const QDate&))); +} + +kMyMoneyScheduledDateTbl::~kMyMoneyScheduledDateTbl() +{ +} + +void kMyMoneyScheduledDateTbl::drawCellContents(QPainter *painter, int /*row*/, int /*col*/, const QDate& theDate) +{ + QRect rect; + QString text; + int w=cellWidth(); + int h=cellHeight(); + QPen pen; + QBrush brushBlue(KGlobalSettings::activeTitleColor()); + QBrush brushLightblue(KGlobalSettings::baseColor()); + QFont font=KGlobalSettings::generalFont(); + MyMoneyFile *file = MyMoneyFile::instance(); + + // ----- + font.setPointSize(fontsize); + QFont fontLarge(font); + QFont fontSmall(font); + fontLarge.setPointSize(fontsize*2); + fontSmall.setPointSize(fontsize-1); + + painter->setFont(font); + + + if (m_type == MONTHLY) + { + if (theDate.month() != date.month()) + { + painter->setFont(fontSmall); + pen = lightGray; + } + else + { + pen = gray; + } + + if (theDate == date) + { + if (hasFocus()) + { // draw the currently selected date + painter->setPen(KGlobalSettings::highlightColor()); + painter->setBrush(KGlobalSettings::highlightColor()); + pen=white; + } else { + painter->setPen(KGlobalSettings::calculateAlternateBackgroundColor(KGlobalSettings::highlightColor())); + painter->setBrush(KGlobalSettings::calculateAlternateBackgroundColor(KGlobalSettings::highlightColor())); + pen=white; + } + } else { + painter->setBrush(KGlobalSettings::baseColor()); + painter->setPen(KGlobalSettings::baseColor()); + } + painter->drawRect(0, 0, w, h); + painter->setPen(pen); + text = QString::number(theDate.day()); + addDayPostfix(text); + painter->drawText(0, 0, w-2, h, AlignRight, text, -1, &rect); + + MyMoneyFile *file = MyMoneyFile::instance(); + QValueList<MyMoneySchedule> schedules; + try + { + + // Honour the filter. + int scheduleTypes=0; + int scheduleOcurrences=0; + int schedulePaymentTypes=0; + + scheduleOcurrences |= MyMoneySchedule::OCCUR_ANY; + schedulePaymentTypes |= MyMoneySchedule::STYPE_ANY; + + if (!m_filterBills) + { + scheduleTypes |= MyMoneySchedule::TYPE_BILL; + } + if (!m_filterDeposits) + { + scheduleTypes |= MyMoneySchedule::TYPE_DEPOSIT; + } + if (!m_filterTransfers) + { + scheduleTypes |= MyMoneySchedule::TYPE_TRANSFER; + } + + schedules = file->scheduleListEx( scheduleTypes, + scheduleOcurrences, + schedulePaymentTypes, + theDate, + m_filterAccounts); + } + catch ( MyMoneyException* e) + { + // SAfe to ignore here, cause no schedules might exist + // for the selected account + delete e; + } + + if (schedules.count() >= 1) + { + QValueList<MyMoneySchedule>::Iterator iter; + bool anyOverdue=false; + for (iter=schedules.begin(); iter!=schedules.end(); ++iter) + { + MyMoneySchedule schedule = *iter; + if (theDate < QDate::currentDate()) + { + if (schedule.isOverdue()) + { + anyOverdue = true; + break; // out early + } + } + } + + if (anyOverdue) + painter->setPen(red); + else + painter->setPen(darkGray); + + painter->setFont(fontLarge); + painter->drawText(0, 0, w, h, AlignCenter, QString::number(schedules.count()), + -1, &rect); + } + + painter->setPen(lightGray); + painter->setBrush(Qt::NoBrush); + painter->drawRect(0, 0, w, h); + } + else if (m_type == WEEKLY) + { + // TODO: Handle other start weekdays than Monday + if (theDate == date) + { + painter->setBrush(KGlobalSettings::highlightColor()); + } + else + { + painter->setBrush(KGlobalSettings::baseColor()); + painter->setPen(KGlobalSettings::baseColor()); + } + + painter->setPen(lightGray); + painter->drawRect(0, 0, w, h); + + text = QString::number(theDate.day()); + addDayPostfix(text); + + painter->drawText(0, 0, w-2, h, AlignRight, QDate::shortDayName(theDate.dayOfWeek()) + " " + text, -1, &rect); + + QValueList<MyMoneySchedule> billSchedules; + QValueList<MyMoneySchedule> depositSchedules; + QValueList<MyMoneySchedule> transferSchedules; + try + { + text = QString(); + + if (!m_filterBills) + { + billSchedules = file->scheduleListEx( MyMoneySchedule::TYPE_BILL, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + theDate, + m_filterAccounts); + + if (billSchedules.count() >= 1) + { + text += i18n("%1 Bills.").arg(QString::number(billSchedules.count())); + } + } + + if (!m_filterDeposits) + { + depositSchedules = file->scheduleListEx( MyMoneySchedule::TYPE_DEPOSIT, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + theDate, + m_filterAccounts); + + if (depositSchedules.count() >= 1) + { + if(!text.isEmpty()) + text += " "; + text += i18n("%1 Deposits.").arg(QString::number(depositSchedules.count())); + } + } + + if (!m_filterTransfers) + { + transferSchedules = file->scheduleListEx( MyMoneySchedule::TYPE_TRANSFER, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + theDate, + m_filterAccounts); + + if (transferSchedules.count() >= 1) + { + if(!text.isEmpty()) + text += " "; + text += i18n("%1 Transfers.").arg(QString::number(transferSchedules.count())); + } + } + } + catch (MyMoneyException* e) + { + // SAfe to ignore here, cause no schedules might exist + // for the selected account + delete e; + } + + bool anyOverdue=false; + QValueList<MyMoneySchedule>::Iterator iter; + for (iter=transferSchedules.begin(); iter!=transferSchedules.end(); ++iter) + { + MyMoneySchedule schedule = *iter; + if (theDate < QDate::currentDate()) + { + if (schedule.isOverdue()) + { + anyOverdue = true; + break; // out early + } + } + } + + if (!anyOverdue) + { + for (iter=depositSchedules.begin(); iter!=depositSchedules.end(); ++iter) + { + MyMoneySchedule schedule = *iter; + if (theDate < QDate::currentDate()) + { + if (schedule.isOverdue()) + { + anyOverdue = true; + break; // out early + } + } + } + + if (!anyOverdue) + { + for (iter=billSchedules.begin(); iter!=billSchedules.end(); ++iter) + { + MyMoneySchedule schedule = *iter; + if (theDate < QDate::currentDate()) + { + if (schedule.isOverdue()) + { + anyOverdue = true; + break; // out early + } + } + } + } + } + + if (anyOverdue) + painter->setPen(red); + else + painter->setPen(darkGray); + + painter->setFont(fontLarge); + painter->drawText(0, 0, w, h, AlignCenter, text, + -1, &rect); + } + else if (m_type == QUARTERLY) + { + painter->setBrush(KGlobalSettings::baseColor()); + + painter->setPen(lightGray); + painter->drawRect(0, 0, w, h); + } +} + +void kMyMoneyScheduledDateTbl::addDayPostfix(QString& text) +{ + int d = text.toInt(); + + if (d >= 1 && d <= 31) + { + QStringList postfixList = QStringList::split("-", i18n("st-nd-rd-th-th-th-th-th-th-th-th-th-th-th-th-th-th-th-th-th-st-nd-rd-th-th-th-th-th-th-th-st"), true); + text += postfixList[d-1]; + } +} + +void kMyMoneyScheduledDateTbl::refresh() +{ + repaintContents(false); +} + +void kMyMoneyScheduledDateTbl::contentsMouseMoveEvent(QMouseEvent* e) +{ + int row, col, pos; + QPoint mouseCoord; + + if (isActiveWindow() || briefWidget.isVisible()) + { + mouseCoord = e->pos(); + row = rowAt(mouseCoord.y()); + col = columnAt(mouseCoord.x()); + if (row<1 || col<0) + { + return; + } + + #if KDE_VERSION < 310 + int firstWeekDay = KGlobal::locale()->weekStartsMonday() ? 1 : 0; + #else + int firstWeekDay = KGlobal::locale()->weekStartDay(); + #endif + + QDate drawDate(date); + QString text; + + if (m_type == MONTHLY) + { + pos=7*(row-1)+col; + if ( firstWeekDay < 4 ) + pos += firstWeekDay; + else + pos += firstWeekDay - 7; + + if (pos<firstday || (firstday+numdays<=pos)) + { // we are either + // painting a day of the previous month or + // painting a day of the following month + + if (pos<firstday) + { // previous month + drawDate = drawDate.addMonths(-1); + text.setNum(numDaysPrevMonth+pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } else { // following month + drawDate = drawDate.addMonths(1); + text.setNum(pos-firstday-numdays+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } else { // paint a day of the current month + text.setNum(pos-firstday+1); + drawDate.setYMD(drawDate.year(), drawDate.month(), text.toInt()); + } + } + else if (m_type == WEEKLY) + { + // TODO: Handle other start weekdays than Monday + text = QDate::shortDayName(row); + text += " "; + + int dayOfWeek = date.dayOfWeek(); + int diff; + + if (row < dayOfWeek) + { + diff = -(dayOfWeek - row); + } + else + { + diff = row - dayOfWeek; + } + + drawDate = date.addDays(diff); + } + else if (m_type == QUARTERLY) + { + } + + m_drawDateOrig = drawDate; + MyMoneyFile *file = MyMoneyFile::instance(); + QValueList<MyMoneySchedule> schedules; + + try + { + int types=0; + + if (!m_filterBills) + { + types |= MyMoneySchedule::TYPE_BILL; + } + + if (!m_filterDeposits) + { + types |= MyMoneySchedule::TYPE_DEPOSIT; + } + + if (!m_filterTransfers) + { + types |= MyMoneySchedule::TYPE_TRANSFER; + } + + + schedules = file->scheduleListEx( types, + MyMoneySchedule::OCCUR_ANY, + MyMoneySchedule::STYPE_ANY, + drawDate, + m_filterAccounts); + } + catch ( MyMoneyException* e) + { + // SAfe to ignore here, cause no schedules might exist + // for the selected account + delete e; + } + + if (schedules.count() >= 1) + { + briefWidget.setSchedules(schedules, drawDate); + + int h = briefWidget.height(); + int w = briefWidget.width(); + + // Take off five pixels so the mouse cursor + // will be over the widget + QPoint p = QCursor::pos(); + if (p.y() + h > QApplication::desktop()->height()) + { + p.setY(p.y() - (h-5)); + } + else + p.setY(p.y() - 5); + + if (p.x() + w > QApplication::desktop()->width()) + { + p.setX(p.x() - (w-5)); + } + else + p.setX(p.x() - 5); + + briefWidget.move(p); + briefWidget.show(); + } + else + { + briefWidget.hide(); + } + } +} + +void kMyMoneyScheduledDateTbl::filterBills(bool enable) +{ + m_filterBills = enable; + repaintContents(false); +} + +void kMyMoneyScheduledDateTbl::filterDeposits(bool enable) +{ + m_filterDeposits = enable; + repaintContents(false); +} + +void kMyMoneyScheduledDateTbl::filterTransfers(bool enable) +{ + m_filterTransfers = enable; + repaintContents(false); +} + +#include "kmymoneyscheduleddatetbl.moc" diff --git a/kmymoney2/widgets/kmymoneyscheduleddatetbl.h b/kmymoney2/widgets/kmymoneyscheduleddatetbl.h new file mode 100644 index 0000000..5985a6b --- /dev/null +++ b/kmymoney2/widgets/kmymoneyscheduleddatetbl.h @@ -0,0 +1,73 @@ +/*************************************************************************** + kmymoneyscheduleddatetbl.h - description + ------------------- + begin : Thu Jul 3 2003 + copyright : (C) 2000-2003 by Michael Edwardes + email : mte@users.sourceforge.net + Javier Campos Morales <javi_c@users.sourceforge.net> + Felix Rodriguez <frodriguez@users.sourceforge.net> + John C <thetacoturtle@users.sourceforge.net> + Thomas Baumgart <ipwizard@users.sourceforge.net> + Kevin Tambascio <ktambascio@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 KMYMONEYSCHEDULEDDATETBL_H +#define KMYMONEYSCHEDULEDDATETBL_H + + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "../widgets/kmymoneydatetbl.h" +#include "../widgets/kmymoneybriefschedule.h" +#include "../mymoney/mymoneyscheduled.h" + +/** + * @author Michael Edwardes + */ + +class kMyMoneyScheduledDateTbl : public kMyMoneyDateTbl +{ + Q_OBJECT +public: + kMyMoneyScheduledDateTbl(QWidget *parent=0, + QDate date=QDate::currentDate(), + const char* name=0, WFlags f=0); + + ~kMyMoneyScheduledDateTbl(); + void refresh(); + void filterBills(bool enable); + void filterDeposits(bool enable); + void filterTransfers(bool enable); + void setFilterAccounts(const QStringList& list) { m_filterAccounts = list; repaintContents(false); } + +signals: + void enterClicked(const MyMoneySchedule&, const QDate&); + void skipClicked(const MyMoneySchedule&, const QDate&); + +protected: + void drawCellContents(QPainter *painter, int row, int col, const QDate& theDate); + void addDayPostfix(QString& text); + void contentsMouseMoveEvent(QMouseEvent* e); + +private: + bool m_filterBills, m_filterDeposits, m_filterTransfers; + QStringList m_filterAccounts; + KMyMoneyBriefSchedule briefWidget; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneyselector.cpp b/kmymoney2/widgets/kmymoneyselector.cpp new file mode 100644 index 0000000..6ae07ca --- /dev/null +++ b/kmymoney2/widgets/kmymoneyselector.cpp @@ -0,0 +1,709 @@ +/*************************************************************************** + kmymoneyselector.cpp + ------------------- + begin : Thu Jun 29 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlayout.h> +#include <qheader.h> +#include <qtimer.h> +#include <qstyle.h> +#include <qregexp.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneyselector.h> +#include <kmymoney/kmymoneylistviewitem.h> +#include <kmymoney/kmymoneychecklistitem.h> + +#include "../kmymoneyglobalsettings.h" + +KMyMoneySelector::KMyMoneySelector(QWidget *parent, const char *name, QWidget::WFlags flags) : + QWidget(parent, name, flags) +{ + m_selMode = QListView::Single; + + m_listView = new KListView(this); + // don't show horizontal scroll bar + m_listView->setHScrollBarMode(QScrollView::AlwaysOff); + + m_listView->setSorting(-1); + + if(parent) { + setFocusProxy(parent->focusProxy()); + m_listView->setFocusProxy(parent->focusProxy()); + } + + m_listView->setAllColumnsShowFocus(true); + + m_layout = new QHBoxLayout( this, 0, 6); + + m_listView->addColumn( "Hidden" ); + m_listView->header()->hide(); + m_listView->header()->setStretchEnabled(true, -1); + m_listView->header()->adjustHeaderSize(); + + m_layout->addWidget( m_listView ); + + // force init + m_selMode = QListView::Multi; + setSelectionMode(QListView::Single); + + connect(m_listView, SIGNAL(rightButtonPressed(QListViewItem* , const QPoint&, int)), this, SLOT(slotListRightMouse(QListViewItem*, const QPoint&, int))); +} + +KMyMoneySelector::~KMyMoneySelector() +{ +} + +void KMyMoneySelector::clear(void) +{ + m_listView->clear(); + m_visibleItem = 0; +} + +void KMyMoneySelector::setSelectionMode(const QListView::SelectionMode mode) +{ + if(m_selMode != mode) { + m_selMode = mode; + clear(); + + // make sure, it's either Multi or Single + if(mode != QListView::Multi) { + m_selMode = QListView::Single; + connect(m_listView, SIGNAL(selectionChanged(void)), this, SIGNAL(stateChanged(void))); + connect(m_listView, SIGNAL(executed(QListViewItem*)), this, SLOT(slotItemSelected(QListViewItem*))); + } else { + disconnect(m_listView, SIGNAL(selectionChanged(void)), this, SIGNAL(stateChanged(void))); + disconnect(m_listView, SIGNAL(executed(QListViewItem*)), this, SLOT(slotItemSelected(QListViewItem*))); + } + } + QWidget::update(); +} + +void KMyMoneySelector::slotItemSelected(QListViewItem *item) +{ + if(m_selMode == QListView::Single) { + KMyMoneyListViewItem* l_item = dynamic_cast<KMyMoneyListViewItem*>(item); + if(l_item && l_item->isSelectable()) { + emit itemSelected(l_item->id()); + } + } +} + +QListViewItem* KMyMoneySelector::newItem(const QString& name, QListViewItem* after, const QString& key, const QString& id, QCheckListItem::Type type) +{ + QListViewItem* item; + if(after) + item = new KMyMoneyCheckListItem(m_listView, after, name, key, id, type); + else + item = new KMyMoneyCheckListItem(m_listView, name, key, id, type); + + item->setSelectable(!id.isEmpty()); + item->setOpen(true); + return item; +} + +QListViewItem* KMyMoneySelector::newItem(const QString& name, const QString& key, const QString& id, QCheckListItem::Type type) +{ + return newItem(name, 0, key, id, type); +} + +QListViewItem* KMyMoneySelector::newTopItem(const QString& name, const QString& key, const QString& id) +{ + QListViewItem* p; + + if(m_selMode == QListView::Multi) { + KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(m_listView, name, key, id); + connect(q, SIGNAL(stateChanged(bool)), this, SIGNAL(stateChanged(void))); + p = static_cast<QListViewItem*> (q); + + } else { + KMyMoneyListViewItem* q = new KMyMoneyListViewItem(m_listView, name, key, id); + p = static_cast<QListViewItem*> (q); + } + + return p; +} + +QListViewItem* KMyMoneySelector::newItem(QListViewItem* parent, const QString& name, const QString& key, const QString& id) +{ + QListViewItem* p; + + if(m_selMode == QListView::Multi) { + KMyMoneyCheckListItem* q = new KMyMoneyCheckListItem(parent, name, key, id); + connect(q, SIGNAL(stateChanged(bool)), this, SIGNAL(stateChanged(void))); + p = static_cast<QListViewItem*> (q); + + } else { + KMyMoneyListViewItem* q = new KMyMoneyListViewItem(parent, name, key, id); + p = static_cast<QListViewItem*> (q); + } + + return p; +} + +void KMyMoneySelector::protectItem(const QString& itemId, const bool protect) +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + QListViewItem* it_v; + KMyMoneyListViewItem* it_l; + KMyMoneyCheckListItem* it_c; + + // scan items + while((it_v = it.current()) != 0) { + it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v); + if(it_l) { + if(it_l->id() == itemId) { + it_l->setSelectable(!protect); + break; + } + } else { + it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c) { + if(it_c->id() == itemId) { + it_c->setSelectable(!protect); + break; + } + } + } + ++it; + } +} + +QListViewItem* KMyMoneySelector::item(const QString& id) const +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + QListViewItem* it_v; + KMyMoneyListViewItem* it_l; + KMyMoneyCheckListItem* it_c; + + while((it_v = it.current()) != 0) { + it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v); + if(it_l) { + if(it_l->id() == id) + break; + } else { + it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->id() == id) + break; + } + ++it; + } + return it_v; +} + +int KMyMoneySelector::optimizedWidth(void) const +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + QListViewItem* it_v; + KMyMoneyListViewItem* it_l; + KMyMoneyCheckListItem* it_c; + + // scan items + int w = 0; +#ifndef KMM_DESIGNER + QFontMetrics fm( KMyMoneyGlobalSettings::listCellFont()); +#else + QFontMetrics fm( font() ); +#endif + while((it_v = it.current()) != 0) { + it_l = dynamic_cast<KMyMoneyListViewItem*>(it_v); + int nw = 0; + if(it_l) { + nw = it_l->width(fm, m_listView, 0); + } else { + it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c) { + nw = it_c->width(fm, m_listView, 0); + } + } + if(nw > w) + w = nw; + ++it; + } + return w; +} + +void KMyMoneySelector::setOptimizedWidth(void) +{ + int w = optimizedWidth(); + + m_listView->setMinimumWidth(w+30); + m_listView->setMaximumWidth(w+30); + m_listView->setColumnWidth(0, w+28); +} + +bool KMyMoneySelector::allItemsSelected(void) const +{ + QListViewItem* it_v; + + if(m_selMode == QListView::Single) + return false; + + for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + QCheckListItem* it_c = dynamic_cast<QCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + if(!(it_c->isOn() && allItemsSelected(it_v))) + return false; + } else { + if(!allItemsSelected(it_v)) + return false; + } + } + } + return true; +} + +bool KMyMoneySelector::allItemsSelected(const QListViewItem *item) const +{ + QListViewItem* it_v; + + for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + QCheckListItem* it_c = static_cast<QCheckListItem*>(it_v); + if(!(it_c->isOn() && allItemsSelected(it_v))) + return false; + } + } + return true; +} + +void KMyMoneySelector::removeItem(const QString& id) +{ + QListViewItem* it_v; + QListViewItemIterator it; + + it = QListViewItemIterator(m_listView); + while((it_v = it.current()) != 0) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + if(id == it_c->id()) { + if(it_c->firstChild()) { + it_c->setSelectable(false); + } else { + delete it_c; + } + } + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + if(id == it_c->id()) { + if(it_c->firstChild()) { + it_c->setSelectable(false); + } else { + delete it_c; + } + } + } + it++; + } + + // get rid of top items that just lost the last children (e.g. Favorites) + it = QListViewItemIterator(m_listView, QListViewItemIterator::NotSelectable); + while((it_v = it.current()) != 0) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->childCount() == 0) + delete it_c; + } + it++; + } + + return; +} + + +void KMyMoneySelector::selectAllItems(const bool state) +{ + QListViewItem* it_v; + + for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + QCheckListItem* it_c = dynamic_cast<QCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + it_c->setOn(state); + } + selectAllSubItems(it_v, state); + } + } + emit stateChanged(); +} + +void KMyMoneySelector::selectItems(const QStringList& itemList, const bool state) +{ + QListViewItem* it_v; + + for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox && itemList.contains(it_c->id())) { + it_c->setOn(state); + } + selectSubItems(it_v, itemList, state); + } + } + emit stateChanged(); +} + +void KMyMoneySelector::selectSubItems(QListViewItem* item, const QStringList& itemList, const bool state) +{ + QListViewItem* it_v; + + for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox && itemList.contains(it_c->id())) { + it_c->setOn(state); + } + selectSubItems(it_v, itemList, state); + } + } +} + +void KMyMoneySelector::selectAllSubItems(QListViewItem* item, const bool state) +{ + QListViewItem* it_v; + + for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + QCheckListItem* it_c = dynamic_cast<QCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + it_c->setOn(state); + } + selectAllSubItems(it_v, state); + } + } +} + +void KMyMoneySelector::selectedItems(QStringList& list) const +{ + QListViewItem* it_v; + + list.clear(); + if(m_selMode == QListView::Single) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(m_listView->selectedItem()); + if(it_c != 0) + list << it_c->id(); + + } else { + for(it_v = m_listView->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + if(it_c->isOn()) + list << (*it_c).id(); + } + selectedItems(list, it_v); + } + } + } +} + +void KMyMoneySelector::selectedItems(QStringList& list, QListViewItem* item) const +{ + QListViewItem* it_v; + + for(it_v = item->firstChild(); it_v != 0; it_v = it_v->nextSibling()) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + if(it_c->isOn()) + list << (*it_c).id(); + selectedItems(list, it_v); + } + } + } +} + +void KMyMoneySelector::itemList(QStringList& list) const +{ + QListViewItemIterator it; + QListViewItem* it_v; + + it = QListViewItemIterator(m_listView, QListViewItemIterator::Selectable); + while((it_v = it.current()) != 0) { + { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + list << it_c->id(); + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + list << it_c->id(); + } + } + it++; + } +} + +void KMyMoneySelector::setSelected(const QString& id, const bool state) +{ + QListViewItemIterator it; + QListViewItem* it_v; + QListViewItem* it_visible = 0; + + it = QListViewItemIterator(m_listView, QListViewItemIterator::Selectable); + while((it_v = it.current()) != 0) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + Q_CHECK_PTR(it_c); + if(it_c->type() == QCheckListItem::CheckBox) { + if(it_c->id() == id) { + it_c->setOn(state); + m_listView->setSelected(it_v, true); + if(!it_visible) + it_visible = it_v; + } + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + Q_CHECK_PTR(it_c); + if(it_c->id() == id) { + m_listView->setSelected(it_v, true); + if(!it_visible) + it_visible = it_v; + ensureItemVisible(it_v); + return; + } + } + it++; + } + + // make sure the first one found is visible + if(it_visible) + ensureItemVisible(it_visible); +} + +void KMyMoneySelector::ensureItemVisible(const QListViewItem *it_v) +{ + // for some reason, I could only use the ensureItemVisible() method + // of QListView successfully, after the widget was drawn on the screen. + // If called before it had no effect (if the item was not visible). + // + // The solution was to store the item we wanted to see in a local var + // and call QListView::ensureItemVisible() about 10ms later in + // the slot slotShowSelected. (ipwizard, 12/29/2003) + m_visibleItem = it_v; + + QTimer::singleShot(100, this, SLOT(slotShowSelected())); +} + +void KMyMoneySelector::slotShowSelected(void) +{ + if(m_listView && m_visibleItem) + m_listView->ensureItemVisible(m_visibleItem); +} + +int KMyMoneySelector::slotMakeCompletion(const QString& _txt) +{ + QString txt(QRegExp::escape(_txt)); + if(KMyMoneyGlobalSettings::stringMatchFromStart() && this->isA("KMyMoneySelector") ) + txt.prepend('^'); + return slotMakeCompletion(QRegExp(txt, false)); +} + +bool KMyMoneySelector::match(const QRegExp& exp, QListViewItem* item) const +{ + return exp.search(item->text(0)) != -1; +} + +int KMyMoneySelector::slotMakeCompletion(const QRegExp& exp) +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + + QListViewItem* it_v; + + // The logic used here seems to be awkward. The problem is, that + // QListViewItem::setVisible works recursively on all it's children + // and grand-children. + // + // The way out of this is as follows: Make all items visible. + // Then go through the list again and perform the checks. + // If an item does not have any children (last leaf in the tree view) + // perform the check. Then check recursively on the parent of this + // leaf that it has no visible children. If that is the case, make the + // parent invisible and continue this check with it's parent. + while((it_v = it.current()) != 0) { + it_v->setVisible(true); + ++it; + } + + QListViewItem* firstMatch = 0; + + if(!exp.pattern().isEmpty()) { + it = QListViewItemIterator(m_listView, QListViewItemIterator::Selectable); + while((it_v = it.current()) != 0) { + if(it_v->firstChild() == 0) { + if(!match(exp, it_v)) { + // this is a node which does not contain the + // text and does not have children. So we can + // safely hide it. Then we check, if the parent + // has more children which are still visible. If + // none are found, the parent node is hidden also. We + // continue until the top of the tree or until we + // find a node that still has visible children. + bool hide = true; + while(hide) { + it_v->setVisible(false); + it_v = it_v->parent(); + if(it_v && it_v->isSelectable()) { + hide = !match(exp, it_v); + QListViewItem* child = it_v->firstChild(); + for(; child && hide; child = child->nextSibling()) { + if(child->isVisible()) + hide = false; + } + } else + hide = false; + } + } else if(!firstMatch) { + firstMatch = it_v; + } + ++it; + + } else if(match(exp, it_v)) { + if(!firstMatch) { + firstMatch = it_v; + } + // a node with children contains the text. We want + // to display all child nodes in this case, so we need + // to advance the iterator to the next sibling of the + // current node. This could well be the sibling of a + // parent or grandparent node. + QListViewItem* curr = it_v; + QListViewItem* item; + while((item = curr->nextSibling()) == 0) { + curr = curr->parent(); + if(curr == 0) + break; + if(match(exp, curr)) + firstMatch = curr; + } + do { + ++it; + } while(it.current() && it.current() != item); + + } else { + // It's a node with children that does not match. We don't + // change it's status here. + ++it; + } + } + } + + // make the first match the one that is selected + // if we have no match, make sure none is selected + if(m_selMode == QListView::Single) { + if(firstMatch) { + m_listView->setSelected(firstMatch, true); + ensureItemVisible(firstMatch); + } else + m_listView->selectAll(false); + } + + // Get the number of visible nodes for the return code + int cnt = 0; + + it = QListViewItemIterator(m_listView, QListViewItemIterator::Selectable | QListViewItemIterator::Visible); + while((it_v = it.current()) != 0) { + cnt++; + it++; + } + return cnt; +} + +bool KMyMoneySelector::contains(const QString& txt) const +{ + QListViewItemIterator it(m_listView, QListViewItemIterator::Selectable); + QListViewItem* it_v; + while((it_v = it.current()) != 0) { + if(it_v->rtti() == 1) { + KMyMoneyCheckListItem* it_c = dynamic_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->text() == txt) { + return true; + } + } else if(it_v->rtti() == 0) { + KMyMoneyListViewItem* it_c = dynamic_cast<KMyMoneyListViewItem*>(it_v); + if(it_c->text(0) == txt) { + return true; + } + } + it++; + } + return false; +} + +void KMyMoneySelector::slotListRightMouse(QListViewItem* it_v, const QPoint& pos, int /* col */) +{ + if(it_v && (it_v->rtti() == 1)) { + KMyMoneyCheckListItem* it_c = static_cast<KMyMoneyCheckListItem*>(it_v); + if(it_c->type() == QCheckListItem::CheckBox) { + // the following is copied from QCheckListItem::activate() et al + int boxsize = m_listView->style().pixelMetric(QStyle::PM_CheckListButtonSize, m_listView); + int align = m_listView->columnAlignment( 0 ); + int marg = m_listView->itemMargin(); + int y = 0; + + if ( align & AlignVCenter ) + y = ( ( height() - boxsize ) / 2 ) + marg; + else + y = (m_listView->fontMetrics().height() + 2 + marg - boxsize) / 2; + + QRect r( 0, y, boxsize-3, boxsize-3 ); + // columns might have been swapped + r.moveBy( m_listView->header()->sectionPos( 0 ), 0 ); + + QPoint topLeft = m_listView->itemRect(it_v).topLeft(); //### inefficient? + QPoint p = m_listView->mapFromGlobal( pos ) - topLeft; + + int xdepth = m_listView->treeStepSize() * (it_v->depth() + (m_listView->rootIsDecorated() ? 1 : 0)) + + m_listView->itemMargin(); + xdepth += m_listView->header()->sectionPos( m_listView->header()->mapToSection( 0 ) ); + p.rx() -= xdepth; + // copy ends around here + + if ( r.contains( p ) ) { + // we get down here, if we have a right click onto the checkbox + selectAllSubItems(it_c, it_c->isOn()); + } + } + } +} + +QStringList KMyMoneySelector::selectedItems(void) const +{ + QStringList list; + selectedItems(list); + return list; +} + +QStringList KMyMoneySelector::itemList(void) const +{ + QStringList list; + itemList(list); + return list; +} + +#include "kmymoneyselector.moc" diff --git a/kmymoney2/widgets/kmymoneyselector.h b/kmymoney2/widgets/kmymoneyselector.h new file mode 100644 index 0000000..2b389d6 --- /dev/null +++ b/kmymoney2/widgets/kmymoneyselector.h @@ -0,0 +1,387 @@ +/*************************************************************************** + kmymoneyselector.h + ------------------- + begin : Thu Jun 29 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 KMYMONEYSELECTOR_H +#define KMYMONEYSELECTOR_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qwidget.h> +#include <qlistview.h> +class QHBoxLayout; + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KListView; + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyutils.h> + +/** + * This class implements a general selector for id based objects. It is based + * on a tree view. Using this widget, one can select one or multiple + * items depending on the mode of operation and the set of items + * selected to be displayed. (see setSelectionMode() ). + * + * - Single selection mode\n + * In this mode the widget allows to select a single entry out of + * the set of displayed items. + * + * - Multi selection mode\n + * In this mode, the widget allows to select one or more entries + * out of the set of displayed items. Selection is performed + * by marking the item in the view. + */ +class KMyMoneySelector : public QWidget +{ + Q_OBJECT +public: + KMyMoneySelector(QWidget *parent=0, const char *name=0, QWidget::WFlags flags = 0); + virtual ~KMyMoneySelector(); + + /** + * This method sets the mode of operation of this widget. + * Supported values are @p QListView::Single and @p QListView::Multi. + * + * @param mode @p QListView::Single selects single selection mode and + * @p QListView::Multi selects multi selection mode + * + * @note When the widget is created, it defaults to QListView::Single. + * Any items loaded into the widget will be cleared if the mode changes. + * Changing the selection mode also changes the type of the items + * created through newItem(). You should therefor set the selection mode + * before you create items. + */ + void setSelectionMode(const QListView::SelectionMode mode); + + /** + * returns the selection mode of the widget. + * + * @sa setSelectionMode() + */ + QListView::SelectionMode selectionMode(void) const { return m_selMode; } + + /** + * This method returns the list of selected item ids. If + * no item is selected, the list is empty. The list is cleared + * when the method is called. + * + * @param list reference to id list + */ + void selectedItems(QStringList& list) const; + + /** + * Convenience method for above method. Requires more resources. + * Provided only for backward compatibility. + * + * @todo Deprecated after 1.0 + */ + QStringList selectedItems(void) const; + + /** + * This method returns the list of all item ids. + * The list is cleared when the method is called. + * + * @param list reference to id list + */ + void itemList(QStringList& list) const; + + /** + * Convenience method for above method. Requires more resources. + * Provided only for backward compatibility. + * + * @todo Deprecated after 1.0 + */ + QStringList itemList(void) const; + + /** + * This method returns an information if all items + * currently shown are selected or not. + * + * @retval true All items shown are selected + * @retval false Not all items are selected + * + * @note If the selection mode is set to Single, this + * method always returns false. + */ + bool allItemsSelected(void) const; + + /** + * This method sets the current selected item and marks the + * checkbox according to @p state in multi-selection-mode. + * + * @param id id of account + * @param state state of checkbox in multi-selection-mode + * @p true checked + * @p false not checked (default) + */ + void setSelected(const QString& id, const bool state = false); + + /** + * Return a pointer to the KListView object + */ + KListView* listView(void) const { return m_listView; }; + + /** + * This method selects/deselects all items that + * are currently in the item list according + * to the parameter @p state. + * + * @param state select items if @p true, deselect otherwise + */ + void selectAllItems(const bool state); + + /** + * This method selects/deselects all items that + * are currently in this object's item list AND are present in the supplied + * @p itemList of items to select, according to the @p state. + * + * @param itemList of item ids to apply @p state to + * @param state select items if @p true, deselect otherwise + */ + void selectItems(const QStringList& itemList, const bool state); + + /** + * Protect an entry from selection. Protection is controlled by + * the parameter @p protect. + * + * @param itemId id of item for which to modify the protection + * @param protect if true, the entry specified by @p accId cannot be + * selected. If false, it can be selected. Defaults to @p true. + */ + void protectItem(const QString& itemId, const bool protect = true); + + /** + * This method modifies the width of the widget to match its optimal size + * so that all entries fit completely. + */ + void setOptimizedWidth(void); + + /** + * This method removes an item with a given id from the list. + * + * @param id QString containing id of item to be removed + */ + void removeItem(const QString& id); + + /** + * This method creates a new top level KMyMoneyCheckListItem object in the list view. + * The type can be influenced with the @a type argument. It defaults + * to QCheckListItem::RadioButtonController. If @a id is empty, the item is not + * selectable. It will be shown 'opened' (see QListViewItem::setOpen()) + * + * @return pointer to newly created object + */ + QListViewItem* newItem(const QString& name, const QString& key = QString(), const QString& id = QString(), QCheckListItem::Type type = QCheckListItem::RadioButtonController); + + /** + * Same as above, but create the item following the item pointed to by @c after. + * If @c after is 0, then behave as previous method + */ + QListViewItem* newItem(const QString& name, QListViewItem* after, const QString& key = QString(), const QString& id = QString(), QCheckListItem::Type type = QCheckListItem::RadioButtonController); + + /** + * This method creates a new selectable object depending on the + * selection mode. This is either a KListViewItem for single + * selection mode or a KMyMoneyCheckListItem for multi selection mode + * + * @note The new item will be the first one in the selection + * + * @param parent pointer to parent item + * @param name the displayed name + * @param key String to be used for completion. If empty defaults to @a name + * @param id the id used to identify the objects + * + * @return pointer to newly created object + */ + QListViewItem* newItem(QListViewItem* parent, const QString& name, const QString& key, const QString& id); + + /** + * This method creates a new selectable object depending on the + * selection mode. This is either a KListViewItem for single + * selection mode or a KMyMoneyCheckListItem for multi selection mode. + * In contrast to the above method, the parent is always the view. + * + * @note The new item will be the first one in the selection + * + * @param name the displayed name + * @param key String to be used for completion. If empty defaults to @a name + * @param id the id used to identify the objects + * + * @return pointer to newly created object + */ + QListViewItem* newTopItem(const QString& name, const QString& key, const QString& id); + + /** + * This method checks if a given @a item matches the given regular expression @a exp. + * + * @param exp const reference to a regular expression object + * @param item pointer to QListViewItem + * + * @retval true item matches + * @retval false item does not match + */ + virtual bool match(const QRegExp& exp, QListViewItem* item) const; + + /** + * This method delays the call for m_listView->ensureItemVisible(item) + * for about 10ms. This seems to be necessary when the widget is not (yet) + * visible on the screen after creation. + * + * @param item pointer to QListViewItem that should be made visible + * + * @sa slotShowSelected() + */ + void ensureItemVisible(const QListViewItem *item); + + /** + * This method returns a pointer to the QListViewItem with the id @a id. + * If such an item is not contained in the list, @a 0 will be returned. + * + * @param id id to be used to find a QListViewItem pointer for + */ + QListViewItem* item(const QString& id) const; + + /** + * This method returns, if any of the items in the selector contains + * the text @a txt. + * + * @param txt const reference to string to be looked for + * @retval true exact match found + * @retval false no match found + */ + virtual bool contains(const QString& txt) const; + + /** + * Clears all items of the selector and the associated listview. + */ + virtual void clear(void); + + /** + * This method returns the optimal width for the widget + */ + int optimizedWidth(void) const; + +public slots: + /** + * This slot selects all items that are currently in + * the item list of the widget. + */ + void slotSelectAllItems(void) { selectAllItems(true); }; + + /** + * This slot deselects all items that are currently in + * the item list of the widget. + */ + void slotDeselectAllItems(void) { selectAllItems(false); }; + +signals: + void stateChanged(void); + + void itemSelected(const QString& id); + +protected: + /** + * Helper method for selectedItems() to traverse the tree. + * + * @param list list of selected ids + * @param item pointer to item to start with + */ + void selectedItems(QStringList& list, QListViewItem* item) const; + + /** + * Helper method for allItemsSelected() to traverse the tree. + * + * @param item pointer to item to start with + */ + bool allItemsSelected(const QListViewItem *item) const; + + /** + * This is a helper method for selectAllItems(). + * + * @param item pointer to item to start with + * @param state selection state (@a true = selected, @a false = not selected) + */ + void selectAllSubItems(QListViewItem* item, const bool state); + + /** + * This is a helper method for selectItems(). + * + * @param item pointer to item to start with + * @param itemList list of ids to be selected + * @param state selection state (@a true = selected, @a false = not selected) + */ + void selectSubItems(QListViewItem* item, const QStringList& itemList, const bool state); + +public slots: + /** + * Hide all listview items that do not match the regular expression @a exp. + * This method returns the number of visible items + * + * @param exp const reference to QRegExp that an item must match to stay visible + * + * @return number of visible items + */ + int slotMakeCompletion(const QRegExp& exp); + + /** + * This is an overloaded member function, provided for convenience. It behaves essentially like the above function. + * + * @param txt contains the pattern for a QRegExp + */ + int slotMakeCompletion(const QString& txt); + + +protected slots: + /** + * This slot is usually connected to a timer signal and simply + * calls m_listView->ensureItemVisible() for the last selected item + * in this widget. + * + * @sa ensureItemVisible(), setSelected(const QString&) + */ + void slotShowSelected(void); + + /** + * This slot is connected to the KListView executed signal + */ + void slotItemSelected(QListViewItem *it_v); + + /** + * This slot processes the right mouse button press on a list view item. + * + * @param it_v pointer to list view item that was pressed + * @param p the position where the mouse was pressed + */ + void slotListRightMouse(QListViewItem* it_v, const QPoint& p, int /* col */); + +protected: + KListView* m_listView; + QStringList m_itemList; + QString m_baseName; + QListView::SelectionMode m_selMode; + QHBoxLayout* m_layout; + +private: + const QListViewItem* m_visibleItem; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneytitlelabel.cpp b/kmymoney2/widgets/kmymoneytitlelabel.cpp new file mode 100644 index 0000000..9dc5140 --- /dev/null +++ b/kmymoney2/widgets/kmymoneytitlelabel.cpp @@ -0,0 +1,104 @@ +/*************************************************************************** + kmymoneytitlelabel.cpp + ------------------- + begin : Sun Feb 05 2005 + copyright : (C) 2005 by Ace Jones + email : acejones@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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpixmap.h> +#include <qvariant.h> +#include <qstyle.h> +#include <qpainter.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kglobalsettings.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "kmymoneytitlelabel.h" + +KMyMoneyTitleLabel::KMyMoneyTitleLabel(QWidget *parent, const char *name) : + QLabel(parent, name), + m_bgColor( KGlobalSettings::highlightColor() ), + m_textColor( KGlobalSettings::highlightedTextColor() ) +{ + setFont(KGlobalSettings::windowTitleFont()); +} + +KMyMoneyTitleLabel::~KMyMoneyTitleLabel() +{ +} + +void KMyMoneyTitleLabel::setLeftImageFile(const QString& _file) +{ + m_leftImageFile = _file; + QString lfullpath = KGlobal::dirs()->findResource("appdata", m_leftImageFile); + m_leftImage.load(lfullpath); + m_leftImage.setAlphaBuffer(true); +} + +void KMyMoneyTitleLabel::setRightImageFile(const QString& _file) +{ + m_rightImageFile = _file; + QString rfullpath = KGlobal::dirs()->findResource("appdata", m_rightImageFile); + m_rightImage.load(rfullpath); + m_rightImage.setAlphaBuffer(true); + if(m_rightImage.height() < 30) + setMinimumHeight(30); + else { + setMinimumHeight( m_rightImage.height() ); + setMaximumHeight( m_rightImage.height() ); + } +} + +void KMyMoneyTitleLabel::resizeEvent ( QResizeEvent * ) +{ + QRect cr = contentsRect(); + QImage output( cr.width(), cr.height(), 32 ); + output.fill( m_bgColor.rgb() ); + + bitBlt ( &output, cr.width() - m_rightImage.width(), 0, &m_rightImage, 0, 0, m_rightImage.width(), m_rightImage.height(), 0 ); + bitBlt ( &output, 0, 0, &m_leftImage, 0, 0, m_leftImage.width(), m_leftImage.height(), 0 ); + + QPixmap pix; + pix.convertFromImage(output); + setPixmap(pix); + setMinimumWidth( m_rightImage.width() ); +} + +void KMyMoneyTitleLabel::drawContents(QPainter *p) +{ + // first draw pixmap + QLabel::drawContents(p); + + // then draw text on top + style().drawItem( p, contentsRect(), alignment(), colorGroup(), isEnabled(), + 0, QString(" ")+m_text, -1, &m_textColor ); +} + +void KMyMoneyTitleLabel::setText(const QString& txt) +{ + m_text = txt; + update(); +} + +#include "kmymoneytitlelabel.moc" diff --git a/kmymoney2/widgets/kmymoneytitlelabel.h b/kmymoney2/widgets/kmymoneytitlelabel.h new file mode 100644 index 0000000..08f9302 --- /dev/null +++ b/kmymoney2/widgets/kmymoneytitlelabel.h @@ -0,0 +1,78 @@ +/*************************************************************************** + kmymoneytitlelabel.h + ------------------- + begin : Sun Feb 05 2005 + copyright : (C) 2005 by Ace Jones + email : acejones@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 KTITLELABEL_H +#define KTITLELABEL_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlabel.h> +#include <qimage.h> +#include <qcolor.h> +class QPixmap; + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + * @author ace jones + */ +class KMyMoneyTitleLabel : public QLabel +{ + Q_OBJECT + Q_PROPERTY( QString leftImageFile READ leftImageFile WRITE setLeftImageFile DESIGNABLE true ) + Q_PROPERTY( QString rightImageFile READ rightImageFile WRITE setRightImageFile DESIGNABLE true ) + Q_PROPERTY( QColor bgColor READ bgColor WRITE setBgColor DESIGNABLE true ) + Q_PROPERTY( QString text READ text WRITE setText DESIGNABLE true ) + +public: + KMyMoneyTitleLabel(QWidget *parent = 0, const char *name = 0); + ~KMyMoneyTitleLabel(); + + void setBgColor(const QColor& _color) { m_bgColor = _color; } + void setLeftImageFile(const QString& _file); + void setRightImageFile(const QString& _file); + + const QString& leftImageFile(void) const { return m_leftImageFile; } + const QString& rightImageFile(void) const { return m_rightImageFile; } + QColor bgColor(void) const { return m_bgColor; } + QString text(void) const { return m_text; } + +public slots: + virtual void setText(const QString& txt); + +protected: + void updatePixmap(void); + virtual void resizeEvent ( QResizeEvent * ); + void drawContents(QPainter *); + +private: + QImage m_leftImage; + QImage m_rightImage; + QColor m_bgColor; + QColor m_textColor; + QString m_text; + + QString m_leftImageFile; + QString m_rightImageFile; +}; + +#endif diff --git a/kmymoney2/widgets/kmymoneywizard.cpp b/kmymoney2/widgets/kmymoneywizard.cpp new file mode 100644 index 0000000..d7fcfdb --- /dev/null +++ b/kmymoney2/widgets/kmymoneywizard.cpp @@ -0,0 +1,376 @@ +/*************************************************************************** + kmymoneywizard.cpp + ------------------- + copyright : (C) 2006 by Thomas Baumagrt + 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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qlayout.h> +#include <qlabel.h> +#include <qpoint.h> +#include <qfont.h> +#include <qframe.h> +#include <qtooltip.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kapplication.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneywizard.h> +#include "kmymoneywizard_p.h" +#include <kmymoney/kmymoneytitlelabel.h> +#include <kmymoney/kguiutils.h> + +KMyMoneyWizardPagePrivate::KMyMoneyWizardPagePrivate(QObject* parent, const char* name) : + QObject(parent, name) +{ +} + +void KMyMoneyWizardPagePrivate::emitCompleteStateChanged(void) +{ + emit completeStateChanged(); +} + + +KMyMoneyWizardPage::KMyMoneyWizardPage(unsigned int step, QWidget* widget, const char* name) : + m_step(step), + m_widget(widget), + d(new KMyMoneyWizardPagePrivate(widget, name)) +{ + m_mandatoryGroup = new kMandatoryFieldGroup(widget); + QObject::connect(m_mandatoryGroup, SIGNAL(stateChanged()), object(), SIGNAL(completeStateChanged())); + widget->hide(); +} + +QObject* KMyMoneyWizardPage::object(void) const +{ + return d; +} + +void KMyMoneyWizardPage::completeStateChanged(void) const +{ + d->emitCompleteStateChanged(); +} + +void KMyMoneyWizardPage::resetPage(void) +{ +} + +void KMyMoneyWizardPage::enterPage(void) +{ +} + +void KMyMoneyWizardPage::leavePage(void) +{ +} + +KMyMoneyWizardPage* KMyMoneyWizardPage::nextPage(void) const +{ + return 0; +} + +bool KMyMoneyWizardPage::isLastPage(void) const +{ + return nextPage() == 0; +} + +bool KMyMoneyWizardPage::isComplete(void) const +{ + if(!isLastPage()) + QToolTip::add(wizard()->m_nextButton, i18n("Continue with next page")); + else + QToolTip::add(wizard()->m_finishButton, i18n("Finish wizard")); + return m_mandatoryGroup->isEnabled(); +} + +const QString& KMyMoneyWizardPage::helpContext(void) const +{ + return QString::null; +} + +KMyMoneyWizard::KMyMoneyWizard(QWidget *parent, const char *name, bool modal, WFlags f) : + QDialog(parent, name, modal, f), + m_step(0) +{ + // enable the little grip in the right corner + setSizeGripEnabled(true); + + // create buttons + m_cancelButton = new KPushButton(i18n("&Cancel"), this); + m_backButton = new KPushButton(i18n("&Back"), this); + m_nextButton = new KPushButton(i18n("&Next"), this); + m_finishButton = new KPushButton(i18n("&Finish"), this); + m_helpButton = new KPushButton(i18n("&Help"), this); + + if ( KGlobalSettings::showIconsOnPushButtons() ) + { + m_backButton->setIconSet( KStdGuiItem::back( KStdGuiItem::UseRTL ).iconSet() ); + m_nextButton->setIconSet( KStdGuiItem::forward( KStdGuiItem::UseRTL ).iconSet() ); + m_finishButton->setIconSet( SmallIconSet( "apply" ) ); + m_cancelButton->setIconSet( SmallIconSet( "button_cancel" ) ); + m_helpButton->setIconSet( SmallIconSet( "help" ) ); + } + + // create button layout + m_buttonLayout = new QHBoxLayout; + m_buttonLayout->addWidget(m_helpButton); + m_buttonLayout->addStretch(1); + m_buttonLayout->addWidget(m_backButton); + m_buttonLayout->addWidget(m_nextButton); + m_buttonLayout->addWidget(m_finishButton); + m_buttonLayout->addWidget(m_cancelButton); + + // create wizard layout + m_wizardLayout = new QVBoxLayout(this, 6, 0, "wizardLayout"); + m_titleLabel = new KMyMoneyTitleLabel(this, "titleLabel"); + m_wizardLayout->addWidget(m_titleLabel); + + QHBoxLayout* hboxLayout = new QHBoxLayout(0, 0, 6, "hboxLayout"); + + // create stage layout and frame + m_stepFrame = new QFrame(this, "stepFrame"); + m_stepFrame->setPaletteBackgroundColor(KGlobalSettings::highlightColor()); + m_stepLayout = new QVBoxLayout(m_stepFrame, 11, 6, "stepLayout"); + m_stepLayout->addWidget(new QLabel("", m_stepFrame)); + m_stepLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + m_stepLabel = new QLabel(m_stepFrame, "stepLabel"); + m_stepLabel->setAlignment(Qt::AlignHCenter); + m_stepLayout->addWidget(m_stepLabel); + hboxLayout->addWidget(m_stepFrame); + + // FIXME use the protected virtual method QWidget::paletteChange() to update the palette + // information when the user selected a different color set using the KConfigCenter + m_stepPalette = m_stepLabel->palette(); + QColorGroup::ColorRole role = QColorGroup::Foreground; + QColor color = KGlobalSettings::highlightedTextColor(); + m_stepPalette.setColor( QPalette::Active, role, color ); + m_stepPalette.setColor( QPalette::Inactive, role, color ); + m_stepPalette.setColor( QPalette::Disabled, role, color ); + m_stepLabel->setPalette(m_stepPalette); + + // create page layout + m_pageLayout = new QVBoxLayout(0, 0, 6, "pageLayout"); + + // the page will be inserted later dynamically above this line + QFrame* line = new QFrame( this, "line" ); + line->setFrameShadow( QFrame::Sunken ); + line->setFrameShape( QFrame::HLine ); + m_pageLayout->addWidget( line ); + m_pageLayout->addLayout(m_buttonLayout); + + // now glue everything together + hboxLayout->addLayout(m_pageLayout); + m_wizardLayout->addLayout(hboxLayout); + + resize(QSize(770, 520).expandedTo(minimumSizeHint())); + clearWState(WState_Polished); + + m_titleLabel->setText("No Title specified"); + m_titleLabel->setRightImageFile("pics/titlelabel_background.png"); + + m_finishButton->hide(); + + connect(m_backButton, SIGNAL(clicked()), this, SLOT(backButtonClicked())); + connect(m_nextButton, SIGNAL(clicked()), this, SLOT(nextButtonClicked())); + connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + connect(m_finishButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_helpButton, SIGNAL(clicked()), this, SLOT(helpButtonClicked())); +} + +void KMyMoneyWizard::setTitle(const QString& txt) +{ + m_titleLabel->setText(txt); +} + +void KMyMoneyWizard::addStep(const QString& text) +{ + QLabel* step = new QLabel(text, m_stepFrame); + step->setFrameStyle(QFrame::Panel | QFrame::Raised); + step->setAlignment(Qt::AlignHCenter); + step->setFrameStyle(QFrame::Box | QFrame::Sunken); + step->setMargin(2); + step->setPalette( m_stepPalette ); + + m_steps.append(step); + m_stepLayout->insertWidget(m_steps.count(), step); + + QFont font(step->font()); + font.setBold(true); + QFontMetrics fm(font); + int w = fm.width(text)+30; + if(m_stepFrame->minimumWidth() < w) { + m_stepFrame->setMinimumWidth(w); + } +} + +void KMyMoneyWizard::setStepHidden(unsigned int step, bool hidden) +{ + if((step < 1) || (step > m_steps.count())) + return; + + m_steps[--step]->setHidden(hidden); + updateStepCount(); +} + +void KMyMoneyWizard::selectStep(unsigned int step) +{ + if((step < 1) || (step > m_steps.count())) + return; + + m_step = step; + QValueList<QLabel*>::iterator it_l; + QFont f = m_steps[0]->font(); + for(it_l = m_steps.begin(); it_l != m_steps.end(); ++it_l) { + f.setBold(false); + (*it_l)->setFrameStyle(QFrame::NoFrame); + if(--step == 0) { + f.setBold(true); + (*it_l)->setFrameStyle(QFrame::Box | QFrame::Sunken); + } + (*it_l)->setFont(f); + } + updateStepCount(); +} + +void KMyMoneyWizard::reselectStep(void) +{ + selectStep(m_step); +} + +void KMyMoneyWizard::updateStepCount(void) +{ + QValueList<QLabel*>::iterator it_l; + int stepCount = 0; + int hiddenAdjust = 0; + int step = 0; + for(it_l = m_steps.begin(); it_l != m_steps.end(); ++it_l) { + if(!(*it_l)->isHidden()) + ++stepCount; + else if(step < m_step) + hiddenAdjust++; + ++step; + } + m_stepLabel->setText(i18n("Step %1 of %2").arg(m_step - hiddenAdjust).arg(stepCount)); +} + +void KMyMoneyWizard::setFirstPage(KMyMoneyWizardPage* page) +{ + page->resetPage(); + m_history.clear(); + m_history.append(page); + switchPage(0); +} + +void KMyMoneyWizard::switchPage(KMyMoneyWizardPage* oldPage) +{ + if(oldPage) { + oldPage->widget()->hide(); + m_pageLayout->remove(oldPage->widget()); + disconnect(oldPage->object(), SIGNAL(completeStateChanged()), this, SLOT(completeStateChanged())); + } + KMyMoneyWizardPage* newPage = m_history.back(); + if(newPage) { + m_pageLayout->insertWidget(0, newPage->widget()); + connect(newPage->object(), SIGNAL(completeStateChanged()), this, SLOT(completeStateChanged())); + newPage->widget()->show(); + selectStep(newPage->step()); + if(newPage->isLastPage()) { + m_nextButton->setDefault(false); + m_finishButton->setDefault(true); + } else { + m_finishButton->setDefault(false); + m_nextButton->setDefault(true); + } + QWidget* w = newPage->initialFocusWidget(); + if(w) + w->setFocus(); + } + completeStateChanged(); +} + +void KMyMoneyWizard::backButtonClicked(void) +{ + KMyMoneyWizardPage* oldPage = m_history.back(); + m_history.pop_back(); + oldPage->leavePage(); + oldPage->resetPage(); + switchPage(oldPage); +} + +void KMyMoneyWizard::nextButtonClicked(void) +{ + // make sure it is really complete. Some widgets only change state during focusOutEvent, + // so we just create such an animal by changing the focus to the next button and + // check again for copmpleness + m_nextButton->setFocus(); + KMyMoneyWizardPage* oldPage = m_history.back(); + if(oldPage->isComplete()) { + KMyMoneyWizardPage* newPage = oldPage->nextPage(); + m_history.append(newPage); + newPage->enterPage(); + newPage->resetPage(); + switchPage(oldPage); + } +} + +void KMyMoneyWizard::helpButtonClicked(void) +{ + KMyMoneyWizardPage* currentPage = m_history.back(); + QString ctx = currentPage->helpContext(); + if(ctx.isEmpty()) + ctx = m_helpContext; + kapp->invokeHelp(ctx); +} + +void KMyMoneyWizard::completeStateChanged(void) +{ + KMyMoneyWizardPage* currentPage = m_history.back(); + bool lastPage = currentPage->isLastPage(); + + m_finishButton->setShown(lastPage); + m_nextButton->setShown(!lastPage); + + KPushButton* button; + + button = lastPage ? m_finishButton : m_nextButton; + + bool rc = currentPage->isComplete(); + button->setEnabled(rc); + + m_backButton->setEnabled(m_history.count() > 1); +} + +void KMyMoneyWizard::accept(void) +{ + // make sure it is really complete. Some widgets only change state during focusOutEvent, + // so we just create such an animal by changing the focus to the finish button and + // check again for completeness. + m_finishButton->setFocus(); + KMyMoneyWizardPage* page = m_history.back(); + if(page->isComplete()) + QDialog::accept(); +} + +#include "kmymoneywizard.moc" + diff --git a/kmymoney2/widgets/kmymoneywizard.h b/kmymoney2/widgets/kmymoneywizard.h new file mode 100644 index 0000000..21b57b4 --- /dev/null +++ b/kmymoney2/widgets/kmymoneywizard.h @@ -0,0 +1,539 @@ +/*************************************************************************** + kmymoneywizard.h + ------------------- + copyright : (C) 2006 by Thomas Baumagrt + 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 KMYMONEYWIZARD_H +#define KMYMONEYWIZARD_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qdialog.h> +#include <qvaluelist.h> +#include <qpalette.h> +class QVBoxLayout; +class QHBoxLayout; +class QLabel; +class QFrame; + +// ---------------------------------------------------------------------------- +// KDE Includes + +class KPushButton; + +// ---------------------------------------------------------------------------- +// Project Includes + +class KMyMoneyTitleLabel; +class KMyMoneyWizard; +class KMyMoneyWizardPagePrivate; +class kMandatoryFieldGroup; + +/** + * @author Thomas Baumgart (C) 2006 + * + * @note: the following documentation is somewhat outdated + * as of May 2007. Wizards should use a namespace + * for the pages and can use the WizardPage<T> template class. + * See the NewUserWizard class and NewUserWizardPages namespace + * as an example of this setup. + * + * This class represents the base class for wizard pages for the + * KMyMoneyWizard. One cannot create a wizard page directly, but + * must derive from it. The KMyMoneyWizardPage class provides the + * necessary functionality to work in concert with KMyMoneyWizard. + * + * Therefore, few steps are necessary to use this class. They seem to + * be awkward at some stage, but I wanted to be able to use Qt designer + * to actually design the widget for the page. That's why some things + * aren't implemented in a more straight fashion than one would + * normally do this. + * + * The first step is to derive a specific base page for the specific wizard. + * In this example we use the name NewUser as template for the specific wizard. + * This class provides a 'back'-pointer to the actual wizard object + * for all pages. + * + * @code + * class KNewUserPage : public KMyMoneyWizardPage + * { + * public: + * KNewUserPage(unsigned int step, QWidget* widget, KNewUserWizard* parent, const char* name); + * + * protected: + * KNewUserWizard* m_wizard; + * } + * @endcode + * + * The implementation of this class is rather straight-forward: + * + * @code + * KNewUserPage::KNewUserPage(unsigned int step, QWidget* widget, KNewUserWizard* parent, const char* name) : + * KMyMoneyWizardPage(step, widget, name), + * m_wizard(parent) + * { + * } + * @endcode + * + * For each page of the wizard, you will have to create a @p ui file with + * Qt designer. + * Let's assume we call the first page of the wizard 'General' and go + * from there. + * We also assume, that the wizard has more than one page. + * The ui designer generated class should have the name KNewUserGeneralDecl + * as all other dialogs. The class definition of KNewUserGeneral will + * look like this: + * + * @code + * class KNewUserGeneral : public KNewUserGeneralDecl, public KNewUserPage + * { + * Q_OBJECT + * public: + * KNewUserGeneral(KNewUserWizard* parent, const char* name = 0); + * KMyMoneyWizardPage* nextPage(void); + * bool isLastPage(void) { return false; } + * + * protected: + * KNewUserWizard* m_wizard; + * } + * @endcode + * + * The implementation depends heavily on the logic of your code. If you only + * fill some widgets, it could be as simple as: + * + * @code + * KNewUserGeneral::KNewUserGeneral(KNewUserWizard* parent, const char* name) : + * KNewUserGeneralDecl(parent), + * KNewUserPage(1, this, parent, name) + * { + * kMandatoryFieldGroup* mandatoryGroup = new kMandatoryFieldGroup(this); + * mandatoryGroup->add(m_userName); + * connect(m_mandatoryGroup, SIGNAL(stateChanged()), object(), SIGNAL(completeStateChanged())); + * } + * + * KMyMoneyWizardPage* KNewUserGeneral::nextPage(void) + * { + * return m_wizard->m_personalPage; + * } + * @endcode + * + * A note on the first parameter to KNewUserPage in the above example: it ties + * this page to be part of step 1 (see KMyMoneyWizard::addStep() for details). + * + * Depending on the actual logic of the page, you would want to override the + * following methods: resetPage, nextPage, isLastPage and isComplete. + * + * @note The implementation of this class is heavily based on ideas found at + * http://doc.trolltech.com/4.1/dialogs-complexwizard.html + */ +class KMyMoneyWizardPage +{ +public: + /** + * This method is called by the wizard when the page is entered from + * the previous page. The default implementation does nothing. + */ + virtual void enterPage(void); + + /** + * This method is called by the wizard when the page is left to return to + * the previous page. The default implementation does nothing. + */ + virtual void leavePage(void); + + /** + * This method is called by the wizard whenever a page is entered + * (either in forward or backward direction). The default + * implementation does nothing. + */ + virtual void resetPage(void); + + /** + * This method returns a pointer to the next page that should be + * shown when the user presses the 'Next' button. + * + * @return pointer to next wizard page + */ + virtual KMyMoneyWizardPage* nextPage(void) const; + + /** + * This returns, if the current page is the last page of the wizard. + * The default implementation returns @p false if nextPage() returns 0, + * @p true otherwise. + * + * @retval false more pages follow + * @retval true this is the last page of the wizard + */ + virtual bool isLastPage(void) const; + + /** + * This returns, if all necessary data for this page has been + * filled. It is used to enabled the 'Next' or 'Finish' button. + * The button is only enabled, if this method returns @p true, + * which is the default implementation. + * + * @retval false more data required from the user before we can proceed + * @retval true all data available, we allow to switch to the next page + */ + virtual bool isComplete(void) const; + + /** + * This method returns the step to which this page belongs. + * It is required by the KMyMoneyWizard and is not intended + * to be used by application code. + * + * @return step of wizard this page belongs to + */ + unsigned int step(void) const { return m_step; } + + /** + * This method returns a pointer to the widget of the page. + * It is required by the KMyMoneyWizard and is not intended + * to be used by application code. + * + * @return pointer to widget of page + */ + QWidget* widget(void) const { return m_widget; } + + /** + * This method returns a pointer to the QObject used for + * the signal/slot mechanism. + * It is required by the KMyMoneyWizard and can be used + * by application code for signal/slot connections as well. + * Other use is not foreseen. + */ + QObject* object(void) const; + + /** + * This method returns a pointer to the widget which should + * receive the focus when the page is opened. + * + * @return pointer to widget or 0 if none is to be selected + * The default implementation returns 0 + */ + virtual QWidget* initialFocusWidget(void) const { return 0; } + + virtual KMyMoneyWizard* wizard(void) const = 0; + + /** + * This method returns a specific help context for the page shown + * The default returns an empty string. + */ + virtual const QString& helpContext(void) const; + + virtual ~KMyMoneyWizardPage() {} +protected: + /** + * Constructor (kept protected, so that one cannot create such an object directly) + */ + KMyMoneyWizardPage(unsigned int step, QWidget* widget, const char* name = 0); + + /** + * This method must be called by the implementation when the + * data in the fields of the wizard change and the state of + * completeness changed. + * + * @note If you do not override isComplete() then there is no need + * to call this method. + */ + void completeStateChanged(void) const; + +protected: + kMandatoryFieldGroup* m_mandatoryGroup; + +private: + unsigned int m_step; + QWidget* m_widget; + KMyMoneyWizardPagePrivate* const d; +}; + + +/** + * The general base class for wizard pages + * + * @author Thomas Baumgart + */ +template <class T> + class WizardPage : public KMyMoneyWizardPage +{ +public: + WizardPage(unsigned int step, QWidget* widget, T* parent, const char* name) : + KMyMoneyWizardPage(step, widget, name), + m_wizard(parent), + m_wizardBase(parent) + { + } + virtual ~WizardPage() {} + virtual KMyMoneyWizard* wizard(void) const { return m_wizardBase; } + +protected: + T* m_wizard; + KMyMoneyWizard* m_wizardBase; +}; + + +/** + * @author Thomas Baumgart (C) 2006 + * + * This is a base class for implementation of the KMyMoneyWizard. It provides + * the following layout of a wizard: + * + * @code + * +-wizardLayout-----------------------------------------------+ + * | | + * +------------------------------------------------------------+ + * |+-stepLayout--++-------------------------------------------+| + * || ||+-pageLayout------------------------------+|| + * || ||| ||| + * || ||| ||| + * || ||| ||| + * || ||| ||| + * || ||| ||| + * || ||| ||| + * || ||+-----------------------------------------+|| + * || |||+-buttonLayout--------------------------+||| + * || |||| |||| + * || |||+---------------------------------------+||| + * || ||+-----------------------------------------+|| + * |+-------------++-------------------------------------------+| + * +------------------------------------------------------------+ + * @endcode + * + * The top bar is filled with a KMyMoneyTitleLabel as known from + * KMyMoney's views. To the left there is an area in the same color + * as the title bar showing the steps for this wizard. Each such step + * can consist of one or more wizard pages. At the bottom of this area + * the text "Step x of y" is shown and updated. To the right of this + * part, the actual wizard page is shown. At the bottom of the page + * the class inserts a standard button widget consisting of a Help, + * Back, Next/Finish and Cancel button. + * + * The wizard serves as container for the wizard pages. In order to access + * the data filled into the pages, one would have to provide getter methods. + * + * Here is an example how this object could be used. Please also see the + * example described with the KMyMoneyWizardPage class. + * + * @code + * + * class KNewUserGeneral; + * class KNewUserPersonal; + * + * class KNewUserWizard : public KMyMoneyWizard + * { + * Q_OBJECT + * public: + * KNewUserWizard(QWidget* parent = 0, const char* name = 0, bool modal = false, WFlags flags = 0); + * + * private: + * KNewUserGeneral* m_generalPage; + * KNewUserPersonal* m_personalPage; + * KNewUserFinal* m_finalPage; + * // add more pages here + * + * friend class KNewUserGeneral; + * friend class KNewUserPersonal; + * friend class KNewUserFinal; + * // add more pages here + * }; + * @endcode + * + * The implementation is also easy and looks like this: + * + * @code + * KNewUserWizard::KNewUserWizard(QWidget* parent, const char* name, bool modal, WFlags flags) : + * KMyMoneyWizard(parent, name, modal, flags) + * { + * setTitle("KMyMoney New User Setup"); + * addStep("General Data"); + * addStep("Personal Data"); + * addStep("Finish"); + * + * m_generalPage = new KNewUserGeneral(this); + * m_personalPage = new KNewUserPersonal(this); + * m_finalPage = new KNewUserFinal(this); + * + * setFirstPage(m_testPage1); + * } + * @endcode + * + * Don't forget to call setFirstPage() to get things started. + * + * The code to use this whole structure would then look something like this: + * + * @code + * KNewUserWizard* wizard = new KNewUserWizard(this, "NewUserWizard"); + * int rc = wizard->exec(); + * @endcode + * + * The return code of exec() is either @p QDialog::Accepted or + * @p QDialog::Rejected. + * + * @note The implementation of this class is heavily based on ideas found at + * http://doc.trolltech.com/4.1/dialogs-complexwizard.html + */ +class KMyMoneyWizard : public QDialog +{ + friend class KMyMoneyWizardPage; + + Q_OBJECT +public: + /** + * Modify the title of the wizard to be @p txt. + * + * @param txt The text that should be used as title + */ + void setTitle(const QString& txt); + + /** + * Add step @p text to the wizard + * + * @param text Text to be shown for this step + */ + void addStep(const QString& text); + + QValueList<KMyMoneyWizardPage*> historyPages(void) const { return m_history; } + + /** + * This method repeats selection of the current step in the + * step frame. + * This is used to allow changes made to showing and hiding + * pages to immediately to be reflected in the step frame + */ + void reselectStep(void); + + /** + * Setup a global help context for the wizard. It will be used whenever + * there is no specific help context available for the current page. + * + * @sa KMyMoneyWizardPage::helpContext() + */ + void setHelpContext(const QString& ctx) { m_helpContext = ctx; } + + virtual ~KMyMoneyWizard(){} + +signals: + /** + * This signal is sent out, when a new payee needs to be created + * @sa KMyMoneyCombo::createItem() + * + * @param txt The name of the payee to be created + * @param id A connected slot should store the id of the created object in this variable + */ + void createPayee(const QString& txt, QString& id); + + /** + * This signal is sent out, when a new category needs to be created + * @sa KMyMoneyCombo::createItem() + * + * @param txt The name of the category to be created + * @param id A connected slot should store the id of the created object in this variable + */ + void createCategory(const QString& txt, QString& id); + +protected: + /** + * Constructor (kept protected, so that one cannot create such an object directly) + */ + KMyMoneyWizard(QWidget *parent = 0, const char *name = 0, bool modal = false, WFlags f = 0); + + /** + * This method sets up the first page after creation of the object + * + * @param page pointer to first page of wizard + */ + void setFirstPage(KMyMoneyWizardPage* page); + + /** + * This method allows to hide or show a @p step. + * + * @param step step to be shown/hidden + * @param hidden hide step if true (the default) or show it if false + */ + void setStepHidden(unsigned int step, bool hidden = true); + +protected slots: + virtual void accept(void); + void completeStateChanged(void); + +private: + void updateStepCount(void); + +private slots: + void backButtonClicked(void); + void nextButtonClicked(void); + void helpButtonClicked(void); + +protected: + /* + * The buttons + */ + KPushButton* m_cancelButton; + KPushButton* m_backButton; + KPushButton* m_nextButton; + KPushButton* m_finishButton; + KPushButton* m_helpButton; + +private: + /** + * Switch to page which is currently the top of the history stack. + * @p oldPage is a pointer to the current page or 0 if no page + * is shown. + * + * @param oldPage pointer to currently displayed page + */ + void switchPage(KMyMoneyWizardPage* oldPage); + + /** + * This method selects the step given by @p step. + * + * @param step step to be selected + */ + void selectStep(unsigned int step); + + /* + * The layouts + */ + QVBoxLayout* m_wizardLayout; + QVBoxLayout* m_stepLayout; + QVBoxLayout* m_pageLayout; + QHBoxLayout* m_buttonLayout; + + /* + * Some misc. widgets required + */ + QFrame* m_stepFrame; + QLabel* m_stepLabel; + QPalette m_stepPalette; + + QValueList<QLabel*> m_steps; // the list of step labels + int m_step; // the currently selected step + + /* + * The title bar + */ + KMyMoneyTitleLabel* m_titleLabel; + + /* + * The history stack + */ + QValueList<KMyMoneyWizardPage*> m_history; + + QString m_helpContext; +}; + + + +#endif diff --git a/kmymoney2/widgets/kmymoneywizard_p.h b/kmymoney2/widgets/kmymoneywizard_p.h new file mode 100644 index 0000000..92d1152 --- /dev/null +++ b/kmymoney2/widgets/kmymoneywizard_p.h @@ -0,0 +1,54 @@ +/*************************************************************************** + kmymoneywizard_p.h + ------------------- + copyright : (C) 2006 by Thomas Baumagrt + 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 KMYMONEYWIZARD_P_H +#define KMYMONEYWIZARD_P_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qobject.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +/** + * @author Thomas Baumgart (C) 2006 + * + * This class represents a helper object required + * to be able to use Qt's signal/slot mechanism within + * the KMyMoneyWizardPage object which cannot be + * derived from QObject directly. + */ +class KMyMoneyWizardPagePrivate : public QObject +{ + Q_OBJECT +public: + /** + * Constructor + */ + KMyMoneyWizardPagePrivate(QObject* parent, const char* name = 0); + + void emitCompleteStateChanged(void); + +signals: + void completeStateChanged(void); +}; + +#endif diff --git a/kmymoney2/widgets/kschedulebriefwidget.ui b/kmymoney2/widgets/kschedulebriefwidget.ui new file mode 100644 index 0000000..7dc523d --- /dev/null +++ b/kmymoney2/widgets/kschedulebriefwidget.ui @@ -0,0 +1,351 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>kScheduleBriefWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kScheduleBriefWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>341</width> + <height>339</height> + </rect> + </property> + <property name="caption"> + <string>Schedules</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>1</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>WinPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <property name="lineWidth"> + <number>2</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="midLineWidth"> + <number>100</number> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_prevButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>m_indexLabel</cstring> + </property> + <property name="text"> + <string>n of n</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>70</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QToolButton"> + <property name="name"> + <cstring>m_nextButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="minimumSize"> + <size> + <width>60</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>60</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="minimumSize"> + <size> + <width>60</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>60</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>Type:</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_name</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_account</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>m_type</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>60</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Account:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QTextEdit"> + <property name="name"> + <cstring>m_details</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + <property name="text"> + <string>1</string> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <spacer> + <property name="name"> + <cstring>Spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>35</width> + <height>0</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_buttonEnter</cstring> + </property> + <property name="text"> + <string>Enter...</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_skipButton</cstring> + </property> + <property name="text"> + <string>Skip</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_closeButton</cstring> + </property> + <property name="text"> + <string>&Close</string> + </property> + <property name="stdItem" stdset="0"> + <number>13</number> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>43</width> + <height>0</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/makekdewidgets.in b/kmymoney2/widgets/makekdewidgets.in new file mode 100755 index 0000000..364ca1b --- /dev/null +++ b/kmymoney2/widgets/makekdewidgets.in @@ -0,0 +1,197 @@ +#!@PERL@ + +# copied from KDELIBS 3.1.4 +# modified for usage with KMyMoney + +use strict; +use vars qw($group $incpath $name $out @includes $init $destroy %widgets); + +sub usage +{ + print STDERR "$_[0]\n" if @_; + print STDERR <<EOT; +Usage: $0 [options] [<widget spec> ...] +Generates a Qt designer-plugin for the widget set defined in widget spec +or STDIN if no filename is given + +Options: + -g <group> default widget group name to display in designer + (default: KDE) + -i <path> path to prepend to include file (default: none) + -n <plugin name> name of the plugin class to generate + (default: <group>WidgetsPlugin) + -o <filename> write output to filename (default: STDOUT) +EOT + exit 1; +} + +sub warning($) { print STDERR "Warning: $_[0]\n" } + +my ($class, %defs); +sub addwidget +{ + return if $class =~ /^(Includes|Init|Destroy)$/; + if(! exists $defs{IncludeFile}) { + $defs{IncludeFile} = join('/', $incpath, lc "$class.h") if ($incpath ne ""); + $defs{IncludeFile} = lc "$class.h" unless ($incpath ne ""); + } + $defs{ImplClass} = $class unless exists $defs{ImplClass}; + $defs{Group} = $group unless exists $defs{Group}; + $defs{ToolTip} = $class unless exists $defs{ToolTip}; + $defs{WhatsThis} = $class unless exists $defs{Long}; + $defs{IconSet} = lc "$class.png" unless exists $defs{IconSet}; + $defs{ConstructorArgs} = '(parent, name)' unless exists $defs{ConstructorArgs}; + $defs{IsContainer} = $defs{IsContainer} ? 'true' : 'false'; + $widgets{$class} = { %defs }; +} + +$group = 'KDE'; +$incpath = ""; + +while ($ARGV[0] =~ /^-/) +{ + my $opt = shift @ARGV; + usage "missing parameter for $opt" unless @ARGV; + if ($opt eq '-g') { $group = shift @ARGV } + elsif ($opt eq '-n') { $name = shift @ARGV } + elsif ($opt eq '-o') { $out = shift @ARGV } + elsif ($opt eq '-i') { $incpath = shift @ARGV } + else { usage "Unknown option $opt" } +} + +$name = "${group}WidgetsPlugin" unless $name; +warning "classname changed to \"$name\"" + if $name =~ s/(^[^A-Za-z_]+|[^A-Za-z0-9_])/_/g; + +while (<>) +{ + chomp; + next if /^(#|\s*$)/; + + if (/^\s*\[([A-Za-z_][A-Za-z0-9_:]*)\]\s*$/) + { + addwidget if $class; + %defs = {}; + $class = $1; + next; + } + elsif (/^\s*\[(.*)\]\s*$/) { die "Invalid class name \"$1\"" } + die "Not in a widget definition" unless $class; + if ($class eq 'Includes') { push @includes, $_ } + elsif ($class eq 'Init') { $init .= "\n\t$_" } + elsif ($class eq 'Destroy') { $destroy .= "\n\t$_" } + elsif (/^\s*(IncludeFile|ImplClass|Group|ToolTip|WhatsThis|IconSet|ConstructorArgs|IsContainer)\s*=\s*(.*)\s*/) + { + $defs{$1} = $2; + } + else { die "Syntax error on line $." } +} +addwidget if $class; + +warning "Nothing to do", exit 0 unless %widgets; + +my @keys = sort keys %widgets; + +if ($out) { open OUT, ">$out" or die "Can't open $out for writing" } +else { open OUT, ">&STDOUT" } + +(my $scriptname = $0) =~ s|^.*/||; +print OUT <<EOT; +/* + * This file was autogenerated by $scriptname. Any changes will be lost! + */ + +#include <qwidgetplugin.h> +// for pixmap search +#include <kstandarddirs.h> + +EOT + +print OUT map { "#include \"$_\"\n" } @includes, map { $widgets{$_}->{IncludeFile} } @keys; + +print OUT <<EOT; + +class $name : public QWidgetPlugin +{ +public: + $name(); + virtual ~$name(); + + virtual QStringList keys() const + { + QStringList result; + for (WidgetInfos::ConstIterator it = m_widgets.begin(); it != m_widgets.end(); ++it) + result << it.key(); + return result; + } + virtual QWidget *create(const QString &key, QWidget *parent = 0, const char *name = 0); + virtual QIconSet iconSet(const QString &key) const + { + QString path = locate("data", "kmymoney2/pics/" + m_widgets[key].iconSet); + return QIconSet(path); + } + virtual bool isContainer(const QString &key) const + { + return m_widgets[key].isContainer; + } +EOT + +print OUT map { <<EOT } qw(group includeFile toolTip whatsThis); + virtual QString $_(const QString &key) const + { + return m_widgets[key].$_; + } +EOT + +print OUT <<EOT; + +private: + struct WidgetInfo + { + QString group; + QString iconSet; + QString includeFile; + QString toolTip; + QString whatsThis; + bool isContainer; + }; + typedef QMap<QString, WidgetInfo> WidgetInfos; + WidgetInfos m_widgets; +}; + +${name}::$name() +{ + WidgetInfo widget; +EOT + +print OUT map { my $w = $_; "\n", (map { my $attr = ucfirst $_; <<EOT } qw(group iconSet includeFile toolTip whatsThis)), <<EOT } @keys; + widget.$_ = "$widgets{$w}->{$attr}"; +EOT + widget.isContainer = $widgets{$w}->{IsContainer}; + m_widgets.insert("$_", widget); +EOT + +print OUT <<EOT; +$init +} + +${name}::~$name() +{$destroy +} + +QWidget *${name}::create(const QString &key, QWidget *parent, const char *name) +{ +EOT + +print OUT map { <<EOT } @keys; + if (key == "$_") + return new $widgets{$_}->{ImplClass}$widgets{$_}->{ConstructorArgs}; +EOT + +print OUT <<EOT; + return 0; +} + +Q_EXPORT_PLUGIN($name) +EOT + diff --git a/kmymoney2/widgets/register.cpp b/kmymoney2/widgets/register.cpp new file mode 100644 index 0000000..974ae23 --- /dev/null +++ b/kmymoney2/widgets/register.cpp @@ -0,0 +1,2438 @@ +/*************************************************************************** + register.cpp - description + ------------------- + begin : Fri Mar 10 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 <typeinfo> + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> +#include <qtimer.h> +#include <qapplication.h> +#include <qeventloop.h> +#include <qtooltip.h> +#include <qimage.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kurldrag.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneydateinput.h> +#include <kmymoney/kmymoneyedit.h> +#include <kmymoney/kmymoneycategory.h> +#include <kmymoney/register.h> +#include <kmymoney/transactionform.h> +#include <kmymoney/stdtransactiondownloaded.h> +#include <kmymoney/stdtransactionmatched.h> +#include "scheduledtransaction.h" +#include "../kmymoneyglobalsettings.h" + +const int LinesPerMemo = 3; + +static QString sortOrderText[] = { + I18N_NOOP("Unknown"), + I18N_NOOP("Post date"), + I18N_NOOP("Date entered"), + I18N_NOOP("Payee"), + I18N_NOOP("Amount"), + I18N_NOOP("Number"), + I18N_NOOP("Entry order"), + I18N_NOOP("Type"), + I18N_NOOP("Category"), + I18N_NOOP("Reconcile state"), + I18N_NOOP("Security") + // add new values above this comment line + }; + +using namespace KMyMoneyRegister; + +static unsigned char fancymarker_bg_image[] = { +/* 200x49 */ + 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, + 0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52, + 0x00,0x00,0x00,0xC8,0x00,0x00,0x00,0x31, + 0x08,0x06,0x00,0x00,0x00,0x9F,0xC5,0xE6, + 0x4F,0x00,0x00,0x00,0x06,0x62,0x4B,0x47, + 0x44,0x00,0xFF,0x00,0xFF,0x00,0xFF,0xA0, + 0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70, + 0x48,0x59,0x73,0x00,0x00,0x0B,0x13,0x00, + 0x00,0x0B,0x13,0x01,0x00,0x9A,0x9C,0x18, + 0x00,0x00,0x00,0x86,0x49,0x44,0x41,0x54, + 0x78,0xDA,0xED,0xDD,0x31,0x0A,0x84,0x40, + 0x10,0x44,0xD1,0x1A,0x19,0x10,0xCF,0xE6, + 0xFD,0x4F,0xB2,0x88,0x08,0x22,0x9B,0x18, + 0x4E,0x1B,0x2C,0x1B,0x18,0xBC,0x07,0x7D, + 0x81,0x82,0x1F,0x77,0x4B,0xB2,0x06,0x18, + 0xEA,0x49,0x3E,0x66,0x00,0x81,0x80,0x40, + 0xE0,0xDF,0x81,0x6C,0x66,0x80,0x3A,0x90, + 0xDD,0x0C,0x50,0x07,0x72,0x98,0x01,0xEA, + 0x40,0x4E,0x33,0x40,0x1D,0xC8,0x65,0x06, + 0x18,0x6B,0xF7,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xF0,0x16,0x3E, + 0x4C,0xC1,0x83,0x9E,0x64,0x32,0x03,0x08, + 0x04,0x7E,0x0A,0xA4,0x9B,0x01,0xEA,0x40, + 0x66,0x33,0x40,0x1D,0xC8,0x62,0x06,0x18, + 0xFB,0x02,0x05,0x87,0x08,0x55,0xFE,0xDE, + 0xA2,0x9D,0x00,0x00,0x00,0x00,0x49,0x45, + 0x4E,0x44,0xAE,0x42,0x60,0x82 +}; + +QPixmap* GroupMarker::m_bg = 0; +int GroupMarker::m_bgRefCnt = 0; + +void ItemPtrVector::sort(void) +{ + if(count() > 0) { + // get rid of 0 pointers in the list + KMyMoneyRegister::ItemPtrVector::iterator it_l; + RegisterItem *item; + for(it_l = begin(); it_l != end(); ++it_l) { + if(*it_l == 0) { + item = last(); + *it_l = item; + pop_back(); + --it_l; + } + } + + std::sort(begin(), end(), item_cmp); + } +} + +bool ItemPtrVector::item_cmp(RegisterItem* i1, RegisterItem* i2) +{ + const QValueList<TransactionSortField>& sortOrder = i1->parent()->sortOrder(); + QValueList<TransactionSortField>::const_iterator it; + int rc = 0; + bool ok1, ok2; + Q_ULLONG n1, n2; + + MyMoneyMoney tmp; + + for(it = sortOrder.begin(); it != sortOrder.end(); ++it) { + TransactionSortField sortField = static_cast<TransactionSortField>(abs(*it)); + switch(sortField) { + case PostDateSort: + rc = i2->sortPostDate().daysTo(i1->sortPostDate()); + if(rc == 0) { + rc = i1->sortSamePostDate() - i2->sortSamePostDate(); + } + break; + + case EntryDateSort: + rc = i2->sortEntryDate().daysTo(i1->sortEntryDate()); + break; + + case PayeeSort: + rc = QString::localeAwareCompare(i1->sortPayee(), i2->sortPayee()); + break; + + case ValueSort: + tmp = i1->sortValue() - i2->sortValue(); + if(tmp.isZero()) + rc = 0; + else if(tmp.isNegative()) + rc = -1; + else + rc = 1; + break; + + case NoSort: + // convert both values to numbers + n1 = i1->sortNumber().toULongLong(&ok1); + n2 = i2->sortNumber().toULongLong(&ok2); + // the following four cases exist: + // a) both are converted correct + // compare them directly + // b) n1 is numeric, n2 is not + // numbers come first, so return -1 + // c) n1 is not numeric, n2 is + // numbers come first, so return 1 + // d) both are non numbers + // compare using localeAwareCompare + if(ok1 && ok2) { // case a) + rc = (n1 > n2) ? 1 : ((n1 == n2 ) ? 0 : -1); + } else if(ok1 && !ok2) { + rc = -1; + } else if(!ok1 && ok2) { + rc = 1; + } else + rc = QString::localeAwareCompare(i1->sortNumber(), i2->sortNumber()); + break; + + case EntryOrderSort: + rc = qstrcmp(i1->sortEntryOrder(), i2->sortEntryOrder()); + break; + + case TypeSort: + rc = i1->sortType() - i2->sortType(); + break; + + case CategorySort: + rc = QString::localeAwareCompare(i1->sortCategory(), i2->sortCategory()); + break; + + case ReconcileStateSort: + rc = static_cast<int>(i1->sortReconcileState()) - static_cast<int>(i2->sortReconcileState()); + break; + + case SecuritySort: + rc = QString::localeAwareCompare(i1->sortSecurity(), i2->sortSecurity()); + break; + + default: + qDebug("Invalid sort key %d", *it); + break; + } + + // the items differ for this sort key so we can return a result + if(rc != 0) + return (*it < 0) ? rc >= 0 : rc < 0; + } + + if(rc == 0) { + rc = qstrcmp(i1->sortEntryOrder(), i2->sortEntryOrder()); + } + + return rc < 0; +} + +GroupMarker::GroupMarker(Register *parent, const QString& txt) : + RegisterItem(parent), + m_txt(txt), + m_drawCounter(parent->drawCounter()-1), // make sure we get painted the first time around + m_showDate(false) +{ + int h; + if(m_parent) { + h = m_parent->rowHeightHint(); + } else { + QFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() ); + h = fm.lineSpacing()+6; + } + + if(m_bg && (m_bg->height() != h)) { + delete m_bg; + m_bg = 0; + } + + // convert the backgroud once + if(m_bg == 0) { + m_bg = new QPixmap; + m_bg->loadFromData(fancymarker_bg_image, sizeof(fancymarker_bg_image)); + + // for now, we can't simply resize the m_bg member as Qt does not support + // alpha resizing. So we take the (slow) detour through a QImage object + // which is ok, since we do this only once. + // m_bg->resize(m_bg->width(), h); + QImage img(m_bg->convertToImage()); + img = img.smoothScale(img.width(), h); + m_bg->convertFromImage(img); + } + + ++m_bgRefCnt; +} + +GroupMarker::~GroupMarker() +{ + --m_bgRefCnt; + if(!m_bgRefCnt) { + delete m_bg; + m_bg = 0; + } +} + +void GroupMarker::paintRegisterCell(QPainter* painter, int row, int /* col */, const QRect& _r, bool /*selected*/, const QColorGroup& _cg) +{ + // avoid painting the marker twice for the same update round + unsigned int drawCounter = m_parent->drawCounter(); + if(m_drawCounter == drawCounter) { + return; + } + m_drawCounter = drawCounter; + + QRect r(_r); + painter->save(); + painter->translate(-r.x(), -r.y()); + + // the group marker always uses all cols + r.setX(m_parent->columnPos(0)); + r.setWidth(m_parent->visibleWidth()); + painter->translate(r.x(), r.y()); + + QRect cellRect; + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->visibleWidth()); + cellRect.setHeight(m_parent->rowHeight(row + m_startRow)); + + // clear out cell rectangle + QColorGroup cg(_cg); + setupColors(cg); + + QBrush backgroundBrush(cg.base()); + painter->fillRect(cellRect, backgroundBrush); + painter->setPen(KMyMoneyGlobalSettings::listGridColor()); + painter->drawLine(cellRect.x(), cellRect.height()-1, cellRect.width(), cellRect.height()-1); + + // now write the text + painter->setPen(cg.text()); + QFont font = painter->font(); + font.setBold(true); + painter->setFont(font); + + painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, m_txt); + + cellRect.setHeight(m_bg->height()); + int curWidth = m_bg->width(); + + // if the background image is too small (not wide enough) we need to increase its width. + if(curWidth < cellRect.width()) { + QPixmap* newPic = new QPixmap(cellRect.width(), cellRect.height()); + int x = 0; + while(x < cellRect.width()) { + copyBlt(newPic, x, 0, m_bg, 0, 0, curWidth, m_bg->height()); + x += curWidth; + } + delete m_bg; + m_bg = newPic; + } + + // now it's time to draw the background + painter->drawPixmap(cellRect, *m_bg); + + // translate back + painter->translate(-r.x(), -r.y()); + + // in case we need to show the date, we just paint it in col 1 + if(m_showDate) { + r.setX(m_parent->columnPos(1)); + r.setWidth(m_parent->columnWidth(1)); + painter->translate(r.x(), r.y()); + + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->columnWidth(1)); + cellRect.setHeight(m_parent->rowHeight(row + m_startRow)); + + font.setBold(false); + painter->setFont(font); + painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, KGlobal::locale()->formatDate(sortPostDate(), true)); + } + + painter->restore(); +} + +void GroupMarker::setupColors(QColorGroup& cg) +{ + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::groupMarkerColor()); +} + +int GroupMarker::rowHeightHint(void) const +{ + if(!m_visible) + return 0; + + return m_bg->height(); +} + +StatementGroupMarker::StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt), + m_dir(dir) +{ + m_showDate = true; +} + +FancyDateGroupMarker::FancyDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : + GroupMarker(parent, txt), + m_date(date) +{ +} + +FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt) +{ +} + +void FiscalYearGroupMarker::setupColors(QColorGroup& cg) +{ + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::groupMarkerColor()); +} + + +SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt) +{ +} + +int SimpleDateGroupMarker::rowHeightHint(void) const +{ + if(!m_visible) + return 0; + + return RegisterItem::rowHeightHint() / 2; +} + +void SimpleDateGroupMarker::paintRegisterCell(QPainter* painter, int row, int /*col*/, const QRect& _r, bool /*selected*/, const QColorGroup& _cg) +{ + QRect r(_r); + painter->save(); + painter->translate(-r.x(), -r.y()); + + // the group marker always uses all cols + r.setX(m_parent->columnPos(0)); + r.setWidth(m_parent->visibleWidth()); + painter->translate(r.x(), r.y()); + + QRect cellRect; + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->visibleWidth()); + cellRect.setHeight(m_parent->rowHeight(row + m_startRow)); + + // clear out cell rectangle + QColorGroup cg(_cg); + if(m_alternate) + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listColor()); + else + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listBGColor()); + QBrush backgroundBrush(cg.base()); + // backgroundBrush.setStyle(Qt::DiagCrossPattern); + backgroundBrush.setStyle(Qt::Dense5Pattern); + backgroundBrush.setColor(KMyMoneyGlobalSettings::listGridColor()); + painter->eraseRect(cellRect); + painter->fillRect(cellRect, backgroundBrush); + painter->setPen(KMyMoneyGlobalSettings::listGridColor()); + painter->drawLine(cellRect.x(), cellRect.height()-1, cellRect.width(), cellRect.height()-1); + + painter->restore(); +} + +TypeGroupMarker::TypeGroupMarker(Register* parent, CashFlowDirection dir, MyMoneyAccount::accountTypeE accType) : + GroupMarker(parent), + m_dir(dir) +{ + switch(dir) { + case Deposit: + m_txt = i18n("Deposits onto account", "Deposits"); + if(accType == MyMoneyAccount::CreditCard) { + m_txt = i18n("Payments towards credit card", "Payments"); + } + break; + case Payment: + m_txt = i18n("Payments made from account", "Payments"); + if(accType == MyMoneyAccount::CreditCard) { + m_txt = i18n("Payments made with credit card", "Charges"); + } + break; + default: + qDebug("Unknown CashFlowDirection %d for TypeGroupMarker constructor", dir); + break; + } +} + +PayeeGroupMarker::PayeeGroupMarker(Register* parent, const QString& name) : + GroupMarker(parent, name) +{ +} + +CategoryGroupMarker::CategoryGroupMarker(Register* parent, const QString& category) : + GroupMarker(parent, category) +{ +} + +ReconcileGroupMarker::ReconcileGroupMarker(Register* parent, MyMoneySplit::reconcileFlagE state) : + GroupMarker(parent), + m_state(state) +{ + switch(state) { + case MyMoneySplit::NotReconciled: + m_txt = i18n("Reconcile state 'Not reconciled'", "Not reconciled"); + break; + case MyMoneySplit::Cleared: + m_txt = i18n("Reconcile state 'Cleared'", "Cleared"); + break; + case MyMoneySplit::Reconciled: + m_txt = i18n("Reconcile state 'Reconciled'", "Reconciled"); + break; + case MyMoneySplit::Frozen: + m_txt = i18n("Reconcile state 'Frozen'", "Frozen"); + break; + default: + m_txt = i18n("Unknown"); + break; + } +} + +class RegisterToolTip : public QToolTip +{ +public: + RegisterToolTip(QWidget* parent, Register* reg); + void maybeTip(const QPoint& pos); + virtual ~RegisterToolTip() {} + +private: + Register* m_register; +}; + +RegisterToolTip::RegisterToolTip(QWidget* parent, Register * reg) : + QToolTip(parent), + m_register(reg) +{ +} + +void RegisterToolTip::maybeTip(const QPoint& pos) +{ + // if we update the register, there's no need to show tooltips + if(!m_register->isUpdatesEnabled()) + return; + + QPoint cpos = m_register->viewportToContents(pos); + // qDebug("RegisterToolTip::mayBeTip(%d,%d)", cpos.x(), cpos.y()); + int row = m_register->rowAt(cpos.y()); + int col = m_register->columnAt(cpos.x()); + RegisterItem* item = m_register->itemAtRow(row); + if(!item) + return; + + QPoint relPos(cpos.x() - m_register->columnPos(0), cpos.y() - m_register->rowPos(item->startRow())); + row = row - item->startRow(); + + // qDebug("row = %d, col = %d", row, col); + // qDebug("relpos = %d,%d", relPos.x(), relPos.y()); + QString msg; + QRect rect; + if(!item->maybeTip(cpos, row, col, rect, msg)) + return; + + QPoint tl(rect.topLeft()); + QPoint br(rect.bottomRight()); + QRect r = QRect(m_register->contentsToViewport(tl), m_register->contentsToViewport(br)); + tip(r, msg); + return; +} + +Register::Register(QWidget *parent, const char *name ) : + TransactionEditorContainer(parent, name), + m_selectAnchor(0), + m_focusItem(0), + m_firstItem(0), + m_lastItem(0), + m_firstErronous(0), + m_lastErronous(0), + m_markErronousTransactions(0), + m_rowHeightHint(0), + m_ledgerLensForced(false), + m_selectionMode(Multi), + m_listsDirty(false), + m_ignoreNextButtonRelease(false), + m_needInitialColumnResize(false), + m_buttonState(Qt::ButtonState(0)), + m_drawCounter(0) +{ + m_tooltip = new RegisterToolTip(viewport(), this); + + setNumCols(MaxColumns); + setCurrentCell(0, 1); + // we do our own sorting + setSorting(false); + + // keep the following list in sync with KMyMoneyRegister::Column in transaction.h + horizontalHeader()->setLabel(NumberColumn, i18n("No.")); + horizontalHeader()->setLabel(DateColumn, i18n("Date")); + horizontalHeader()->setLabel(AccountColumn, i18n("Account")); + horizontalHeader()->setLabel(SecurityColumn, i18n("Security")); + horizontalHeader()->setLabel(DetailColumn, i18n("Details")); + horizontalHeader()->setLabel(ReconcileFlagColumn, i18n("C")); + horizontalHeader()->setLabel(PaymentColumn, i18n("Payment")); + horizontalHeader()->setLabel(DepositColumn, i18n("Deposit")); + horizontalHeader()->setLabel(QuantityColumn, i18n("Quantity")); + horizontalHeader()->setLabel(PriceColumn, i18n("Price")); + horizontalHeader()->setLabel(ValueColumn, i18n("Value")); + horizontalHeader()->setLabel(BalanceColumn, i18n("Balance")); + + setLeftMargin(0); + verticalHeader()->hide(); + + for(int i = 0; i < numCols(); ++i) + setColumnStretchable(i, false); + + horizontalHeader()->setResizeEnabled(false); + horizontalHeader()->setMovingEnabled(false); + horizontalHeader()->setClickEnabled(false); + + horizontalHeader()->installEventFilter(this); + + // never show horizontal scroll bars + setHScrollBarMode(QScrollView::AlwaysOff); + + connect(this, SIGNAL(clicked(int, int, int, const QPoint&)), this, SLOT(selectItem(int, int, int, const QPoint&))); + connect(this, SIGNAL(doubleClicked(int, int, int, const QPoint&)), this, SLOT(slotDoubleClicked(int, int, int, const QPoint&))); + + // double clicking the header turns on auto column sizing + connect(horizontalHeader(), SIGNAL(sectionSizeChanged(int)), this, SLOT(slotAutoColumnSizing(int))); + + //DND + setAcceptDrops(true); +} + +// DND +Transaction* Register::dropTransaction(QPoint cPos) const +{ + Transaction* t = 0; + cPos -= QPoint( verticalHeader()->width(), horizontalHeader()->height() ); + if(cPos.y() >= 0) { + cPos += QPoint(contentsX(), contentsY()); + int row = rowAt(cPos.y()); + t = dynamic_cast<Transaction*>(itemAtRow(row)); + } + return t; +} + +void Register::dragMoveEvent(QDragMoveEvent* event) +{ + if ( KURLDrag::canDecode(event) ) { + event->ignore(); + Transaction* t = dropTransaction(event->pos()); + if(t && !t->isScheduled()) { + event->accept(); + } + } +} + +void Register::dropEvent(QDropEvent* event) +{ + qDebug("Register::dropEvent"); + if ( KURLDrag::canDecode(event) ) { + event->ignore(); + Transaction* t = dropTransaction(event->pos()); + if(t && !t->isScheduled()) { + qDebug("Drop was ok"); + KURL::List urls; + KURLDrag::decode(event, urls); + qDebug("List is '%s'", urls.toStringList().join(";").data()); + event->accept(); + } + } +} +// DND end + + +Register::~Register() +{ + clear(); + delete m_tooltip; + m_tooltip = 0; +} + +void Register::slotAutoColumnSizing(int section) +{ + Q_UNUSED(section) +#if 0 + // this is some trial code to make the col sizes adjustable + // there are some drawbacks though: what when we have a register + // but no account? (ipwizard 2007-11-06) + if(isUpdatesEnabled()) { + int w = visibleWidth(); + QString size; + for(int i=0; i < numCols(); ++i) { + if(i) + size += ","; + if(i == DetailColumn) { + size += "0"; + continue; + } + size += QString("%1").arg((columnWidth(i) * 100) / w); + } + qDebug("size = %s", size.data()); + m_account.setValue("kmm-ledger-column-width", size); + } +#endif +} + +bool Register::eventFilter(QObject* o, QEvent* e) +{ + if(o == horizontalHeader() && e->type() == QEvent::MouseButtonPress) { + QMouseEvent *me = dynamic_cast<QMouseEvent*>(e); + if(me->button() == Qt::RightButton) { + emit headerClicked(); + } + // eat up left mouse button press for now + return true; + + } else if(o == horizontalHeader() && e->type() == QEvent::Paint) { + // always show the header in bold (to suppress cell selection) + QFont f(horizontalHeader()->font()); + f.setBold(true); + horizontalHeader()->setFont(f); + + } else if(o == this && e->type() == QEvent::KeyPress) { + QKeyEvent* ke = dynamic_cast<QKeyEvent*>(e); + if(ke->key() == Qt::Key_Menu) { + emit openContextMenu(); + return true; + } + } + + return QTable::eventFilter(o, e); +} + +void Register::setupRegister(const MyMoneyAccount& account, const QValueList<Column>& cols) +{ + m_account = account; + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + + for(int i = 0; i < MaxColumns; ++i) + hideColumn(i); + + m_needInitialColumnResize = true; + + m_lastCol = static_cast<Column>(0); + QValueList<Column>::const_iterator it_c; + for(it_c = cols.begin(); it_c != cols.end(); ++it_c) { + if((*it_c) > MaxColumns) + continue; + showColumn(*it_c); + if(*it_c > m_lastCol) + m_lastCol = *it_c; + } + + setUpdatesEnabled(enabled); +} + +void Register::setupRegister(const MyMoneyAccount& account, bool showAccountColumn) +{ + m_account = account; + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + + for(int i = 0; i < MaxColumns; ++i) + hideColumn(i); + + horizontalHeader()->setLabel(PaymentColumn, i18n("Payment made from account", "Payment")); + horizontalHeader()->setLabel(DepositColumn, i18n("Deposit into account", "Deposit")); + + if(account.id().isEmpty()) { + setUpdatesEnabled(enabled); + return; + } + + m_needInitialColumnResize = true; + + // turn on standard columns + showColumn(DateColumn); + showColumn(DetailColumn); + showColumn(ReconcileFlagColumn); + + // balance + switch(account.accountType()) { + case MyMoneyAccount::Stock: + break; + default: + showColumn(BalanceColumn); + break; + } + + // Number column + switch(account.accountType()) { + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::Loan: + case MyMoneyAccount::AssetLoan: + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Equity: + if(KMyMoneyGlobalSettings::alwaysShowNrField()) + showColumn(NumberColumn); + break; + + case MyMoneyAccount::Checkings: + case MyMoneyAccount::CreditCard: + showColumn(NumberColumn); + break; + + default: + hideColumn(NumberColumn); + break; + } + + switch(account.accountType()) { + case MyMoneyAccount::Income: + case MyMoneyAccount::Expense: + showAccountColumn = true; + break; + default: + break; + } + + if(showAccountColumn) + showColumn(AccountColumn); + + // Security, activity, payment, deposit, amount, price and value column + switch(account.accountType()) { + default: + showColumn(PaymentColumn); + showColumn(DepositColumn); + break; + + case MyMoneyAccount::Investment: + showColumn(SecurityColumn); + showColumn(QuantityColumn); + showColumn(PriceColumn); + showColumn(ValueColumn); + break; + } + + // headings + switch(account.accountType()) { + case MyMoneyAccount::CreditCard: + horizontalHeader()->setLabel(PaymentColumn, i18n("Payment made with credit card", "Charge")); + horizontalHeader()->setLabel(DepositColumn, i18n("Payment towards credit card", "Payment")); + break; + case MyMoneyAccount::Asset: + case MyMoneyAccount::AssetLoan: + horizontalHeader()->setLabel(PaymentColumn, i18n("Decrease of asset/liability value", "Decrease")); + horizontalHeader()->setLabel(DepositColumn, i18n("Increase of asset/liability value", "Increase")); + break; + case MyMoneyAccount::Liability: + case MyMoneyAccount::Loan: + horizontalHeader()->setLabel(PaymentColumn, i18n("Increase of asset/liability value", "Increase")); + horizontalHeader()->setLabel(DepositColumn, i18n("Decrease of asset/liability value", "Decrease")); + break; + case MyMoneyAccount::Income: + case MyMoneyAccount::Expense: + horizontalHeader()->setLabel(PaymentColumn, i18n("Income")); + horizontalHeader()->setLabel(DepositColumn, i18n("Expense")); + break; + + default: + break; + } + + switch(account.accountType()) { + default: + m_lastCol = BalanceColumn; + break; + } + + setUpdatesEnabled(enabled); +} + +bool Register::focusNextPrevChild(bool next) +{ + return QFrame::focusNextPrevChild(next); +} + +void Register::setSortOrder(const QString& order) +{ + QStringList orderList = QStringList::split(",", order); + QStringList::const_iterator it; + m_sortOrder.clear(); + for(it = orderList.begin(); it != orderList.end(); ++it) { + m_sortOrder << static_cast<TransactionSortField>((*it).toInt()); + } +} + +void Register::sortItems(void) +{ + if(m_items.count() == 0) + return; + + // sort the array of pointers to the transactions + m_items.sort(); + + // update the next/prev item chains + RegisterItem* prev = 0; + RegisterItem* item; + m_firstItem = m_lastItem = 0; + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + item = m_items[i]; + if(!item) + continue; + + if(!m_firstItem) + m_firstItem = item; + m_lastItem = item; + if(prev) + prev->setNextItem(item); + item->setPrevItem(prev); + item->setNextItem(0); + prev = item; + } + + // update the balance visibility settings + item = m_lastItem; + bool showBalance = true; + while(item) { + Transaction* t = dynamic_cast<Transaction*>(item); + if(t) { + t->setShowBalance(showBalance); + if(!t->isVisible()) { + showBalance = false; + } + } + item = item->prevItem(); + } + + // force update of the item index (row to item array) + m_listsDirty = true; +} + +TransactionSortField Register::primarySortKey(void) const +{ + if(!m_sortOrder.isEmpty()) + return static_cast<KMyMoneyRegister::TransactionSortField>(abs(m_sortOrder.first())); + return UnknownSort; +} + + +void Register::clear(void) +{ + m_firstErronous = m_lastErronous = 0; + m_ensureVisibleItem = 0; + + RegisterItem* p; + while((p = firstItem()) != 0) { + delete p; + } + m_items.clear(); + + m_firstItem = m_lastItem = 0; + + m_listsDirty = true; + m_selectAnchor = 0; + m_focusItem = 0; + +#ifndef KMM_DESIGNER + // recalculate row height hint + QFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() ); + m_rowHeightHint = fm.lineSpacing()+6; +#endif + + m_needInitialColumnResize = true; +} + +void Register::insertItemAfter(RegisterItem*p, RegisterItem* prev) +{ + RegisterItem* next = 0; + if(!prev) + prev = lastItem(); + + if(prev) { + next = prev->nextItem(); + prev->setNextItem(p); + } + if(next) + next->setPrevItem(p); + + p->setPrevItem(prev); + p->setNextItem(next); + + if(!m_firstItem) + m_firstItem = p; + if(!m_lastItem) + m_lastItem = p; + + if(prev == m_lastItem) + m_lastItem = p; + + m_listsDirty = true; +} + +void Register::addItem(RegisterItem* p) +{ + RegisterItem* q = lastItem(); + if(q) + q->setNextItem(p); + p->setPrevItem(q); + p->setNextItem(0); + + m_items.append(p); + + if(!m_firstItem) + m_firstItem = p; + m_lastItem = p; + m_listsDirty = true; +} + +void Register::removeItem(RegisterItem* p) +{ + // remove item from list + if(p->prevItem()) + p->prevItem()->setNextItem(p->nextItem()); + if(p->nextItem()) + p->nextItem()->setPrevItem(p->prevItem()); + + // update first and last pointer if required + if(p == m_firstItem) + m_firstItem = p->nextItem(); + if(p == m_lastItem) + m_lastItem = p->prevItem(); + + // make sure we don't do it twice + p->setNextItem(0); + p->setPrevItem(0); + + // remove it from the m_items array + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; + if(!item) + continue; + if(item == p) { + m_items[i] = 0; + break; + } + } + m_listsDirty = true; +} + +RegisterItem* Register::firstItem(void) const +{ + return m_firstItem; +} + +RegisterItem* Register::lastItem(void) const +{ + return m_lastItem; +} + +void Register::setupItemIndex(int rowCount) +{ + // setup index array + m_itemIndex.clear(); + m_itemIndex.reserve(rowCount); + + // fill index array + rowCount = 0; + RegisterItem* prev = 0; + m_firstItem = m_lastItem = 0; + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; + if(!item) + continue; + if(!m_firstItem) + m_firstItem = item; + m_lastItem = item; + if(prev) + prev->setNextItem(item); + item->setPrevItem(prev); + item->setNextItem(0); + prev = item; + for(int j = item->numRowsRegister(); j; --j) { + m_itemIndex.push_back(item); + } + } +} + +void Register::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) +{ + // the QTable::drawContents() method does not honor the block update flag + // so we take care of it here + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + if(m_listsDirty) { + updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); + } + + ++m_drawCounter; + QTable::drawContents(p, cx, cy, cw, ch); +} + +void Register::updateAlternate(void) const +{ + bool alternate = false; + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; + if(!item) + continue; + if(item->isVisible()) { + item->setAlternate(alternate); + alternate ^= true; + } + } +} + +void Register::suppressAdjacentMarkers(void) +{ + bool lastWasGroupMarker = false; + KMyMoneyRegister::RegisterItem* p = lastItem(); + KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p); + if(t && t->transaction().id().isEmpty()) { + lastWasGroupMarker = true; + p = p->prevItem(); + } + while(p) { + KMyMoneyRegister::GroupMarker* m = dynamic_cast<KMyMoneyRegister::GroupMarker*>(p); + if(m) { + // make adjacent group marker invisible except those that show statement information + if(lastWasGroupMarker && (dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(m) == 0)) { + m->setVisible(false); + } + lastWasGroupMarker = true; + } else if(p->isVisible()) + lastWasGroupMarker = false; + p = p->prevItem(); + } +} + +void Register::updateRegister(bool forceUpdateRowHeight) +{ + ::timetrace("Update register"); + if(m_listsDirty || forceUpdateRowHeight) { + // don't get in here recursively + m_listsDirty = false; + + int rowCount = 0; + // determine the number of rows we need to display all items + // while going through the list, check for erronous transactions + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; + if(!item) + continue; + item->setStartRow(rowCount); + item->setNeedResize(); + rowCount += item->numRowsRegister(); + + if(item->isErronous()) { + if(!m_firstErronous) + m_firstErronous = item; + m_lastErronous = item; + } + } + + updateAlternate(); + + // create item index + setupItemIndex(rowCount); + + bool needUpdateHeaders = (numRows() != rowCount) | forceUpdateRowHeight; + + // setup QTable. Make sure to suppress screen updates for now + bool updatesEnabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + setNumRows(rowCount); + + // if we need to update the headers, we do it now for all rows + // again we make sure to suppress screen updates + if(needUpdateHeaders) { + // int height = rowHeightHint(); + + verticalHeader()->setUpdatesEnabled(false); + + for(int i = 0; i < rowCount; ++i) { + RegisterItem* item = itemAtRow(i); + if(item->isVisible()) { + showRow(i); + } else { + hideRow(i); + } + verticalHeader()->resizeSection(i, item->rowHeightHint()); + } + verticalHeader()->setUpdatesEnabled(true); + } + + // add or remove scrollbars as required + updateScrollBars(); + + setUpdatesEnabled(updatesEnabled); + + // force resizeing of the columns if necessary + if(m_needInitialColumnResize) { + QTimer::singleShot(0, this, SLOT(resize())); + m_needInitialColumnResize = false; + } else { + updateContents(); + + // if the number of rows changed, we might need to resize the register + // to make sure we reflect the current visibility of the scrollbars. + if(needUpdateHeaders) + QTimer::singleShot(0, this, SLOT(resize())); + } + } + ::timetrace("Done updateing register"); +} + +int Register::rowHeightHint(void) const +{ + if(!m_rowHeightHint) { + qDebug("Register::rowHeightHint(): m_rowHeightHint is zero!!"); + } + return m_rowHeightHint; +} + +void Register::paintCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg) +{ + // determine the item that we need to paint in the row and call it's paintRegisterCell() method + if((row < 0) || ((unsigned)row > m_itemIndex.size())) { + qDebug("Register::paintCell: row %d out of bounds %d", row, (int)m_itemIndex.size()); + return; + } + + // qDebug("paintCell(%d,%d)", row, col); + RegisterItem* const item = m_itemIndex[row]; + item->paintRegisterCell(painter, row - item->startRow(), col, r, selected, cg); +} + +void Register::focusInEvent(QFocusEvent* ev) +{ + QTable::focusInEvent(ev); + if(m_focusItem) { + m_focusItem->setFocus(true, false); + repaintItems(m_focusItem); + } +} + +void Register::focusOutEvent(QFocusEvent* ev) +{ + if(m_focusItem) { + m_focusItem->setFocus(false, false); + repaintItems(m_focusItem); + } + QTable::focusOutEvent(ev); +} + +void Register::resize(void) +{ + resize(DetailColumn); +} + +void Register::resize(int col) +{ + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + + // resize the register + int w = visibleWidth(); + + // TODO I was playing a bit with manual ledger resizing but could not get + // a good solution. I just leave the code around, so that maybe others + // pick it up again. So far, it's not clear to me where to store the + // size of the sections: + // + // a) with the account (as it is done now) + // b) with the application for the specific account type + // c) ???? + // + // Ideas are welcome (ipwizard: 2007-07-19) + // Note: currently there's no way to switch back to automatic + // column sizing once the manual sizing option has been saved +#if 0 + if(m_account.value("kmm-ledger-column-width").isEmpty()) { +#endif + + // check which space we need + if(columnWidth(NumberColumn)) + adjustColumn(NumberColumn); + if(columnWidth(AccountColumn)) + adjustColumn(AccountColumn); + if(columnWidth(PaymentColumn)) + adjustColumn(PaymentColumn); + if(columnWidth(DepositColumn)) + adjustColumn(DepositColumn); + if(columnWidth(BalanceColumn)) + adjustColumn(BalanceColumn); + if(columnWidth(PriceColumn)) + adjustColumn(PriceColumn); + if(columnWidth(ValueColumn)) + adjustColumn(ValueColumn); + + // make amount columns all the same size + // only extend the entry columns to make sure they fit + // the widget + int dwidth = 0; + int ewidth = 0; + if(ewidth < columnWidth(PaymentColumn)) + ewidth = columnWidth(PaymentColumn); + if(ewidth < columnWidth(DepositColumn)) + ewidth = columnWidth(DepositColumn); + if(dwidth < columnWidth(BalanceColumn)) + dwidth = columnWidth(BalanceColumn); + if(ewidth < columnWidth(PriceColumn)) + ewidth = columnWidth(PriceColumn); + if(dwidth < columnWidth(ValueColumn)) + dwidth = columnWidth(ValueColumn); + + int swidth = columnWidth(SecurityColumn); + if(swidth > 0) { + adjustColumn(SecurityColumn); + swidth = columnWidth(SecurityColumn); + } + +#ifndef KMM_DESIGNER + // Resize the date and money fields to either + // a) the size required by the input widget if no transaction form is shown + // b) the adjusted value for the input widget if the transaction form is visible + if(!KMyMoneyGlobalSettings::transactionForm()) { + kMyMoneyDateInput* dateField = new kMyMoneyDateInput; + kMyMoneyEdit* valField = new kMyMoneyEdit; + + dateField->setFont(KMyMoneyGlobalSettings::listCellFont()); + setColumnWidth(DateColumn, dateField->minimumSizeHint().width()); + valField->setMinimumWidth(ewidth); + ewidth = valField->minimumSizeHint().width(); + + if(swidth > 0) { + swidth = columnWidth(SecurityColumn) + 40; + } + delete valField; + delete dateField; + } else { + adjustColumn(DateColumn); + } +#endif + + if(columnWidth(PaymentColumn)) + setColumnWidth(PaymentColumn, ewidth); + if(columnWidth(DepositColumn)) + setColumnWidth(DepositColumn, ewidth); + if(columnWidth(BalanceColumn)) + setColumnWidth(BalanceColumn, dwidth); + if(columnWidth(PriceColumn)) + setColumnWidth(PriceColumn, ewidth); + if(columnWidth(ValueColumn)) + setColumnWidth(ValueColumn, dwidth); + + if(columnWidth(ReconcileFlagColumn)) + setColumnWidth(ReconcileFlagColumn, 20); + + if(swidth > 0) + setColumnWidth(SecurityColumn, swidth); +#if 0 + // see comment above + } else { + QStringList colSizes = QStringList::split(",", m_account.value("kmm-ledger-column-width"), true); + for(int i; i < colSizes.count(); ++i) { + int colWidth = colSizes[i].toInt(); + if(colWidth == 0) + continue; + setColumnWidth(i, w * colWidth / 100); + } + } +#endif + + for(int i = 0; i < numCols(); ++i) { + if(i == col) + continue; + + w -= columnWidth(i); + } + setColumnWidth(col, w); + + setUpdatesEnabled(enabled); + updateContents(); +} + + +void Register::adjustColumn(int col) +{ +#ifdef KMM_DESIGNER + Q_UNUSED(col) +#else + QString msg = "%1 adjusting column %2"; + ::timetrace((msg.arg("Start").arg(col)).data()); + QHeader *topHeader = horizontalHeader(); + QFontMetrics cellFontMetrics(KMyMoneyGlobalSettings::listCellFont()); + + int w = topHeader->fontMetrics().width( topHeader->label( col ) ) + 10; + if ( topHeader->iconSet( col ) ) + w += topHeader->iconSet( col )->pixmap().width(); + w = QMAX( w, 20 ); + + int maxWidth = 0; + switch(col) { + case NumberColumn: + maxWidth = cellFontMetrics.width("0123456789"); + break; + default: + break; + } + + // check for date column + if(col == DateColumn) { + QString txt = KGlobal::locale()->formatDate(QDate(6999,12,29), true); + int nw = cellFontMetrics.width(txt+" "); + w = QMAX( w, nw ); + } else { + + // scan through the transactions + for(unsigned i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; + if(!item) + continue; + Transaction* t = dynamic_cast<Transaction*>(item); + if(t) { + int nw = t->registerColWidth(col, cellFontMetrics); + w = QMAX( w, nw ); + if(maxWidth) { + if(w > maxWidth) { + w = maxWidth; + break; + } + } + } + } + } + + setColumnWidth( col, w ); +#endif +} + +void Register::repaintItems(RegisterItem* first, RegisterItem* last) +{ + if(first == 0 && last == 0) { + first = firstItem(); + last = lastItem(); + } + + if(first == 0) + return; + + if(last == 0) + last = first; + + // qDebug("repaintItems from row %d to row %d", first->startRow(), last->startRow()+last->numRowsRegister()-1); + + // the following code is based on code I found in + // QTable::cellGeometry() and QTable::updateCell() (ipwizard) + QRect cg(0, + rowPos(first->startRow()), + visibleWidth(), + rowPos(last->startRow()+last->numRowsRegister()-1) - rowPos(first->startRow()) + rowHeight(last->startRow()+last->numRowsRegister()-1)); + + QRect r(contentsToViewport(QPoint (cg.x() - 2, cg.y() - 2 )), QSize(cg.width() + 4, cg.height() + 4 )); + + QRect tmp = m_lastRepaintRect | r; + if(abs(tmp.height()) > 3000) { + // make sure that the previously triggered repaint has been done before we + // trigger the next. Not having this used to cause some trouble when changing + // the focus within a 2000 item ledger from the last to the first item. + QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput, 10); + } + m_lastRepaintRect = r; + QApplication::postEvent( viewport(), new QPaintEvent( r, FALSE ) ); + +} + +void Register::clearSelection(void) +{ + unselectItems(); +} + +void Register::doSelectItems(int from, int to, bool selected) +{ + int start, end; + // make sure start is smaller than end + if(from <= to) { + start = from; + end = to; + } else { + start = to; + end = from; + } + // make sure we stay in bounds + if(start < 0) + start = 0; + if((end <= -1) || ((unsigned)end > (m_items.size()-1))) + end = m_items.size()-1; + + RegisterItem* firstItem; + RegisterItem* lastItem; + firstItem = lastItem = 0; + for(int i = start; i <= end; ++i) { + RegisterItem* const item = m_items[i]; + if(item) { + if(selected != item->isSelected()) { + if(!firstItem) + firstItem = item; + item->setSelected(selected); + lastItem = item; + } + } + } + + // anything changed? + if(firstItem || lastItem) + repaintItems(firstItem, lastItem); +} + +RegisterItem* Register::itemAtRow(int row) const +{ + if(row >= 0 && (unsigned)row < m_itemIndex.size()) { + return m_itemIndex[row]; + } + return 0; +} + +int Register::rowToIndex(int row) const +{ + for(unsigned i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; + if(!item) + continue; + if(row >= item->startRow() && row < (item->startRow() + item->numRowsRegister())) + return i; + } + return -1; +} + +void Register::selectedTransactions(SelectedTransactions& list) const +{ + if(m_focusItem && m_focusItem->isSelected() && m_focusItem->isVisible()) { + Transaction* t = dynamic_cast<Transaction*>(m_focusItem); + if(t) { + QString id; + if(t->isScheduled()) + id = t->transaction().id(); + SelectedTransaction s(t->transaction(), t->split(), id); + list << s; + } + } + + for(unsigned i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; + // make sure, we don't include the focus item twice + if(item == m_focusItem) + continue; + if(item && item->isSelected() && item->isVisible()) { + Transaction* t = dynamic_cast<Transaction*>(item); + if(t) { + QString id; + if(t->isScheduled()) + id = t->transaction().id(); + SelectedTransaction s(t->transaction(), t->split(), id); + list << s; + } + } + } +} + +QValueList<RegisterItem*> Register::selectedItems(void) const +{ + QValueList<RegisterItem*> list; + + RegisterItem* item = m_firstItem; + while(item) { + if(item && item->isSelected() && item->isVisible()) { + list << item; + } + item = item->nextItem(); + } + return list; +} + +int Register::selectedItemsCount(void) const +{ + int cnt = 0; + RegisterItem* item = m_firstItem; + while(item) { + if(item->isSelected() && item->isVisible()) + ++cnt; + item = item->nextItem(); + } + return cnt; +} + +void Register::contentsMouseReleaseEvent( QMouseEvent *e ) +{ + if(m_ignoreNextButtonRelease) { + m_ignoreNextButtonRelease = false; + return; + } + + m_buttonState = e->state(); + QTable::contentsMouseReleaseEvent(e); +} + +void Register::selectItem(int row, int col, int button, const QPoint& /* mousePos */) +{ + if(row >= 0 && (unsigned)row < m_itemIndex.size()) { + RegisterItem* item = m_itemIndex[row]; + + // don't support selecting when the item has an editor + // or the item itself is not selectable + if(item->hasEditorOpen() || !item->isSelectable()) + return; + + QString id = item->id(); + selectItem(item); + // selectItem() might have changed the pointers, so we + // need to reconstruct it here + item = itemById(id); + Transaction* t = dynamic_cast<Transaction*>(item); + if(t) { + if(!id.isEmpty()) { + switch(button & Qt::MouseButtonMask) { + case Qt::RightButton: + emit openContextMenu(); + break; + + case Qt::LeftButton: + if(t && col == ReconcileFlagColumn && selectedItemsCount() == 1 && !t->isScheduled()) + emit reconcileStateColumnClicked(t); + break; + + default: + break; + } + } else { + emit emptyItemSelected(); + } + } + } +} + +void Register::setAnchorItem(RegisterItem* anchorItem) +{ + m_selectAnchor = anchorItem; +} + +bool Register::setFocusItem(RegisterItem* focusItem) +{ + if(focusItem && focusItem->canHaveFocus()) { + if(m_focusItem) { + m_focusItem->setFocus(false); + // issue a repaint here only if we move the focus + if(m_focusItem != focusItem) + repaintItems(m_focusItem); + } + Transaction* item = dynamic_cast<Transaction*>(focusItem); + if(m_focusItem != focusItem && item) { + emit focusChanged(item); + } + + m_focusItem = focusItem; + m_focusItem->setFocus(true); + if(m_listsDirty) + updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); + ensureItemVisible(m_focusItem); + repaintItems(m_focusItem); + return true; + } else + return false; +} + +bool Register::setFocusToTop(void) +{ + RegisterItem* rgItem=m_firstItem; + while (rgItem) { + if (setFocusItem(rgItem)) + return true; + rgItem=rgItem->nextItem(); + } + return false; +} + +void Register::selectItem(RegisterItem* item, bool dontChangeSelections) +{ + if(!item) + return; + + // kdDebug(2) << "Register::selectItem(" << item << "): type is " << typeid(*item).name() << endl; + + Qt::ButtonState buttonState = m_buttonState; + m_buttonState = Qt::NoButton; + + if(m_selectionMode == NoSelection) + return; + + if(item->isSelectable()) { + QString id = item->id(); + QValueList<RegisterItem*> itemList = selectedItems(); + bool okToSelect = true; + int cnt = itemList.count(); + bool sameEntryType = true; + if(cnt > 0) { + if(typeid(*itemList.begin()) != typeid(item)) + sameEntryType = false; + } + + if(buttonState & Qt::LeftButton) { + if(!(buttonState & (Qt::ShiftButton | Qt::ControlButton))) { + if((cnt != 1) || ((cnt == 1) && !item->isSelected())) { + emit aboutToSelectItem(item, okToSelect); + if(okToSelect) { + // pointer 'item' might have changed. reconstruct it. + item = itemById(id); + unselectItems(); + item->setSelected(true); + setFocusItem(item); + } + } + if(okToSelect) + m_selectAnchor = item; + } + + if(m_selectionMode == Multi) { + switch(buttonState & (Qt::ShiftButton | Qt::ControlButton)) { + case Qt::ControlButton: + okToSelect = sameEntryType; + if(typeid(*item) == typeid(StdTransactionScheduled)) + okToSelect = false; + // toggle selection state of current item + emit aboutToSelectItem(item, okToSelect); + if(okToSelect) { + // pointer 'item' might have changed. reconstruct it. + item = itemById(id); + item->setSelected(!item->isSelected()); + setFocusItem(item); + } + break; + + case Qt::ShiftButton: + okToSelect = sameEntryType; + if(typeid(*item) == typeid(StdTransactionScheduled)) + okToSelect = false; + emit aboutToSelectItem(item, okToSelect); + if(okToSelect) { + // pointer 'item' might have changed. reconstruct it. + item = itemById(id); + unselectItems(); + selectItems(rowToIndex(m_selectAnchor->startRow()), rowToIndex(item->startRow())); + setFocusItem(item); + } + break; + } + } + } else if(buttonState & Qt::RightButton) { + // if the right button is pressed then only change the + // selection if none of the Shift/Ctrl button is pressed and + // one of the following conditions is true: + // + // a) single transaction is selected + // b) multiple transactions are selected and the one to be selected is not + if(!(buttonState & (Qt::ShiftButton | Qt::ControlButton))) { + if((cnt > 0) && (!item->isSelected())) { + okToSelect = sameEntryType; + emit aboutToSelectItem(item, okToSelect); + if(okToSelect) { + // pointer 'item' might have changed. reconstruct it. + item = itemById(id); + unselectItems(); + item->setSelected(true); + setFocusItem(item); + } + } + if(okToSelect) + m_selectAnchor = item; + } + } else { + // we get here when called by application logic + emit aboutToSelectItem(item, okToSelect); + if(okToSelect) { + // pointer 'item' might have changed. reconstruct it. + item = itemById(id); + if(!dontChangeSelections) + unselectItems(); + item->setSelected(true); + setFocusItem(item); + m_selectAnchor = item; + } + } + if(okToSelect) { + SelectedTransactions list(this); + emit selectionChanged(list); + } + } +} + +void Register::ensureItemVisible(RegisterItem* item) +{ + if(!item) + return; + + m_ensureVisibleItem = item; + QTimer::singleShot(0, this, SLOT(slotEnsureItemVisible())); +} + +void Register::slotDoubleClicked(int row, int, int, const QPoint&) +{ + if(row >= 0 && (unsigned)row < m_itemIndex.size()) { + RegisterItem* p = m_itemIndex[row]; + if(p->isSelectable()) { + m_ignoreNextButtonRelease = true; + // double click to start editing only works if the focus + // item is among the selected ones + if(!focusItem()) { + setFocusItem(p); + if(m_selectionMode != NoSelection) + p->setSelected(true); + } + + if(m_focusItem->isSelected()) { + // don't emit the signal right away but wait until + // we come back to the Qt main loop + QTimer::singleShot(0, this, SIGNAL(editTransaction())); + } + } + } +} + +void Register::slotEnsureItemVisible(void) +{ + // if clear() has been called since the timer was + // started, we just ignore the call + if(!m_ensureVisibleItem) + return; + + // make sure to catch latest changes + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + updateRegister(); + setUpdatesEnabled(enabled); + + RegisterItem* item = m_ensureVisibleItem; + RegisterItem* prev = item->prevItem(); + while(prev && !prev->isVisible()) + prev = prev->prevItem(); + RegisterItem* next = item->nextItem(); + while(next && !next->isVisible()) + next = next->nextItem(); + + int rowPrev, rowNext; + rowPrev = item->startRow(); + rowNext = item->startRow() + item->numRowsRegister() - 1; + + if(prev) + rowPrev = prev->startRow(); + if(next) + rowNext = next->startRow() + next->numRowsRegister() - 1; + + if(rowPrev < 0) + rowPrev = 0; + if(rowNext >= numRows()) + rowNext = numRows()-1; + + int wt = contentsY(); // window top + int wh = visibleHeight(); // window height + int lt = rowPos(rowPrev); // top of line above lens + int lb = rowPos(rowNext)+rowHeight(rowNext); // bottom of line below lens + + // only update widget, if the transaction is not fully visible + if(lt < wt || lb >= (wt + wh)) { + if(rowPrev >= 0) { + ensureCellVisible(rowPrev, 0); + } + + ensureCellVisible(item->startRow(), 0); + + if(rowNext < numRows()) { + ensureCellVisible(rowNext, 0); + } + } +} + +TransactionSortField KMyMoneyRegister::textToSortOrder(const QString& text) +{ + for(int idx = 1; idx < static_cast<int>(MaxSortFields); ++idx) { + if(text == i18n(sortOrderText[idx])) { + return static_cast<TransactionSortField>(idx); + } + } + return UnknownSort; +} + +const QString KMyMoneyRegister::sortOrderToText(TransactionSortField idx) +{ + if(idx < PostDateSort || idx >= MaxSortFields) + idx = UnknownSort; + return i18n(sortOrderText[idx]); +} + +QString Register::text(int /*row*/, int /*col*/) const +{ + return QString("a"); +} + +QWidget* Register::cellWidget(int row, int col) const +{ + // separeted here in two if()s, because this method is called for each + // event from QTable::eventFilter and in the most cases it is -1, -1 + if(row < 0 || col < 0) + return 0; + + if(row > numRows() - 1 || col > numCols() - 1) { + if(numRows() && numCols()) + qWarning("Register::cellWidget(%d,%d) out of bounds (%d,%d)", row, col, numRows(), numCols()); + return 0; + } + + if(!m_cellWidgets.count()) + return 0; + + QWidget* w = 0; + QPair<int, int> idx = qMakePair(row, col); + QMap<QPair<int, int>, QWidget*>::const_iterator it_w; + + it_w = m_cellWidgets.find(idx); + if(it_w != m_cellWidgets.end()) + w = *it_w; + return w; +} + +void Register::insertWidget(int row, int col, QWidget* w) +{ + if(row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) { + qWarning("Register::insertWidget(%d,%d) out of bounds", row, col); + return; + } + + QPair<int, int> idx = qMakePair(row, col); + m_cellWidgets[idx] = w; +} + +void Register::clearCellWidget(int row, int col) +{ + if(row < 0 || col < 0 || row > numRows() - 1 || col > numCols() - 1) { + qWarning("Register::clearCellWidget(%d,%d) out of bounds", row, col); + return; + } + + QPair<int, int> idx = qMakePair(row, col); + QMap<QPair<int, int>, QWidget*>::iterator it_w; + + it_w = m_cellWidgets.find(idx); + if(it_w != m_cellWidgets.end()) { + (*it_w)->deleteLater(); + m_cellWidgets.remove(it_w); + } +} + +QWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const +{ + return 0; +} + +void Register::setCellContentFromEditor(int /*row*/, int /*col*/) +{ +} + +void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/) +{ +} + +void Register::arrangeEditWidgets(QMap<QString, QWidget*>& editWidgets, KMyMoneyRegister::Transaction* t) +{ + t->arrangeWidgetsInRegister(editWidgets); + ensureItemVisible(t); + // updateContents(); +} + +void Register::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const +{ + t->tabOrderInRegister(tabOrderWidgets); +} + +void Register::removeEditWidgets(QMap<QString, QWidget*>& editWidgets) +{ + // remove pointers from map + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ) { + if((*it)->parentWidget() == this) { + editWidgets.remove(it); + it = editWidgets.begin(); + } else + ++it; + } + + // now delete the widgets + KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(focusItem()); + for(int row = t->startRow(); row < t->startRow() + t->numRowsRegister(true); ++row) { + for(int col = 0; col < numCols(); ++col) { + if(cellWidget(row, col)) + clearCellWidget(row, col); + } + // make sure to reduce the possibly size to what it was before editing started + setRowHeight(row, t->rowHeightHint()); + } +} + +void Register::slotToggleErronousTransactions(void) +{ + // toggle switch + m_markErronousTransactions ^= 1; + + // check if anything needs to be redrawn + KMyMoneyRegister::RegisterItem* p = m_firstErronous; + while(p && p->prevItem() != m_lastErronous) { + if(p->isErronous()) + repaintItems(p); + p = p->nextItem(); + } + + // restart timer + QTimer::singleShot(500, this, SLOT(slotToggleErronousTransactions())); +} + +RegisterItem* Register::itemById(const QString& id) const +{ + if(id.isEmpty()) + return m_lastItem; + + for(QValueVector<RegisterItem*>::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; + if(!item) + continue; + if(item->id() == id) + return item; + } + return 0; +} + +void Register::handleItemChange(RegisterItem* old, bool shift, bool control) +{ + if(m_selectionMode == Multi) { + if(shift) { + selectRange(m_selectAnchor ? m_selectAnchor : old, + m_focusItem, false, true, (m_selectAnchor && !control) ? true : false); + } else if(!control) { + selectItem(m_focusItem, false); + } + } +} + +void Register::selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel) +{ + if(!from || !to) + return; + if(from == to && !includeFirst) + return; + bool swap = false; + if(to == from->prevItem()) + swap = true; + + RegisterItem* item; + if(!swap && from != to && from != to->prevItem()) { + bool found = false; + for(item = from; item; item = item->nextItem()) { + if(item == to) { + found = true; + break; + } + } + if(!found) + swap = true; + } + + if(swap) { + item = from; + from = to; + to = item; + if(!includeFirst) + to = to->prevItem(); + + } else if(!includeFirst) { + from = from->nextItem(); + } + + bool changed = false; + if(clearSel) { + for(item = firstItem(); item; item = item->nextItem()) { + if(item->isSelected() && item->isVisible()) { + item->setSelected(false); + changed = true; + } + } + } + + for(item = from; item; item = item->nextItem()) { + if(item->isSelectable()) { + if(!invert) { + if(!item->isSelected() && item->isVisible()) { + item->setSelected(true); + changed = true; + } + } else { + bool sel = !item->isSelected(); + if((item->isSelected() != sel) && (sel || !sel)) { + if(item->isVisible()) { + item->setSelected(sel); + changed = true; + } + } + } + } + if(item == to) + break; + } +} + +void Register::scrollPage(int key, ButtonState state) +{ + RegisterItem* oldFocusItem = m_focusItem; + + // make sure we have a focus item + if(!m_focusItem) + setFocusItem(m_firstItem); + if(!m_focusItem && m_firstItem) + setFocusItem(m_firstItem->nextItem()); + if(!m_focusItem) + return; + + RegisterItem* item = m_focusItem; + int height = 0; + + switch(key) { + case Qt::Key_PageUp: + while(height < visibleHeight() && item->prevItem()) { + do { + item = item->prevItem(); + if(item->isVisible()) + height += item->rowHeightHint(); + } while((!item->isSelectable() || !item->isVisible()) && item->prevItem()); + } + break; + case Qt::Key_PageDown: + while(height < visibleHeight() && item->nextItem()) { + do { + if(item->isVisible()) + height += item->rowHeightHint(); + item = item->nextItem(); + } while((!item->isSelectable() || !item->isVisible()) && item->nextItem()); + } + break; + + case Qt::Key_Up: + if(item->prevItem()) { + do { + item = item->prevItem(); + } while((!item->isSelectable() || !item->isVisible()) && item->prevItem()); + } + break; + + case Qt::Key_Down: + if(item->nextItem()) { + do { + item = item->nextItem(); + } while((!item->isSelectable() || !item->isVisible()) && item->nextItem()); + } + break; + + case Qt::Key_Home: + item = m_firstItem; + while((!item->isSelectable() || !item->isVisible()) && item->nextItem()) + item = item->nextItem(); + break; + + case Qt::Key_End: + item = m_lastItem; + while((!item->isSelectable() || !item->isVisible()) && item->prevItem()) + item = item->prevItem(); + break; + } + + // make sure to avoid selecting a possible empty transaction at the end + Transaction* t = dynamic_cast<Transaction*>(item); + if(t && t->transaction().id().isEmpty()) { + if(t->prevItem()) { + item = t->prevItem(); + } + } + + if(!(state & ShiftButton) || !m_selectAnchor) + m_selectAnchor = item; + + setFocusItem(item); + + if(item->isSelectable()) { + handleItemChange(oldFocusItem, state & Qt::ShiftButton, state & Qt::ControlButton); + // tell the world about the changes in selection + SelectedTransactions list(this); + emit selectionChanged(list); + } + + if(m_focusItem && !m_focusItem->isSelected() && m_selectionMode == Single) + selectItem(item); +} + +void Register::keyPressEvent(QKeyEvent* ev) +{ + switch(ev->key()) { + case Qt::Key_Space: + if(m_selectionMode != NoSelection) { + // get the state out of the event ... + m_buttonState = ev->state(); + // ... and pretend that we have pressed the left mouse button ;) + m_buttonState = static_cast<Qt::ButtonState>(m_buttonState | Qt::LeftButton); + selectItem(m_focusItem); + } + break; + + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Down: + case Qt::Key_Up: + scrollPage(ev->key(), ev->state()); + break; + + default: + QTable::keyPressEvent(ev); + break; + } +} + +Transaction* Register::transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) +{ + Transaction* t = 0; + MyMoneySplit s = split; + + if(parent->account() == MyMoneyAccount()) { + t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId); + return t; + } + + switch(parent->account().accountType()) { + case MyMoneyAccount::Checkings: + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::CreditCard: + case MyMoneyAccount::Loan: + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Currency: + case MyMoneyAccount::Income: + case MyMoneyAccount::Expense: + case MyMoneyAccount::AssetLoan: + case MyMoneyAccount::Equity: + if(s.accountId().isEmpty()) + s.setAccountId(parent->account().id()); + if(s.isMatched()) + t = new KMyMoneyRegister::StdTransactionMatched(parent, transaction, s, uniqueId); + else if(transaction.isImported()) + t = new KMyMoneyRegister::StdTransactionDownloaded(parent, transaction, s, uniqueId); + else + t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId); + break; + + case MyMoneyAccount::Investment: + if(s.isMatched()) + t = new KMyMoneyRegister::InvestTransaction/* Matched */(parent, transaction, s, uniqueId); + else if(transaction.isImported()) + t = new KMyMoneyRegister::InvestTransactionDownloaded(parent, transaction, s, uniqueId); + else + t = new KMyMoneyRegister::InvestTransaction(parent, transaction, s, uniqueId); + break; + + case MyMoneyAccount::CertificateDep: + case MyMoneyAccount::MoneyMarket: + case MyMoneyAccount::Stock: + default: + qDebug("Register::transactionFactory: invalid accountTypeE %d", parent->account().accountType()); + break; + } + return t; +} + +void Register::addGroupMarkers(void) +{ + QMap<QString, int> list; + QMap<QString, int>::const_iterator it; + KMyMoneyRegister::RegisterItem* p = firstItem(); + KMyMoneyRegister::Transaction* t; + QString name; + QDate today; + QDate yesterday, thisWeek, lastWeek; + QDate thisMonth, lastMonth; + QDate thisYear; + int weekStartOfs; + + switch(primarySortKey()) { + case KMyMoneyRegister::PostDateSort: + case KMyMoneyRegister::EntryDateSort: + today = QDate::currentDate(); + thisMonth.setYMD(today.year(), today.month(), 1); + lastMonth = thisMonth.addMonths(-1); + yesterday = today.addDays(-1); + // a = QDate::dayOfWeek() todays weekday (1 = Monday, 7 = Sunday) + // b = KLocale::weekStartDay() first day of week (1 = Monday, 7 = Sunday) + weekStartOfs = today.dayOfWeek() - KGlobal::locale()->weekStartDay(); + if(weekStartOfs < 0) { + weekStartOfs = 7 + weekStartOfs; + } + thisWeek = today.addDays(-weekStartOfs); + lastWeek = thisWeek.addDays(-7); + thisYear.setYMD(today.year(), 1, 1); + if(KMyMoneyGlobalSettings::startDate().date() != QDate(1900,1,1)) + new KMyMoneyRegister::FancyDateGroupMarker(this, KMyMoneyGlobalSettings::startDate().date(), i18n("Prior transactions possibly filtered")); + + if(KMyMoneyGlobalSettings::showFancyMarker()) { + if(m_account.lastReconciliationDate().isValid()) + new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, m_account.lastReconciliationDate(), i18n("Last reconciliation")); + + if(!m_account.value("lastImportedTransactionDate").isEmpty() + && !m_account.value("lastStatementBalance").isEmpty()) { + MyMoneyMoney balance(m_account.value("lastStatementBalance")); + if(m_account.accountGroup() == MyMoneyAccount::Liability) + balance = -balance; + QString txt = i18n("Online Statement Balance: %1").arg(balance.formatMoney(m_account.fraction())); + new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, QDate::fromString(m_account.value("lastImportedTransactionDate"), Qt::ISODate), txt); + } + + new KMyMoneyRegister::FancyDateGroupMarker(this, thisYear, i18n("This year")); + new KMyMoneyRegister::FancyDateGroupMarker(this, lastMonth, i18n("Last month")); + new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth, i18n("This month")); + new KMyMoneyRegister::FancyDateGroupMarker(this, lastWeek, i18n("Last week")); + new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek, i18n("This week")); + new KMyMoneyRegister::FancyDateGroupMarker(this, yesterday, i18n("Yesterday")); + new KMyMoneyRegister::FancyDateGroupMarker(this, today, i18n("Today")); + new KMyMoneyRegister::FancyDateGroupMarker(this, today.addDays(1), i18n("Future transactions")); + new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek.addDays(7), i18n("Next week")); + new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth.addMonths(1), i18n("Next month")); + + } else { + new KMyMoneyRegister::SimpleDateGroupMarker(this, today.addDays(1), i18n("Future transactions")); + } + if(KMyMoneyGlobalSettings::showFiscalMarker()) { + QDate currentFiscalYear(QDate::currentDate().year(), KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); + + if(QDate::currentDate() < currentFiscalYear) + currentFiscalYear = currentFiscalYear.addYears(-1); + QDate previousFiscalYear = currentFiscalYear.addYears(-1); + new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear, i18n("Current fiscal year")); + new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(-1), i18n("Previous fiscal year")); + new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(1), i18n("Next fiscal year")); + } + break; + + case KMyMoneyRegister::TypeSort: + if(KMyMoneyGlobalSettings::showFancyMarker()) { + new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Deposit, m_account.accountType()); + new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Payment, m_account.accountType()); + } + break; + + case KMyMoneyRegister::ReconcileStateSort: + if(KMyMoneyGlobalSettings::showFancyMarker()) { + new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::NotReconciled); + new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Cleared); + new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Reconciled); + new KMyMoneyRegister::ReconcileGroupMarker(this, MyMoneySplit::Frozen); + } + break; + + case KMyMoneyRegister::PayeeSort: + if(KMyMoneyGlobalSettings::showFancyMarker()) { + while(p) { + t = dynamic_cast<KMyMoneyRegister::Transaction*>(p); + if(t) { + list[t->sortPayee()] = 1; + } + p = p->nextItem(); + } + for(it = list.begin(); it != list.end(); ++it) { + name = it.key(); + if(name.isEmpty()) { + name = i18n("Unknown payee", "Unknown"); + } + new KMyMoneyRegister::PayeeGroupMarker(this, name); + } + } + break; + + case KMyMoneyRegister::CategorySort: + if(KMyMoneyGlobalSettings::showFancyMarker()) { + while(p) { + t = dynamic_cast<KMyMoneyRegister::Transaction*>(p); + if(t) { + list[t->sortCategory()] = 1; + } + p = p->nextItem(); + } + for(it = list.begin(); it != list.end(); ++it) { + name = it.key(); + if(name.isEmpty()) { + name = i18n("Unknown category", "Unknown"); + } + new KMyMoneyRegister::CategoryGroupMarker(this, name); + } + } + break; + + case KMyMoneyRegister::SecuritySort: + if(KMyMoneyGlobalSettings::showFancyMarker()) { + while(p) { + t = dynamic_cast<KMyMoneyRegister::InvestTransaction*>(p); + if(t) { + list[t->sortSecurity()] = 1; + } + p = p->nextItem(); + } + for(it = list.begin(); it != list.end(); ++it) { + name = it.key(); + if(name.isEmpty()) { + name = i18n("Unknown security", "Unknown"); + } + new KMyMoneyRegister::CategoryGroupMarker(this, name); + } + } + break; + + default: // no markers supported + break; + } +} + +void Register::removeUnwantedGroupMarkers(void) +{ + // remove all trailing group markers except statement markers + KMyMoneyRegister::RegisterItem* q; + KMyMoneyRegister::RegisterItem* p = lastItem(); + while(p) { + q = p; + if(dynamic_cast<KMyMoneyRegister::Transaction*>(p) + || dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(p)) + break; + + p = p->prevItem(); + delete q; + } + + // remove all adjacent group markers + bool lastWasGroupMarker = false; + p = lastItem(); + while(p) { + q = p; + KMyMoneyRegister::GroupMarker* m = dynamic_cast<KMyMoneyRegister::GroupMarker*>(p); + p = p->prevItem(); + if(m) { + m->markVisible(true); + // make adjacent group marker invisible except those that show statement information + if(lastWasGroupMarker && (dynamic_cast<KMyMoneyRegister::StatementGroupMarker*>(m) == 0)) { + m->markVisible(false); + } + lastWasGroupMarker = true; + } else if(q->isVisible()) + lastWasGroupMarker = false; + } +} + + +#include "register.moc" + +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/register.h b/kmymoney2/widgets/register.h new file mode 100644 index 0000000..d9d2627 --- /dev/null +++ b/kmymoney2/widgets/register.h @@ -0,0 +1,605 @@ +/*************************************************************************** + register.h + ---------- + begin : Fri Mar 10 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 REGISTER_H +#define REGISTER_H + +// Some STL headers in GCC4.3 contain operator new. Memory checker mangles these +#ifdef _CHECK_MEMORY + #undef new +#endif + +#include <algorithm> + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtable.h> +#include <qvaluelist.h> +#include <qvaluevector.h> +#include <qwidgetlist.h> +#include <qmap.h> +#include <qpair.h> +#include <qevent.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes +#ifdef _CHECK_MEMORY + #include <kmymoney/mymoneyutils.h> +#endif + +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/registeritem.h> +#include <kmymoney/transaction.h> +#include <kmymoney/transactioneditorcontainer.h> +#include <kmymoney/selectedtransaction.h> +#include <kmymoney/transactionsortoption.h> + +class RegisterToolTip; + +namespace KMyMoneyRegister { + +typedef enum { + UnknownSort = 0, //< unknown sort criteria + PostDateSort = 1, //< sort by post date + EntryDateSort, //< sort by entry date + PayeeSort, //< sort by payee name + ValueSort, //< sort by value + NoSort, //< sort by number field + EntryOrderSort, //< sort by entry order + TypeSort, //< sort by CashFlowDirection + CategorySort, //< sort by Category + ReconcileStateSort, //< sort by reconciliation state + SecuritySort, //< sort by security (only useful for investment accounts) + // insert new values in front of this line + MaxSortFields +} TransactionSortField; + +typedef enum { + Ascending = 0, //< sort in ascending order + Descending //< sort in descending order +} SortDirection; + +class Register; +class RegisterItem; +class ItemPtrVector; + +const QString sortOrderToText(TransactionSortField idx); +TransactionSortField textToSortOrder(const QString& text); + + +class QWidgetContainer : public QMap<QString, QWidget*> +{ +public: + QWidgetContainer() {} + + QWidget* haveWidget(const QString& name) const { + QWidgetContainer::const_iterator it_w; + it_w = find(name); + if(it_w != end()) + return *it_w; + return 0; + } + + void removeOrphans(void) { + QWidgetContainer::iterator it_w; + for(it_w = begin(); it_w != end(); ) { + if((*it_w) && (*it_w)->parent()) + ++it_w; + else { + delete (*it_w); + remove(it_w); + it_w = begin(); + } + } + } + +}; + +class GroupMarker : public RegisterItem +{ +public: + GroupMarker(Register* parent, const QString& txt = QString()); + ~GroupMarker(); + void setText(const QString& txt) { m_txt = txt; } + const QString& text(void) const { return m_txt; } + bool isSelectable(void) const { return false; } + bool canHaveFocus(void) const { return false; } + int numRows(void) const { return 1; } + + virtual const char* className(void) { return "GroupMarker"; } + + bool isErronous(void) const { return false; } + + void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + void paintFormCell(QPainter* /* painter */, int /* row */, int /* col */, const QRect& /* r */, bool /* selected */, const QColorGroup& /* cg */) {} + + int rowHeightHint(void) const; + + bool matches(const QString&) const { return true; } + virtual int sortSamePostDate(void) const { return 0; } + +protected: + void setupColors(QColorGroup& cg); + +protected: + QString m_txt; + unsigned int m_drawCounter; + bool m_showDate; + + static QPixmap* m_bg; + static int m_bgRefCnt; +}; + + +class FancyDateGroupMarker : public GroupMarker +{ +public: + FancyDateGroupMarker(Register* parent, const QDate& date, const QString& txt); + + virtual const QDate& sortPostDate(void) const { return m_date; } + virtual const QDate& sortEntryDate(void) const { return m_date; } + virtual const char* className(void) { return "FancyDateGroupMarker"; } +private: + QDate m_date; +}; + +class StatementGroupMarker : public FancyDateGroupMarker +{ +public: + StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt ); + CashFlowDirection sortType(void) const { return m_dir; } + virtual int sortSamePostDate(void) const { return 3; } +private: + CashFlowDirection m_dir; +}; + +class SimpleDateGroupMarker : public FancyDateGroupMarker +{ +public: + SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt); + void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + int rowHeightHint(void) const; + virtual const char* className(void) { return "SimpleDateGroupMarker"; } +}; + +class TypeGroupMarker : public GroupMarker +{ +public: + TypeGroupMarker(Register* parent, CashFlowDirection dir, MyMoneyAccount::accountTypeE accType); + CashFlowDirection sortType(void) const { return m_dir; } +private: + CashFlowDirection m_dir; +}; + +class FiscalYearGroupMarker : public FancyDateGroupMarker +{ +public: + FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt); + virtual const char* className(void) { return "FiscalYearGroupMarker"; } + virtual int sortSamePostDate(void) const { return 1; } + +protected: + void setupColors(QColorGroup& cg); +}; + +class PayeeGroupMarker : public GroupMarker +{ +public: + PayeeGroupMarker(Register* parent, const QString& name); + const QString& sortPayee(void) const { return m_txt; } +}; + +class CategoryGroupMarker : public GroupMarker +{ +public: + CategoryGroupMarker(Register* parent, const QString& category); + const QString& sortCategory(void) const { return m_txt; } + const QString& sortSecurity(void) const { return m_txt; } + + virtual const char* className(void) { return "CategoryGroupMarker"; } +}; + +class ReconcileGroupMarker : public GroupMarker +{ +public: + ReconcileGroupMarker(Register* parent, MyMoneySplit::reconcileFlagE state); + virtual MyMoneySplit::reconcileFlagE sortReconcileState(void) const { return m_state; } +private: + MyMoneySplit::reconcileFlagE m_state; +}; + + +class ItemPtrVector : public QValueVector<RegisterItem *> +{ +public: + ItemPtrVector() {} + + void sort(void); + +protected: + /** + * sorter's compare routine. Returns true if i1 < i2 + */ + static bool item_cmp(RegisterItem* i1, RegisterItem* i2); +}; + + +class Register : public TransactionEditorContainer +{ + Q_OBJECT + + // friend class QHeader; + // friend class QTableHeader; + // friend class RegisterItem; + friend class Transaction; + friend class StdTransaction; + friend class InvestTransaction; + +public: + Register(QWidget *parent = 0, const char *name = 0); + virtual ~Register(); + + /** + * add the item @a p to the register + */ + void addItem(RegisterItem* p); + + /** + * insert the item @a p into the register after item @a q + */ + void insertItemAfter(RegisterItem* p, RegisterItem* q); + + /** + * remove the item @p from the register + */ + void removeItem(RegisterItem* p); + + /** + * This method returns a list of pointers to all selected items + * in the register + * + * @retval QValueList<RegisterItem*> + */ + QValueList<RegisterItem*> selectedItems(void) const; + + /** + * Construct a list of all currently selected transactions in the register. + * If the current item carrying the focus (see focusItem() ) is selected + * it will be the first one contained in the list. + * + * @param list reference to QValueList receiving the SelectedTransaction()'s + */ + void selectedTransactions(SelectedTransactions& list) const; + + QString text(int row, int col) const; + QWidget* createEditor(int row, int col, bool initFromCell) const; + void setCellContentFromEditor(int row, int col); + QWidget* cellWidget(int row, int col) const; + void endEdit(int row, int col, bool accept, bool replace); + void paintCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + + void resizeData(int) {} + QTableItem* item(int, int) { return 0; } + void setItem(int, int, QTableItem*) {} + void clearCell(int, int) {} + void clearCellWidget(int, int); + + /** + * Override the QTable member function to avoid display of focus + */ + void paintFocus(QPainter*, const QRect& ) {} + + /** + * Override the QTable member function to avoid functionality + */ + void updateCell(int /* row */, int /* col */) {} + + RegisterItem* focusItem(void) const { return m_focusItem; } + RegisterItem* anchorItem(void) const { return m_selectAnchor; } + + /** + * set focus to specific item. + * @return true if the item got focus + */ + bool setFocusItem(RegisterItem* focusItem); + + void setAnchorItem(RegisterItem* anchorItem); + + /** + * Set focus to the first focussable item + * @return true if a focussable item was found + */ + bool setFocusToTop(void); + + /** + * Select @a item and unselect all others if @a dontChangeSelections + * is @a false. If m_buttonState differs from Qt::NoButton (method is + * called as a result of a mouse button press), then the setting of + * @a dontChangeSelections has no effect. + */ + void selectItem(RegisterItem* item, bool dontChangeSelections = false); + + /** + * Clears all items in the register. All objects + * added to the register will be deleted. + */ + void clear(void); + + void updateRegister(bool forceUpdateRowHeight = false); + + /** + * Assign all visible items an alternate background color + */ + void updateAlternate(void) const; + + /** + * make sure, we only show a single marker in a row + * through hiding unused ones + */ + void suppressAdjacentMarkers(void); + + /** + * Adjusts column @a col so that all data fits in width. + */ + void adjustColumn(int col); + + /** + * Convenience method to setup the register to show the columns + * based on the account type of @a account. If @a showAccountColumn + * is @a true then the account column is shown independant of the + * account type. If @a account does not have an @a id, all columns + * will be hidden. + */ + void setupRegister(const MyMoneyAccount& account, bool showAccountColumn = false); + + /** + * Show the columns contained in @a cols for @a account. @a account + * can be left empty ( MyMoneyAccount() ) e.g. for the search dialog. + */ + void setupRegister(const MyMoneyAccount& account, const QValueList<Column>& cols); + + void setSortOrder(const QString& order); + const QValueList<TransactionSortField>& sortOrder(void) const { return m_sortOrder; } + TransactionSortField primarySortKey(void) const; + void sortItems(void); + + /** + * This member returns the last visible column that is used by the register + * after it has been setup using setupRegister(). + * + * @return last actively used column (base 0) + */ + Column lastCol(void) const { return m_lastCol; } + + RegisterItem* firstItem(void) const; + RegisterItem* firstVisibleItem(void) const; + RegisterItem* nextItem(RegisterItem*) const; + RegisterItem* lastItem(void) const; + RegisterItem* lastVisibleItem(void) const; + RegisterItem* prevItem(RegisterItem*) const; + RegisterItem* itemAtRow(int row) const; + + void resize(int col); + + void forceUpdateLists(void) { m_listsDirty = true; } + + void ensureItemVisible(RegisterItem* item); + + void arrangeEditWidgets(QMap<QString, QWidget*>& editWidgets, Transaction* t); + void removeEditWidgets(QMap<QString, QWidget*>& editWidgets); + void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; + + int rowHeightHint(void) const; + + void clearSelection(void); + + bool markErronousTransactions(void) const { return (m_markErronousTransactions & 0x01) != 0; } + + /** + * This method creates a specifc transaction according to the + * transaction passed in @a transaction. + * + * @param parent pointer to register where the created object should be added + * @param transaction the transaction which should be used to create the object + * @param split the split of the transaction which should be used to create the object + * @param uniqueId an int that will be used to construct the id of the item + * + * @return pointer to created object (0 upon failure) + */ + static Transaction* transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + + const MyMoneyAccount& account(void) const { return m_account; } + + void repaintItems(RegisterItem* first = 0, RegisterItem* last = 0); + + unsigned int drawCounter(void) const { return m_drawCounter; } + + /** + * This method creates group marker items and adds them to the register + */ + void addGroupMarkers(void); + + /** + * This method removes all trailing group markers and in a second + * run reduces all adjacent group markers to show only one. In that + * case the last one will remain. + */ + void removeUnwantedGroupMarkers(void); + + void setLedgerLensForced(bool forced=true) { m_ledgerLensForced = forced; } + + /** + * Sets the selection mode to @a mode. Supported modes are QTable::Single and + * QTable::Multi. QTable::Multi is the default when the object is created. + */ + void setSelectionMode(SelectionMode mode) { m_selectionMode = mode; } + +protected: + + void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + + void contentsMouseReleaseEvent( QMouseEvent *e ); + + void unselectItems(int from = -1, int to = -1) { doSelectItems(from, to, false); } + void selectItems(int from, int to) { doSelectItems(from, to, true); } + void doSelectItems(int from, int to, bool selected); + int selectedItemsCount(void) const; + + void focusOutEvent(QFocusEvent*); + void focusInEvent(QFocusEvent*); + void keyPressEvent(QKeyEvent*); + + int rowToIndex(int row) const; + void setupItemIndex(int rowCount); + + /** + * This method determines the register item that is one page + * further down or up in the ledger from the previous focus item. + * The height to scroll is determined by visibleHeight() + * + * @param key Qt::Page_Up or Qt::Page_Down depending on the direction to scroll + * @param state state of Qt::ShiftButton, Qt::ControlButton, Qt::AltButton and + * Qt::MetaButton. + */ + void scrollPage(int key, ButtonState state); + + /** + * This method determines the pointer to a RegisterItem + * based on the item's @a id. If @a id is empty, this method + * returns @a m_lastItem. + * + * @param id id of the item to be searched + * @return pointer to RegisterItem or 0 if not found + */ + RegisterItem* itemById(const QString& id) const; + + void insertWidget(int row, int col, QWidget* w); + + /** + * Override logic and use standard QFrame behaviour + */ + bool focusNextPrevChild(bool next); + + bool eventFilter(QObject* o, QEvent* e); + + void handleItemChange(RegisterItem* old, bool shift, bool control); + + void selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel); + + // DND + void dragMoveEvent(QDragMoveEvent* event); + void dropEvent(QDropEvent* event); + Transaction* dropTransaction(QPoint cPos) const; + +protected slots: + void resize(void); + + void selectItem(int row, int col, int button, const QPoint & mousePos ); + void slotEnsureItemVisible(void); + void slotDoubleClicked(int, int, int, const QPoint&); + + void slotToggleErronousTransactions(void); + void slotAutoColumnSizing(int section); + +signals: + void selectionChanged(void); + void selectionChanged(const KMyMoneyRegister::SelectedTransactions& list); + /** + * This signal is emitted when the focus and selection changes to @p item. + * + * @param item pointer to transaction that received the focus and was selected + */ + void focusChanged(KMyMoneyRegister::Transaction* item); + + /** + * This signal is emitted when the focus changes but the selection remains + * the same. This usually happens when the focus is changed using the keyboard. + */ + void focusChanged(void); + + /** + * This signal is emitted when an @p item is about to be selected. The boolean + * @p okToSelect is preset to @c true. If the @p item should not be selected + * for whatever reason, the boolean @p okToSelect should be reset to @c false + * by the connected slot. + */ + void aboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect); + + void editTransaction(void); + void headerClicked(void); + + /** + * This signal is sent out when the user clicks on the ReconcileStateColumn and + * only a single transaction is selected. + */ + void reconcileStateColumnClicked(KMyMoneyRegister::Transaction* item); + + /** + * This signal is sent out, if an item without a transaction id has been selected. + */ + void emptyItemSelected(void); + + /** + * This signal is sent out, if the user selects an item with the right mouse button + */ + void openContextMenu(void); + + /** + * This signal is sent out when a new item has been added to the register + */ + void itemAdded(RegisterItem* item); + +protected: + ItemPtrVector m_items; + QValueVector<RegisterItem*> m_itemIndex; + RegisterItem* m_selectAnchor; + RegisterItem* m_focusItem; + RegisterItem* m_ensureVisibleItem; + RegisterItem* m_firstItem; + RegisterItem* m_lastItem; + RegisterItem* m_firstErronous; + RegisterItem* m_lastErronous; + + int m_markErronousTransactions; + int m_rowHeightHint; + + MyMoneyAccount m_account; + + bool m_ledgerLensForced; + SelectionMode m_selectionMode; + +private: + bool m_listsDirty; + bool m_ignoreNextButtonRelease; + bool m_needInitialColumnResize; + Qt::ButtonState m_buttonState; + Column m_lastCol; + QValueList<TransactionSortField> m_sortOrder; + QMap<QPair<int, int>, QWidget*> m_cellWidgets; + RegisterToolTip* m_tooltip; + QRect m_lastRepaintRect; + unsigned int m_drawCounter; +}; + +} // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/registeritem.cpp b/kmymoney2/widgets/registeritem.cpp new file mode 100644 index 0000000..6ef082f --- /dev/null +++ b/kmymoney2/widgets/registeritem.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + registeritem.cpp - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/registeritem.h> +#include <kmymoney/register.h> + +#include "../kmymoneyglobalsettings.h" + +using namespace KMyMoneyRegister; + +QDate RegisterItem::nullDate; +MyMoneyMoney RegisterItem::nullValue; + +RegisterItem::RegisterItem() : + m_parent(0), + m_prev(0), + m_next(0) +{ + init(); +} + +RegisterItem::RegisterItem(Register* parent) : + m_parent(parent), + m_prev(0), + m_next(0) +{ + init(); + parent->addItem(this); +} + +void RegisterItem::init(void) +{ + m_startRow = 0; + m_rowsRegister = 1; + m_rowsForm = 1; + m_visible = true; +} + +RegisterItem::~RegisterItem() +{ + m_parent->removeItem(this); +} + +void RegisterItem::setParent(Register* parent) +{ + m_parent = parent; +} + +void RegisterItem::setNumRowsRegister(int rows) +{ + if(rows != m_rowsRegister) { + m_rowsRegister = rows; + if(m_parent) + m_parent->forceUpdateLists(); + } +} + +bool RegisterItem::markVisible(bool visible) +{ + if(m_visible == visible) + return false; + m_visible = visible; + return true; +} + +void RegisterItem::setVisible(bool visible) +{ + if(markVisible(visible) && m_parent) { + if(visible) { + for(int i = startRow(); i < startRow() + numRowsRegister(); ++i) { + m_parent->showRow(i); + m_parent->setRowHeight(i, rowHeightHint()); + } + } else { + for(int i = startRow(); i < startRow() + numRowsRegister(); ++i) { + m_parent->hideRow(i); + } + } + } +} + +int RegisterItem::rowHeightHint(void) const +{ + if(!m_visible) + return 0; + + if(m_parent) { + return m_parent->rowHeightHint(); + } + + QFontMetrics fm( KMyMoneyGlobalSettings::listCellFont() ); + return fm.lineSpacing()+6; +} diff --git a/kmymoney2/widgets/registeritem.h b/kmymoney2/widgets/registeritem.h new file mode 100644 index 0000000..84afa84 --- /dev/null +++ b/kmymoney2/widgets/registeritem.h @@ -0,0 +1,226 @@ +/*************************************************************************** + registeritem.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <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 REGISTERITEM_H +#define REGISTERITEM_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> +#include <qdatetime.h> +#include <qpainter.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneymoney.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/mymoneyobject.h> + +namespace KMyMoneyRegister { + +typedef enum { + Deposit = 0, //< transaction is deposit + Payment, //< transaction is payment + Unknown //< transaction cashflow is unknown +} CashFlowDirection; + +typedef enum { + ActionNone = -1, + ActionCheck = 0, + /* these should be values which qt 3.3 never uses for QTab: + * qt starts upwards from 0 + */ + ActionDeposit = 12201, + ActionTransfer = 12202, + ActionWithdrawal = 12203, + ActionAtm, + // insert new values above this line + MaxAction +} Action; + + +class Register; + +/** + * @author Thomas Baumgart + */ +class RegisterItem +{ +public: + RegisterItem(); + RegisterItem(Register* parent); + virtual ~RegisterItem(); + + virtual const char* className(void) = 0; + + virtual bool isSelectable(void) const = 0; + virtual bool isSelected(void) const { return false; } + virtual void setSelected(bool /* selected*/) {} + + virtual bool canHaveFocus(void) const = 0; + virtual bool hasFocus(void) const { return false; } + virtual bool hasEditorOpen(void) const { return false; } + + virtual void setFocus(bool /*focus*/, bool updateLens = true) { updateLens = false; } + + virtual bool isErronous(void) const = 0; + + // helper functions used for sorting + virtual const QDate& sortPostDate(void) const { return nullDate; } + virtual int sortSamePostDate(void) const = 0; + virtual const QDate& sortEntryDate(void) const { return nullDate; } + virtual const QString& sortPayee(void) const { return QString::null; } + virtual const MyMoneyMoney& sortValue(void) const { return nullValue; } + virtual const QString& sortNumber(void) const { return QString::null; } + virtual const QString& sortEntryOrder(void) const { return QString::null; } + virtual CashFlowDirection sortType(void) const { return Deposit; } + virtual const QString& sortCategory(void) const { return QString::null; } + virtual MyMoneySplit::reconcileFlagE sortReconcileState(void) const { return MyMoneySplit::MaxReconcileState; } + virtual const QString& sortSecurity(void) const { return QString::null; } + + /** + * This method sets the row offset of the item in the register + * to row. + * + * @param row row offset + * + * @note The row offset is based on QTable rows, not register + * items. + */ + virtual void setStartRow(int row) { m_startRow = row; } + int startRow(void) const { return m_startRow; } + virtual int rowHeightHint(void) const; + + /** + * This method modifies the number of rows required to display this item + * in a Register. + * It calls Register::forceUpdateLists() when the number differs. + */ + virtual void setNumRowsRegister(int rows); + + /** + * This method modifies the number of rows required to display this item + * in a Form. + */ + virtual void setNumRowsForm(int rows) { m_rowsForm = rows; } + + /** + * This method returns the number of rows required to display this item + * in a Register + */ + virtual int numRowsRegister(void) const { return m_rowsRegister; } + + /** + * This method returns the number of rows required to display this item + * in a Form + */ + virtual int numRowsForm(void) const { return m_rowsForm; } + virtual int numColsForm(void) const { return 1; } + + /** + * This method sets up the register item to be shown in normal (@p alternate = @p false) + * or alternate (@p alternate = @p true) background. + * + * @param alternate selects normal or alternate background + */ + virtual void setAlternate(bool alternate) { m_alternate = alternate; } + + virtual void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg) = 0; + virtual void paintFormCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg) = 0; + + virtual const QString& id(void) const { return MyMoneyObject::emptyId(); } + + /** + * Sets the parent of this item to be the register @p parent + * + * @param parent pointer to register + */ + void setParent(Register* parent); + + /** + * This member returns a pointer to the parent object + * + * @retval pointer to Register + */ + Register* parent(void) const { return m_parent; } + + void setNeedResize(void) { m_needResize = true; } + + bool isVisible(void) const { return m_visible; } + + /** + * Marks the item visible depending on @a visible and + * updates the underlying register object + */ + virtual void setVisible(bool visible); + + /** + * Marks the item visible depending on @a visible but + * does not update the underlying register object. Returns + * true, if visibility has changed. + */ + virtual bool markVisible(bool visible); + + void setNextItem(RegisterItem* p) { m_next = p; } + void setPrevItem(RegisterItem* p) { m_prev = p; } + RegisterItem* nextItem(void) const { return m_next; } + RegisterItem* prevItem(void) const { return m_prev; } + + virtual bool matches(const QString&) const = 0; + + /** + * Checks if the mouse hovered over an area that has a tooltip associated with it. + * The mouse position is given in relative coordinates to the @a startRow and the + * @a row and @a col of the item are also passed as relative values. + * + * If a tooltip shall be shown, this method presets the rectangle @a r with the + * area in register coordinates and @a msg with the string that will be passed + * to QToolTip::tip. @a true is returned in this case. + * + * If no tooltip is available, @a false will be returned. + */ + virtual bool maybeTip(const QPoint& /* relpos */, int /* row */, int /* col */, QRect& /* r */, QString& /* msg */) { return false; } + +protected: + /// This method serves as helper for all constructors + void init(void); + +protected: + Register* m_parent; + RegisterItem* m_prev; + RegisterItem* m_next; + int m_startRow; + int m_rowsRegister; + int m_rowsForm; + bool m_alternate; + bool m_needResize; + bool m_visible; + +private: + static QDate nullDate; + static MyMoneyMoney nullValue; +}; + +} // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/registersearchline.cpp b/kmymoney2/widgets/registersearchline.cpp new file mode 100644 index 0000000..f0ef6e9 --- /dev/null +++ b/kmymoney2/widgets/registersearchline.cpp @@ -0,0 +1,301 @@ +/*************************************************************************** + registersearchline.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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qapplication.h> +#include <qlabel.h> +#include <qtoolbutton.h> +#include <qtimer.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <ktoolbar.h> +#include <ktoolbarbutton.h> +#include <kiconloader.h> +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <registersearchline.h> + +using namespace KMyMoneyRegister; + +class RegisterSearchLine::RegisterSearchLinePrivate +{ +public: + RegisterSearchLinePrivate() : + reg(0), + combo(0), + queuedSearches(0), + status(0) {} + + Register* reg; + QComboBox* combo; + QString search; + int queuedSearches; + int status; +}; + +RegisterSearchLine::RegisterSearchLine(QWidget* parent, Register* reg, const char* name) : + KLineEdit(parent, name), + d(new RegisterSearchLinePrivate) +{ + init(reg); +} + +RegisterSearchLine::RegisterSearchLine(QWidget* parent, const char* name) : + KLineEdit(parent, name), + d(new RegisterSearchLinePrivate) +{ + init(0); +} + +void RegisterSearchLine::init(Register *reg) +{ + d->reg = reg; + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(queueSearch(const QString&))); + + QLabel* label = new QLabel(i18n("label for status combo", "Stat&us"), parentWidget()); + d->combo = new QComboBox(parentWidget()); + // don't change the order of the following lines unless updating + // the case labels in RegisterSearchLine::itemMatches() at the same time + d->combo->insertItem(SmallIcon("run"), i18n("Any status")); + d->combo->insertItem(SmallIcon("fileimport"), i18n("Imported")); + d->combo->insertItem(SmallIcon("connect_creating"), i18n("Matched")); + d->combo->insertItem(SmallIcon("attention"), i18n("Erroneous")); + d->combo->insertItem(i18n("Not marked")); + d->combo->insertItem(i18n("Not reconciled")); + d->combo->insertItem(i18n("Cleared")); + d->combo->setCurrentItem(0); + connect(d->combo, SIGNAL(activated(int)), this, SLOT(slotStatusChanged(int))); + + label->setBuddy(d->combo); + + if(reg) { + connect(reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); + connect(reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + } else { + setEnabled(false); + } +} + +RegisterSearchLine::~RegisterSearchLine() +{ + delete d; +} + +void RegisterSearchLine::setRegister(Register* reg) +{ + if(d->reg) { + disconnect(d->reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); + disconnect(d->reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + } + + d->reg = reg; + + if(reg) { + connect(reg, SIGNAL(destroyed()), this, SLOT(registerDestroyed())); + connect(reg, SIGNAL(itemAdded(RegisterItem*)), this, SLOT(itemAdded(RegisterItem*))); + } + + setEnabled(reg != 0); +} + +void RegisterSearchLine::slotStatusChanged(int status) +{ + d->status = status; + updateSearch(); +} + +void RegisterSearchLine::queueSearch(const QString& search) +{ + d->queuedSearches++; + d->search = search; + QTimer::singleShot(200, this, SLOT(activateSearch())); +} + +void RegisterSearchLine::activateSearch(void) +{ + --(d->queuedSearches); + if(d->queuedSearches == 0) + updateSearch(d->search); +} + +void RegisterSearchLine::updateSearch(const QString& s) +{ + if(!d->reg) + return; + + d->search = s.isNull() ? text() : s; + + // keep track of the current focus item + RegisterItem* focusItem = d->reg->focusItem(); + + bool enabled = d->reg->isUpdatesEnabled(); + d->reg->setUpdatesEnabled(false); + + bool scrollBarVisible = d->reg->verticalScrollBar()->isVisible(); + + RegisterItem* p = d->reg->firstItem(); + for(; p; p = p->nextItem()) { + p->setVisible(itemMatches(p, d->search)); + } + d->reg->suppressAdjacentMarkers(); + d->reg->updateAlternate(); + d->reg->setUpdatesEnabled(enabled); + + // if focus item is still visible, then make sure we have + // it on screen + if(focusItem && focusItem->isVisible()) { + d->reg->updateContents(); + d->reg->ensureItemVisible(focusItem); + } else + d->reg->repaintContents(); + + d->reg->updateScrollBars(); + + // if the scrollbar's visibility changed, we need to resize the contents + if(scrollBarVisible != d->reg->verticalScrollBar()->isVisible()) { + d->reg->resize(DetailColumn); + } +} + +bool RegisterSearchLine::itemMatches(const RegisterItem* item, const QString& s) const +{ + const Transaction* t = dynamic_cast<const Transaction*>(item); + if(t && !t->transaction().id().isEmpty()) { + // Keep the case list of the following switch statement + // in sync with the logic to fill the combo box in + // RegisterSearchLine::init() + switch(d->status) { + default: + break; + case 1: // Imported + if(!t->transaction().isImported()) + return false; + break; + case 2: // Matched + if(!t->split().isMatched()) + return false; + break; + case 3: // Erroneous + if(t->transaction().splitSum().isZero()) + return false; + break; + case 4: // Not marked + if(t->split().reconcileFlag() != MyMoneySplit::NotReconciled) + return false; + break; + case 5: // Not reconciled + if(t->split().reconcileFlag() != MyMoneySplit::NotReconciled + && t->split().reconcileFlag() != MyMoneySplit::Cleared) + return false; + break; + case 6: // Cleared + if(t->split().reconcileFlag() != MyMoneySplit::Cleared) + return false; + break; + } + } + + return item->matches(s); +} + +void RegisterSearchLine::reset(void) +{ + clear(); + d->combo->setCurrentItem(0); + slotStatusChanged(0); +} + +void RegisterSearchLine::itemAdded(RegisterItem* item) const +{ + item->setVisible(itemMatches(item, text())); +} + +void RegisterSearchLine::registerDestroyed(void) +{ + d->reg = 0; + setEnabled(false); +} + + +class RegisterSearchLineWidget::RegisterSearchLineWidgetPrivate +{ + public: + RegisterSearchLineWidgetPrivate() : + reg(0), + searchLine(0), + clearButton(0) {} + + Register* reg; + RegisterSearchLine* searchLine; + QToolButton* clearButton; +}; + + +RegisterSearchLineWidget::RegisterSearchLineWidget(Register* reg, QWidget* parent, const char* name) : + QHBox(parent, name), + d(new RegisterSearchLineWidgetPrivate) +{ + d->reg = reg; + setSpacing(5); + QTimer::singleShot(0, this, SLOT(createWidgets())); +} + +RegisterSearchLineWidget::~RegisterSearchLineWidget() +{ + delete d; +} + +RegisterSearchLine* RegisterSearchLineWidget::createSearchLine(Register* reg) +{ + if(!d->searchLine) + d->searchLine = new RegisterSearchLine(this, reg); + return d->searchLine; +} + +void RegisterSearchLineWidget::createWidgets(void) +{ + if(!d->clearButton) { + d->clearButton = new QToolButton(this); + QIconSet icon = SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"); + d->clearButton->setIconSet(icon); + } + + d->clearButton->show(); + + QLabel *label = new QLabel(i18n("S&earch:"), this, "kde toolbar widget"); + + d->searchLine = createSearchLine(d->reg); + d->searchLine->show(); + + label->setBuddy(d->searchLine); + label->show(); + + connect(d->clearButton, SIGNAL(clicked()), d->searchLine, SLOT(reset())); +} + +RegisterSearchLine* RegisterSearchLineWidget::searchLine(void) const +{ + return d->searchLine; +} + +#include "registersearchline.moc" diff --git a/kmymoney2/widgets/registersearchline.h b/kmymoney2/widgets/registersearchline.h new file mode 100644 index 0000000..d495ab1 --- /dev/null +++ b/kmymoney2/widgets/registersearchline.h @@ -0,0 +1,151 @@ +/*************************************************************************** + registersearchline.h + ------------------- + begin : Sun Jan 14 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 REGISTERSEARCHLINE_H +#define REGISTERSEARCHLINE_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qhbox.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klineedit.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/register.h> +#include <kmymoney/export.h> + +namespace KMyMoneyRegister { + +/** + * This class makes it easy to add a search line for filtering the items + * in a register based on simple text. Inspired by the idea of the kdelibs + * class KListViewSearchLine. + * + * @author Thomas Baumgart + */ +class KMYMONEY_EXPORT RegisterSearchLine : public KLineEdit +{ + Q_OBJECT +public: + /** + * Constructs a RegisterSearchLine with @a reg being the register to be + * filtered. + * + * If @a reg is null then the widget will be disabled until a register + * is set with setRegister(). + */ + RegisterSearchLine(QWidget* parent = 0, Register* reg = 0, const char* name = 0); + + /** + * Constructs a RegisterSearchLine + * + * The widget will be disabled until a register is set with setRegister(). + */ + RegisterSearchLine(QWidget* parent = 0, const char* name = 0); + + /** + * Destroys the object + */ + ~RegisterSearchLine(); + + /** + * Sets the KMyMoneyRegister that is filtered by this search line. + * If @a reg is null then the widget will be disabled. + * + * @see KMyMoneyRegister() + */ + void setRegister(Register* reg); + +protected: + virtual bool itemMatches(const RegisterItem* item, const QString& s) const; + +public slots: + virtual void updateSearch(const QString& s = QString::null); + virtual void reset(void); + +protected slots: + void queueSearch(const QString& search); + void activateSearch(void); + void slotStatusChanged(int); + +private slots: + void itemAdded(RegisterItem* item) const; + void registerDestroyed(void); + +private: + void init(Register* reg); + +private: + class RegisterSearchLinePrivate; + RegisterSearchLinePrivate* const d; +}; + +/** + * Creates a widget containing a RegisterSearchLine, a label with the text + * "Search" and a button to clear the search. Modelled after KListViewSearchLineWidget. + * + * @author Thomas Baumgart + */ +class KMYMONEY_EXPORT RegisterSearchLineWidget : public QHBox +{ + Q_OBJECT +public: + /** + * Creates a RegisterSearchLineWidget for @a reg with @a parent as the + * parent and with @a name. + */ + RegisterSearchLineWidget(Register* reg = 0, QWidget* parent = 0, const char* name = 0); + + /** + * Destroys the object + */ + ~RegisterSearchLineWidget(); + + /** + * Returns a pointer to the searchline + */ + RegisterSearchLine* searchLine() const; + + /** + * Creates the search line. This can be useful to reimplement in cases where + * a RegisterSearchLine subclass is used. + */ + virtual RegisterSearchLine* createSearchLine(Register* reg); + +protected slots: + /** + * Creates the widgets inside of the widget. This is called from the + * constructor via a single shot timer so that it is guaranteed to run + * after construction is complete. This makes it suitable for overriding in + * subclasses. + */ + virtual void createWidgets(void); + +private: + class RegisterSearchLineWidgetPrivate; + RegisterSearchLineWidgetPrivate* const d; +}; + +} // namespace + +#endif diff --git a/kmymoney2/widgets/scheduledtransaction.cpp b/kmymoney2/widgets/scheduledtransaction.cpp new file mode 100644 index 0000000..98d6787 --- /dev/null +++ b/kmymoney2/widgets/scheduledtransaction.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + scheduledtransaction.cpp + ------------------- + begin : Tue Aug 19 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <scheduledtransaction.h> +#include <kmymoney/kmymoneyglobalsettings.h> +#include <kmymoney/register.h> + +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +StdTransactionScheduled::StdTransactionScheduled(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + StdTransaction(parent, transaction, split, uniqueId), + m_drawCounter(parent->drawCounter()-1) +{ + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); +} + +bool StdTransactionScheduled::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) +{ + QRect r(cellRect); + cg = m_parent->palette().disabled(); + + bool rc = Transaction::paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, brush); + return rc; +} + + diff --git a/kmymoney2/widgets/scheduledtransaction.h b/kmymoney2/widgets/scheduledtransaction.h new file mode 100644 index 0000000..836ceee --- /dev/null +++ b/kmymoney2/widgets/scheduledtransaction.h @@ -0,0 +1,86 @@ +/*************************************************************************** + scheduledtransaction.h + ------------------- + begin : Tue Aug 19 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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 SCHEDULEDTRANSACTION_H +#define SCHEDULEDTRANSACTION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transaction.h> + +namespace KMyMoneyTransactionForm { + class TransactionForm; +}; // namespace + +namespace KMyMoneyRegister { + +class StdTransactionScheduled : public StdTransaction +{ +public: + StdTransactionScheduled(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionScheduled() {} + + virtual const char* className(void) { return "StdTransactionScheduled"; } + + /** + * This method sets the general paramaters required for the painting of a cell + * in the register. These are: + * + * - background color (alternating) + * - background color (imported transaction) + * - background color (matched transaction) + * - background color (selected transaction) + * - cellRect (area covering the cell) + * - textRect (area covering the text) + * - color of the pen to do the painting of text and lines + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param cellRect ref to QRect object receiving the area information for the cell + * @param textRect ref to QRect object receiving the area information for the text + * @param cg ref to QColorGroup object receiving the color information to be used + * @param brush ref to QBrush object receiveing the brush information to be used + */ + virtual bool paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush); + + bool isSelectable(void) const { return true; } + bool canHaveFocus(void) const { return true; } + virtual bool isScheduled(void) const { return true; } + + virtual int sortSamePostDate(void) const { return 4; } + +// virtual void paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) const; + +// void registerCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + +private: + unsigned int m_drawCounter; +}; + +}; // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: + diff --git a/kmymoney2/widgets/selectedtransaction.cpp b/kmymoney2/widgets/selectedtransaction.cpp new file mode 100644 index 0000000..055dc3d --- /dev/null +++ b/kmymoney2/widgets/selectedtransaction.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + selectedtransaction.cpp - description + ------------------- + begin : Fri Jun 2008 + copyright : (C) 2000-2008 by Thomas Baumgart + email : Thomas Baumgart <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 "selectedtransaction.h" + +#include "register.h" +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/mymoneyfile.h> + +namespace KMyMoneyRegister { + +int SelectedTransaction::warnLevel() const +{ + int warnLevel = 0; + QValueList<MyMoneySplit>::const_iterator it_s; + for(it_s = transaction().splits().begin(); warnLevel < 2 && it_s != transaction().splits().end(); ++it_s) { + const MyMoneyAccount& acc = MyMoneyFile::instance()->account((*it_s).accountId()); + if(acc.isClosed()) + warnLevel = 3; + else if((*it_s).reconcileFlag() == MyMoneySplit::Frozen) + warnLevel = 2; + else if((*it_s).reconcileFlag() == MyMoneySplit::Reconciled && warnLevel < 1) + warnLevel = 1; + } + return warnLevel; +} + +SelectedTransactions::SelectedTransactions(const Register* r) +{ + r->selectedTransactions(*this); +} + +int SelectedTransactions::warnLevel() const +{ + int warnLevel = 0; + SelectedTransactions::const_iterator it_t; + for(it_t = begin(); warnLevel < 3 && it_t != end(); ++it_t) { + int thisLevel = (*it_t).warnLevel(); + if (thisLevel > warnLevel) + warnLevel = thisLevel; + } + return warnLevel; +} + +bool SelectedTransactions::canModify() const +{ + return warnLevel() < 2; +} + +bool SelectedTransactions::canDuplicate() const +{ + return warnLevel() < 3; +} + +} // namespace diff --git a/kmymoney2/widgets/selectedtransaction.h b/kmymoney2/widgets/selectedtransaction.h new file mode 100644 index 0000000..0b82020 --- /dev/null +++ b/kmymoney2/widgets/selectedtransaction.h @@ -0,0 +1,87 @@ +/*************************************************************************** + selectedtransaction.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <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 SELECTEDTRANSACTION_H +#define SELECTEDTRANSACTION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneytransaction.h> +#include <kmymoney/mymoneyscheduled.h> +#include <kmymoney/mymoneysplit.h> + +namespace KMyMoneyRegister { + +class SelectedTransaction +{ +public: + SelectedTransaction() {} + SelectedTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const QString& scheduleId = QString()) : + m_transaction(t), m_split(s), m_scheduleId(scheduleId) {} + + MyMoneyTransaction& transaction(void) { return m_transaction; } + const MyMoneyTransaction& transaction(void) const { return m_transaction; } + MyMoneySplit& split(void) { return m_split; } + const MyMoneySplit& split(void) const { return m_split; } + + bool isScheduled(void) const { return !m_scheduleId.isEmpty(); } + const QString& scheduleId(void) const { return m_scheduleId; } + + /** + * checks the transaction for specific reasons which would + * speak against editing/modifying it. + * @retval 0 no sweat, user can modify + * @retval 1 at least one split has been reconciled already + * @retval 2 some transactions cannot be changed anymore - parts of them are frozen + * @retval 3 some transactions cannot be changed anymore - they touch closed accounts + */ + int warnLevel() const; + +private: + MyMoneyTransaction m_transaction; + MyMoneySplit m_split; + QString m_scheduleId; +}; + +class Register; + +class SelectedTransactions:public QValueList<SelectedTransaction> +{ +public: + SelectedTransactions() {} + SelectedTransactions(const Register* r); + + /** + * @return the highest warnLevel of all transactions in the list + */ + int warnLevel() const; + + bool canModify() const; + bool canDuplicate() const; +}; + +} // namespace + +#endif + diff --git a/kmymoney2/widgets/sortoptionlistitem.h b/kmymoney2/widgets/sortoptionlistitem.h new file mode 100644 index 0000000..8f2cdb0 --- /dev/null +++ b/kmymoney2/widgets/sortoptionlistitem.h @@ -0,0 +1,390 @@ +/*************************************************************************** + sortoptionlistitem.h + ---------- + begin : Fri Jun 02 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// Note: This file will be included from transactionsortoption.ui.h + +static const char * sortAscendingXpm[] = { +"16 16 145 2", +" c None", +". c #3368C9", +"+ c #4276D5", +"@ c #5284E0", +"# c #6C95E0", +"$ c #6A93DF", +"% c #5585DF", +"& c #3F72D2", +"* c #356ACD", +"= c #487AD7", +"- c #4F84E6", +"; c #B4C9EF", +"> c #BFD1F2", +", c #D5E1F5", +"' c #C9D7F2", +") c #A3BCEA", +"! c #6A97EB", +"~ c #3573E7", +"{ c #376FD5", +"] c #4378DA", +"^ c #6B94E1", +"/ c #D7E2F6", +"( c #E3EAF8", +"_ c #CDDAF4", +": c #BED0EF", +"< c #94B5F0", +"[ c #4485FB", +"} c #3A7EFB", +"| c #3777ED", +"1 c #386FD4", +"2 c #4E83E7", +"3 c #D5E0F6", +"4 c #CADAF7", +"5 c #C3D5F6", +"6 c #AEC7F5", +"7 c #C1D4F5", +"8 c #BBCFF4", +"9 c #9BB8F0", +"0 c #86B0FD", +"a c #639AFE", +"b c #3B7FFB", +"c c #3574E8", +"d c #376AC8", +"e c #4075DA", +"f c #A9C2F2", +"g c #E0E8F7", +"h c #C5D6F6", +"i c #FBFCFE", +"j c #F8FAFE", +"k c #FDFEFE", +"l c #FEFEFE", +"m c #F7F9FD", +"n c #FAFCFE", +"o c #A5C5FE", +"p c #4285FE", +"q c #4281F7", +"r c #366DD3", +"s c #467DE3", +"t c #B6CCF3", +"u c #DBE5F6", +"v c #D1DDF5", +"w c #E7EDF9", +"x c #DAE5F8", +"y c #E2EBFB", +"z c #CDDFFE", +"A c #508EFD", +"B c #4C88F5", +"C c #477CDF", +"D c #6290E5", +"E c #CFDCF3", +"F c #A7C0ED", +"G c #8FB0ED", +"H c #EEF3FC", +"I c #BFD6FD", +"J c #4F8BF7", +"K c #6696ED", +"L c #5584DC", +"M c #3A67BB", +"N c #608EE4", +"O c #C3D3F1", +"P c #A4BEED", +"Q c #E5EDFB", +"R c #EFF5FE", +"S c #DBE8FE", +"T c #FDFDFE", +"U c #C8DAFA", +"V c #779FE7", +"W c #5C88D9", +"X c #3B67BA", +"Y c #9DBAEC", +"Z c #A8C1EC", +"` c #EBF2FE", +" . c #A6C5FE", +".. c #B6CFFE", +"+. c #FCFDFE", +"@. c #DAE5F9", +"#. c #83A7E9", +"$. c #7EA2E4", +"%. c #5E88D7", +"&. c #3D73D6", +"*. c #87A9E8", +"=. c #9CB7EA", +"-. c #D1DEF7", +";. c #A8C7FE", +">. c #669CFE", +",. c #8CB4FD", +"'. c #D6E2F7", +"). c #89AAE7", +"!. c #83A4E3", +"~. c #4675CE", +"{. c #477DE1", +"]. c #84A6E5", +"^. c #B9CDF2", +"/. c #E4EDFE", +"(. c #A6C6FE", +"_. c #699DFD", +":. c #4F8BF8", +"<. c #739FEF", +"[. c #D0DEF6", +"}. c #BBCEF1", +"|. c #88A8E5", +"1. c #658DD8", +"2. c #4973C2", +"3. c #3F75DA", +"4. c #4E82E3", +"5. c #5F90EA", +"6. c #669BFB", +"7. c #5C95FC", +"8. c #518CF7", +"9. c #779FE8", +"0. c #7298DD", +"a. c #5880CB", +"b. c #3C74DC", +"c. c #3875E7", +"d. c #4482F6", +"e. c #4D89F5", +"f. c #83A4E4", +"g. c #5881CC", +"h. c #356ED7", +"i. c #477DE0", +"j. c #5685DC", +"k. c #5D88DA", +"l. c #5E89D8", +"m. c #4676CF", +"n. c #4774C7", +" . . ", +" + @ # $ % & * ", +" = - ; > , ' ) ! ~ { ", +" ] ^ / ( / _ : < [ } | 1 ", +" 2 3 4 5 6 7 8 9 0 a b c d ", +" e f g h i j k l m n o p q r ", +" s t u v w x y l l i z A B C ", +" D E _ : F G H l l l I J K L M ", +" N O : P G Q l R S T U K V W X ", +" s Y Z G Q l ` ...+.@.#.$.%. ", +" &.*.=.-.l ` ;.>.,.k '.).!.~. ", +" {.].^./.(._.:.<.[.}.|.1.2. ", +" 3.4.5.6.7.8.K 9.).|.0.a. ", +" b.c.d.e.K V $.f.1.g. ", +" h.i.j.k.l.m.n. ", +" "}; + + +static const char * sortDescendingXpm[] = { +"16 16 145 2", +" c None", +". c #3D73D6", +"+ c #467DE3", +"@ c #608EE4", +"# c #6290E5", +"$ c #4075DA", +"% c #3F75DA", +"& c #477DE1", +"* c #87A9E8", +"= c #9DBAEC", +"- c #C3D3F1", +"; c #CFDCF3", +"> c #B6CCF3", +", c #A9C2F2", +"' c #4E83E7", +") c #4378DA", +"! c #3C74DC", +"~ c #4E82E3", +"{ c #84A6E5", +"] c #9CB7EA", +"^ c #A8C1EC", +"/ c #BED0EF", +"( c #CDDAF4", +"_ c #DBE5F6", +": c #E0E8F7", +"< c #D5E0F6", +"[ c #6B94E1", +"} c #487AD7", +"| c #3875E7", +"1 c #5F90EA", +"2 c #B9CDF2", +"3 c #D1DEF7", +"4 c #8FB0ED", +"5 c #A4BEED", +"6 c #D1DDF5", +"7 c #C5D6F6", +"8 c #CADAF7", +"9 c #D7E2F6", +"0 c #4F84E6", +"a c #356ED7", +"b c #4482F6", +"c c #669BFB", +"d c #E4EDFE", +"e c #FEFEFE", +"f c #E5EDFB", +"g c #A7C0ED", +"h c #E7EDF9", +"i c #FBFCFE", +"j c #C3D5F6", +"k c #E3EAF8", +"l c #B4C9EF", +"m c #4276D5", +"n c #477DE0", +"o c #4D89F5", +"p c #5C95FC", +"q c #A6C6FE", +"r c #EBF2FE", +"s c #DAE5F8", +"t c #F8FAFE", +"u c #AEC7F5", +"v c #BFD1F2", +"w c #5284E0", +"x c #5685DC", +"y c #6696ED", +"z c #518CF7", +"A c #699DFD", +"B c #A8C7FE", +"C c #EEF3FC", +"D c #E2EBFB", +"E c #FDFEFE", +"F c #C1D4F5", +"G c #D5E1F5", +"H c #6C95E0", +"I c #3368C9", +"J c #5D88DA", +"K c #779FE7", +"L c #4F8BF8", +"M c #669CFE", +"N c #A6C5FE", +"O c #EFF5FE", +"P c #BBCFF4", +"Q c #C9D7F2", +"R c #6A93DF", +"S c #5E89D8", +"T c #7EA2E4", +"U c #779FE8", +"V c #739FEF", +"W c #8CB4FD", +"X c #B6CFFE", +"Y c #DBE8FE", +"Z c #F7F9FD", +"` c #9BB8F0", +" . c #94B5F0", +".. c #A3BCEA", +"+. c #5585DF", +"@. c #4676CF", +"#. c #83A4E4", +"$. c #89AAE7", +"%. c #D0DEF6", +"&. c #FCFDFE", +"*. c #FDFDFE", +"=. c #FAFCFE", +"-. c #86B0FD", +";. c #4485FB", +">. c #6A97EB", +",. c #3F72D2", +"'. c #4774C7", +"). c #658DD8", +"!. c #88A8E5", +"~. c #BBCEF1", +"{. c #D6E2F7", +"]. c #DAE5F9", +"^. c #C8DAFA", +"/. c #BFD6FD", +"(. c #CDDFFE", +"_. c #A5C5FE", +":. c #639AFE", +"<. c #3A7EFB", +"[. c #3573E7", +"}. c #356ACD", +"|. c #5881CC", +"1. c #7298DD", +"2. c #83A7E9", +"3. c #4F8BF7", +"4. c #508EFD", +"5. c #4285FE", +"6. c #3B7FFB", +"7. c #3777ED", +"8. c #376FD5", +"9. c #5880CB", +"0. c #83A4E3", +"a. c #4C88F5", +"b. c #4281F7", +"c. c #3574E8", +"d. c #386FD4", +"e. c #4973C2", +"f. c #4675CE", +"g. c #5E88D7", +"h. c #5C88D9", +"i. c #5584DC", +"j. c #477CDF", +"k. c #366DD3", +"l. c #376AC8", +"m. c #3B67BA", +"n. c #3A67BB", +" ", +" . + @ # + $ ", +" % & * = - ; > , ' ) ", +" ! ~ { ] ^ / ( _ : < [ } ", +" | 1 2 3 4 5 / 6 7 8 9 0 ", +" a b c d e f 4 g h i j k l m ", +" n o p q r e f 4 s t u 9 v w ", +" x y z A B r e C D E F ( G H I ", +" J K y L M N O e e e P / Q R I ", +" S T U V W X Y e e Z ` ...+. ", +" @.#.$.%.E &.*.e i =.-.;.>.,. ", +" '.).!.~.{.].^./.(._.:.<.[.}. ", +" |.1.!.$.2.y 3.4.5.6.7.8. ", +" 9.).0.T K y a.b.c.d. ", +" e.f.g.h.i.j.k.l. ", +" m.n. "}; + +class SortOptionListItem : public KListViewItem +{ +public: + SortOptionListItem(QListView* parent, QListViewItem* after, const QString& txt, int direction); + int direction(void) const; + +public slots: + void toggleDirection(void); + +private: + void setPixmap(void); + +private: + int m_direction; +}; + +SortOptionListItem::SortOptionListItem(QListView* parent, QListViewItem* after, const QString& txt, int direction) : + KListViewItem(parent, after, txt) +{ + m_direction = (direction >= 0) ? 1 : -1; + setPixmap(); +} + +void SortOptionListItem::setPixmap(void) +{ + if(m_direction > 0) + KListViewItem::setPixmap(0, QPixmap(&sortAscendingXpm[0])); + else + KListViewItem::setPixmap(0, QPixmap(&sortDescendingXpm[0])); +} + +void SortOptionListItem::toggleDirection(void) +{ + m_direction *= (-1); + setPixmap(); +} + +int SortOptionListItem::direction(void) const +{ + return m_direction; +} + diff --git a/kmymoney2/widgets/stdtransactiondownloaded.cpp b/kmymoney2/widgets/stdtransactiondownloaded.cpp new file mode 100644 index 0000000..1305252 --- /dev/null +++ b/kmymoney2/widgets/stdtransactiondownloaded.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + stdtransactiondownloaded.cpp + ------------------- + begin : Sun May 11 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kdebug.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <stdtransactiondownloaded.h> +#include <kmymoney/kmymoneyglobalsettings.h> +#include <kmymoney/register.h> + +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +StdTransactionDownloaded::StdTransactionDownloaded(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + StdTransaction(parent, transaction, split, uniqueId) +{ +} + +bool StdTransactionDownloaded::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) + +{ + bool rc = Transaction::paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, brush); + // if not selected paint in selected background color + if(!isSelected()) { + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::importedTransactionColor()); + brush = QBrush(cg.base()); + } + return rc; +} + +InvestTransactionDownloaded::InvestTransactionDownloaded(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + InvestTransaction(parent, transaction, split, uniqueId) +{ +} + +bool InvestTransactionDownloaded::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) + +{ + bool rc = Transaction::paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, brush); + // if not selected paint in selected background color + if(!isSelected()) { + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::importedTransactionColor()); + brush = QBrush(cg.base()); + } + return rc; +} + diff --git a/kmymoney2/widgets/stdtransactiondownloaded.h b/kmymoney2/widgets/stdtransactiondownloaded.h new file mode 100644 index 0000000..705fb04 --- /dev/null +++ b/kmymoney2/widgets/stdtransactiondownloaded.h @@ -0,0 +1,130 @@ +/*************************************************************************** + stdtransactiondownloaded.h + ------------------- + begin : Sun May 11 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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 STDTRANSACTIONDOWNLOADED_H +#define STDTRANSACTIONDOWNLOADED_H + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transaction.h> + +namespace KMyMoneyTransactionForm { + class TransactionForm; +}; // namespace + +namespace KMyMoneyRegister { + +class StdTransactionDownloaded : public StdTransaction +{ +public: + StdTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionDownloaded() {} + + virtual const char* className(void) { return "StdTransactionDownloaded"; } + + /** + * This method sets the general paramaters required for the painting of a cell + * in the register. These are: + * + * - background color (alternating) + * - background color (imported transaction) + * - background color (matched transaction) + * - background color (selected transaction) + * - cellRect (area covering the cell) + * - textRect (area covering the text) + * - color of the pen to do the painting of text and lines + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param cellRect ref to QRect object receiving the area information for the cell + * @param textRect ref to QRect object receiving the area information for the text + * @param cg ref to QColorGroup object receiving the color information to be used + * @param brush ref to QBrush object receiveing the brush information to be used + */ + virtual bool paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush); + +#if 0 + virtual void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + + bool formCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + void registerCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + + int numColsForm(void) const { return 4; } + + void arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets); + void arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets); + void tabOrderInForm(QWidgetList& tabOrderWidgets) const; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; + + int numRowsRegister(bool expanded) const; +#endif + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister(void) const { return StdTransaction::numRowsRegister(); } +}; + +class InvestTransactionDownloaded : public InvestTransaction +{ + public: + InvestTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~InvestTransactionDownloaded() {} + + virtual const char* className(void) { return "InvestTransactionDownloaded"; } + + /** + * This method sets the general paramaters required for the painting of a cell + * in the register. These are: + * + * - background color (alternating) + * - background color (imported transaction) + * - background color (matched transaction) + * - background color (selected transaction) + * - cellRect (area covering the cell) + * - textRect (area covering the text) + * - color of the pen to do the painting of text and lines + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param cellRect ref to QRect object receiving the area information for the cell + * @param textRect ref to QRect object receiving the area information for the text + * @param cg ref to QColorGroup object receiving the color information to be used + * @param brush ref to QBrush object receiveing the brush information to be used + */ + virtual bool paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush); + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister(void) const { return InvestTransaction::numRowsRegister(); } +}; + + +}; // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: + diff --git a/kmymoney2/widgets/stdtransactionmatched.cpp b/kmymoney2/widgets/stdtransactionmatched.cpp new file mode 100644 index 0000000..9bc191b --- /dev/null +++ b/kmymoney2/widgets/stdtransactionmatched.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + stdtransactionmatched.cpp + ------------------- + begin : Sat May 11 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qregion.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kdebug.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <stdtransactionmatched.h> +#include <kmymoney/kmymoneyglobalsettings.h> +#include <kmymoney/register.h> + +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +StdTransactionMatched::StdTransactionMatched(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + StdTransaction(parent, transaction, split, uniqueId), + m_drawCounter(parent->drawCounter()-1) +{ + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); +} + +bool StdTransactionMatched::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) +{ + QRect r(cellRect); + + bool rc = Transaction::paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, brush); + + // if not selected paint in matched background color + if(!isSelected()) { + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::matchedTransactionColor()); + brush = QBrush(cg.base()); + } + + // the first line needs to be painted across all columns + if(row + m_additionalRows - m_rowsRegister == 0) { + // avoid painting the text over multiple columns twice for the same update round + unsigned int drawCounter = m_parent->drawCounter(); + if(m_drawCounter == drawCounter) { + return false; + } + + + // the fixed text always uses all cols + col = m_parent->columnAt(1); + painter->translate(-r.x() + m_parent->columnPos(col), 0); +#if 0 + r.setX(m_parent->columnPos(col)); + r.setWidth(m_parent->visibleWidth()); + painter->translate(r.x(), r.y()); +#endif + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->visibleWidth()); + cellRect.setHeight(m_parent->rowHeight(row + m_startRow)); + + textRect = cellRect; + textRect.setX(2); + textRect.setWidth(textRect.width()-4); + } + return rc; +} + +void StdTransactionMatched::registerCellText(QString& txt, int& align, int row, int col, QPainter* painter) +{ + // run through the standard + StdTransaction::registerCellText(txt, align, row, col, painter); + + // we only cover the additional rows + if(row >= m_rowsRegister - m_additionalRows) { + // make row relative to the last three rows + row += m_additionalRows - m_rowsRegister; + + // remove anything that had been added by the standard method + txt = ""; + + // and we draw this information in italics + if(painter) { + QFont font = painter->font(); + font.setItalic(true); + painter->setFont(font); + } + + MyMoneyTransaction matchedTransaction = m_split.matchedTransaction(); + MyMoneySplit matchedSplit; + try { + matchedSplit = matchedTransaction.splitById(m_split.value("kmm-match-split")); + } catch(MyMoneyException *e) { + delete e; + } + + QValueList<MyMoneySplit>::const_iterator it_s; + const QValueList<MyMoneySplit>& list = matchedTransaction.splits(); + MyMoneyMoney importedValue; + for(it_s = list.begin(); it_s != list.end(); ++it_s) { + if((*it_s).accountId() == m_account.id()) { + importedValue += (*it_s).shares(); + } + } + + QDate postDate; + QString memo; + switch(row) { + case 0: + if(painter) + txt = QString(" ")+i18n("KMyMoney has matched a downloaded transaction with a manually entered one (result above)"); + // return true for the first visible column only + break; + + case 1: + switch(col) { + case DateColumn: + align |= Qt::AlignLeft; + txt = i18n("Bank entry:"); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + txt = QString("%1 %2").arg(matchedTransaction.postDate().toString(Qt::ISODate)).arg(matchedTransaction.memo()); + break; + + case PaymentColumn: + align |= Qt::AlignRight; + if(importedValue.isNegative()) { + txt = (-importedValue).formatMoney(m_account.fraction()); + } + break; + + case DepositColumn: + align |= Qt::AlignRight; + if(!importedValue.isNegative()) { + txt = importedValue.formatMoney(m_account.fraction()); + } + break; + } + break; + + case 2: + switch(col) { + case DateColumn: + align |= Qt::AlignLeft; + txt = i18n("Your entry:"); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + postDate = m_transaction.postDate(); + if(!m_split.value("kmm-orig-postdate").isEmpty()) { + postDate = QDate::fromString(m_split.value("kmm-orig-postdate"), Qt::ISODate); + } + memo = m_split.memo(); + if(!matchedSplit.memo().isEmpty() && memo != matchedSplit.memo()) { + int pos = memo.findRev(matchedSplit.memo()); + if(pos != -1) { + memo = memo.left(pos); + if(memo.endsWith("\n")) + memo = memo.left(pos-1); + } + } + txt = QString("%1 %2").arg(postDate.toString(Qt::ISODate)).arg(memo); + break; + + case PaymentColumn: + align |= Qt::AlignRight; + if(m_split.value().isNegative()) { + txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); + } + break; + + case DepositColumn: + align |= Qt::AlignRight; + if(!m_split.value().isNegative()) { + txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); + } + break; + + } + break; + } + } +} + +void StdTransactionMatched::paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& _cg) const +{ + // the last 3 rows should not show a grid + if(row < m_rowsRegister - m_additionalRows) { + Transaction::paintRegisterGrid(painter, row, col, r, _cg); + + } else if(row == m_rowsRegister-1) { + painter->setPen(KMyMoneyGlobalSettings::listGridColor()); + painter->drawLine(r.x(), r.height()-1, r.width(), r.height()-1); + } +} diff --git a/kmymoney2/widgets/stdtransactionmatched.h b/kmymoney2/widgets/stdtransactionmatched.h new file mode 100644 index 0000000..599e6e0 --- /dev/null +++ b/kmymoney2/widgets/stdtransactionmatched.h @@ -0,0 +1,103 @@ +/*************************************************************************** + stdtransactionmatched.h + ------------------- + begin : Sat May 31 2008 + copyright : (C) 2008 by Thomas Baumgart + email : Thomas Baumgart <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 STDTRANSACTIONMATCHED_H +#define STDTRANSACTIONMATCHED_H + +// ---------------------------------------------------------------------------- +// QT Includes +#include <qbrush.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transaction.h> + +namespace KMyMoneyTransactionForm { + class TransactionForm; +}; // namespace + +namespace KMyMoneyRegister { + +class StdTransactionMatched : public StdTransaction +{ + static const int m_additionalRows = 3; + +public: + StdTransactionMatched(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionMatched() {} + + virtual const char* className(void) { return "StdTransactionMatched"; } + + /** + * This method sets the general paramaters required for the painting of a cell + * in the register. These are: + * + * - background color (alternating) + * - background color (imported transaction) + * - background color (matched transaction) + * - background color (selected transaction) + * - cellRect (area covering the cell) + * - textRect (area covering the text) + * - color of the pen to do the painting of text and lines + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param cellRect ref to QRect object receiving the area information for the cell + * @param textRect ref to QRect object receiving the area information for the text + * @param cg ref to QColorGroup object receiving the color information to be used + * @param brush ref to QBrush object receiveing the brush information to be used + */ + virtual bool paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush); + + virtual void paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) const; + +#if 0 + virtual void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + + bool formCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + + int numColsForm(void) const { return 4; } + + void arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets); + void arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets); + void tabOrderInForm(QWidgetList& tabOrderWidgets) const; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; + + int numRowsRegister(bool expanded) const; +#endif + + void registerCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister(bool) + */ + int numRowsRegister(bool expanded) const { return StdTransaction::numRowsRegister(expanded) + m_additionalRows; } + +private: + unsigned int m_drawCounter; +}; + +}; // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: + diff --git a/kmymoney2/widgets/transaction.cpp b/kmymoney2/widgets/transaction.cpp new file mode 100644 index 0000000..4c8a23b --- /dev/null +++ b/kmymoney2/widgets/transaction.cpp @@ -0,0 +1,2189 @@ +/*************************************************************************** + transaction.cpp - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> +#include <qpainter.h> +#include <qwidgetlist.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kdebug.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transaction.h> +#include <kmymoney/mymoneytransaction.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/mymoneyfile.h> +#include <kmymoney/mymoneypayee.h> +#include <kmymoney/register.h> +#include <kmymoney/kmymoneycategory.h> +#include <kmymoney/kmymoneydateinput.h> +#include <kmymoney/transactionform.h> +#include <kmymoney/kmymoneylineedit.h> +#include <kmymoney/kmymoneyedit.h> +#include <kmymoney/transactioneditor.h> +#include <kmymoney/investtransactioneditor.h> +#include <kmymoney/kmymoneyutils.h> + +#include "../kmymoneyglobalsettings.h" + +using namespace KMyMoneyRegister; +using namespace KMyMoneyTransactionForm; + +static unsigned char attentionSign[] = { + 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A, + 0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52, + 0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14, + 0x08,0x06,0x00,0x00,0x00,0x8D,0x89,0x1D, + 0x0D,0x00,0x00,0x00,0x04,0x73,0x42,0x49, + 0x54,0x08,0x08,0x08,0x08,0x7C,0x08,0x64, + 0x88,0x00,0x00,0x00,0x19,0x74,0x45,0x58, + 0x74,0x53,0x6F,0x66,0x74,0x77,0x61,0x72, + 0x65,0x00,0x77,0x77,0x77,0x2E,0x69,0x6E, + 0x6B,0x73,0x63,0x61,0x70,0x65,0x2E,0x6F, + 0x72,0x67,0x9B,0xEE,0x3C,0x1A,0x00,0x00, + 0x02,0x05,0x49,0x44,0x41,0x54,0x38,0x8D, + 0xAD,0x95,0xBF,0x4B,0x5B,0x51,0x14,0xC7, + 0x3F,0x2F,0xBC,0x97,0x97,0x97,0x97,0x77, + 0xF3,0xF2,0x1C,0xA4,0x54,0x6B,0x70,0x10, + 0x44,0x70,0x2A,0x91,0x2E,0x52,0x02,0x55, + 0x8A,0xB5,0xA3,0xAB,0x38,0x08,0x66,0xCC, + 0xEE,0xE0,0xE2,0x20,0xB8,0x38,0xB8,0xB8, + 0xF8,0x1F,0x38,0x29,0xA5,0x29,0x74,0x90, + 0x0E,0x0D,0x0E,0x22,0x1D,0x44,0xA8,0xD0, + 0xD4,0xB4,0x58,0x4B,0x09,0xF9,0xF1,0x4A, + 0x3B,0xD4,0xD3,0xE1,0x55,0xD3,0x34,0xAF, + 0x49,0x6C,0x3D,0xF0,0x85,0x7B,0xCF,0xFD, + 0x9E,0xEF,0x3D,0xE7,0xFE,0xD4,0x44,0x84, + 0xDB,0xB4,0x48,0x2F,0xA4,0x94,0xAB,0xE5, + 0x52,0xAE,0x96,0xEB,0x49,0x51,0x44,0x3A, + 0x02,0x18,0x88,0xC7,0xF1,0xE3,0x71,0x7C, + 0x60,0xA0,0x1B,0xBF,0x6B,0x86,0x49,0xC5, + 0x46,0x3E,0x47,0x34,0x9F,0x23,0x9A,0x54, + 0x6C,0xFC,0x57,0x86,0x40,0xC6,0x4B,0xE1, + 0x37,0xCA,0x48,0xA3,0x8C,0x78,0x29,0x7C, + 0x20,0xD3,0x31,0xA6,0xD3,0xA0,0x52,0x1C, + 0x6D,0x6F,0x72,0xD9,0x28,0x23,0xFE,0x07, + 0x64,0x7B,0x93,0x4B,0xA5,0x38,0xFA,0x27, + 0x41,0x60,0x6E,0x74,0x84,0x7A,0xE5,0x1D, + 0x92,0x54,0x88,0xE7,0x22,0xD5,0x12,0x32, + 0x3A,0x42,0x1D,0x98,0xBB,0x91,0x20,0x60, + 0xDA,0x36,0x17,0xFB,0x7B,0xC8,0xC1,0x4B, + 0x04,0x02,0xBC,0x7E,0x81,0xEC,0xEF,0x21, + 0xB6,0xCD,0x05,0x60,0xF6,0x2C,0x68,0x9A, + 0x2C,0xCF,0x4C,0xE1,0x4B,0x05,0x39,0x3F, + 0x69,0x0A,0xBE,0x7F,0x83,0x48,0x05,0x99, + 0x99,0xC2,0x37,0x4D,0x96,0x7B,0x12,0x04, + 0xFA,0x2D,0x8B,0xC6,0xE9,0x61,0x10,0x2C, + 0x15,0xC4,0x8A,0x21,0x86,0x8E,0xFC,0xF8, + 0x12,0xF4,0x4F,0x0F,0x11,0xCB,0xA2,0x01, + 0xF4,0x77,0x3D,0x36,0x4E,0x82,0xF5,0xA5, + 0x05,0x8C,0xE1,0x74,0xD3,0x37,0x34,0x18, + 0x20,0xF2,0x8B,0x3D,0x9C,0x86,0xA5,0x05, + 0x0C,0x27,0xC1,0x7A,0xC7,0x63,0x03,0x8C, + 0x2B,0x07,0xBF,0x5A,0x6A,0x66,0x27,0x15, + 0x64,0x3A,0x8B,0x3C,0x7A,0xD8,0xEA,0xAB, + 0x96,0x10,0xE5,0xE0,0x03,0xE3,0x7F,0xCD, + 0x50,0x39,0x6C,0xAD,0xAD,0x10,0x53,0xAA, + 0x75,0xD2,0xF4,0xBD,0x00,0x2D,0x5C,0x05, + 0x6B,0x2B,0xC4,0x94,0xC3,0xD6,0xEF,0xFE, + 0x6B,0x41,0x4D,0xD3,0x66,0xFB,0x3C,0xC6, + 0x16,0xE7,0xDB,0x97,0x61,0xE2,0x3E,0x3C, + 0xC8,0xB4,0x15,0xC7,0xE2,0x3C,0x91,0x3E, + 0x8F,0x31,0x4D,0xD3,0x66,0x5B,0x4A,0x06, + 0x8C,0x84,0xCD,0x59,0x61,0xA7,0xB5,0xAC, + 0x2B,0x9C,0x1C,0x04,0x08,0x1B,0x2B,0xEC, + 0x20,0x09,0x9B,0x33,0xC0,0xB8,0xDE,0x65, + 0x43,0x27,0x9F,0x9D,0xA4,0x1E,0x16,0xF0, + 0xF9,0x6D,0xB0,0xC3,0x86,0x1E,0xB4,0xC3, + 0x38,0xD9,0x49,0xEA,0x86,0x4E,0xFE,0xEA, + 0x29,0xF4,0x2C,0x8B,0xDA,0x71,0x31,0x9C, + 0xFC,0xF5,0x23,0x32,0x34,0x88,0xDC,0xBD, + 0x13,0x5C,0xBF,0x30,0xCE,0x71,0x11,0xB1, + 0x2C,0x6A,0x80,0xA7,0xDB,0x36,0xAB,0x4F, + 0xA6,0x89,0xBA,0x49,0x38,0xFF,0xD4,0xBE, + 0x4E,0x00,0xAF,0x9E,0x81,0x08,0xD4,0xEA, + 0x01,0xFE,0x34,0x37,0x09,0x4F,0x1F,0x13, + 0xDD,0x7D,0xCE,0xAA,0x96,0x72,0x29,0x7C, + 0xFB,0xCE,0x44,0xB8,0xD4,0xCD,0x2C,0x66, + 0x52,0xD4,0x6E,0xFB,0x0B,0xF8,0x09,0x63, + 0x63,0x31,0xE4,0x85,0x76,0x2E,0x0E,0x00, + 0x00,0x00,0x00,0x49,0x45,0x4E,0x44,0xAE, + 0x42,0x60,0x82 +}; + +Transaction::Transaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + RegisterItem(parent), + m_transaction(transaction), + m_split(split), + m_form(0), + m_uniqueId(m_transaction.id()), + m_formRowHeight(-1), + m_selected(false), + m_focus(false), + m_erronous(false), + m_inEdit(false), + m_inRegisterEdit(false), + m_showBalance(true), + m_reducedIntensity(false) +{ + MyMoneyFile* file = MyMoneyFile::instance(); + + // load the account + if(!m_split.accountId().isEmpty()) + m_account = file->account(m_split.accountId()); + + // load the payee + if(!m_split.payeeId().isEmpty()) { + m_payee = file->payee(m_split.payeeId()).name(); + } + if(parent->account().isIncomeExpense()) { + m_payeeHeader = m_split.shares().isNegative() ? i18n("From") : i18n("Pay to"); + } else { + m_payeeHeader = m_split.shares().isNegative() ? i18n("Pay to") : i18n("From"); + } + + // load the currency + if(!m_transaction.id().isEmpty()) + m_splitCurrencyId = m_account.currencyId(); + + // check if transaction is errnous or not + m_erronous = !m_transaction.splitSum().isZero(); + + if(!m_uniqueId.isEmpty()) { + m_uniqueId += "-"; + QString id; + id.setNum(uniqueId); + m_uniqueId += id.rightJustify(3, '0'); + } +} + +void Transaction::setFocus(bool focus, bool updateLens) +{ + if(focus != m_focus) { + m_focus = focus; + } + if(updateLens) { + if(KMyMoneyGlobalSettings::ledgerLens() + || !KMyMoneyGlobalSettings::transactionForm() + || KMyMoneyGlobalSettings::showRegisterDetailed() + || m_parent->m_ledgerLensForced) { + if(focus) + setNumRowsRegister(numRowsRegister(true)); + else + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + } + } +} + +void Transaction::markAttachment(QPainter* painter, int /* row */, int /* col */, const QRect& r) +{ + static QPixmap clip; + + const int m = 2; // margin + int h = m_parent->rowHeightHint() - (2*m); + int lx = r.topRight().x() - h; + if(isErronous()) + lx -= h; + QRect cr(QPoint(lx - m, m), QSize(h, h)); + + painter->save(); + if(clip.isNull()) { + clip = KGlobal::iconLoader()->loadIcon("attach", KIcon::Small, KIcon::SizeSmall, KIcon::DefaultState); + if(clip.height() > h) { + clip.resize(h, h); + } + } + + painter->drawPixmap(QPoint(lx - m, m + (h - clip.height())/2 ), clip); + painter->restore(); +} + +void Transaction::markAsErronous(QPainter* painter, int /* row */, int /* col */, const QRect& r) +{ + const int m = 2; // margin + int h = m_parent->rowHeightHint() - (2*m); + QRect cr(QPoint(r.topRight().x() - h - m, m), QSize(h, h)); + + painter->save(); + QPixmap attention; + attention.loadFromData(attentionSign, sizeof(attentionSign), 0, 0); + + if(attention.height() > h) { + attention.resize(h, h); + } + painter->drawPixmap(QPoint(r.topRight().x() - h - m, m + (h - attention.height())/2 ), attention); + painter->restore(); + +} + +bool Transaction::paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush) +{ + if(m_reducedIntensity) + cg = m_parent->palette().disabled(); + + if(m_alternate) + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listColor()); + else + cg.setColor(QColorGroup::Base, KMyMoneyGlobalSettings::listBGColor()); + + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->columnWidth(col)); + cellRect.setHeight(m_parent->rowHeight(m_startRow + row)); + + textRect = cellRect; + textRect.setX(2); + textRect.setWidth(textRect.width()-4); + + if(m_selected) { + brush = QBrush(cg.highlight()); + painter->setPen(cg.highlightedText()); + } else { + brush = QBrush(cg.base()); + painter->setPen(cg.text()); + } + + // do we need to switch to the error color? + if(m_erronous && m_parent->markErronousTransactions()) { + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + + // do we need to switch to the negative balance color? + if(col == BalanceColumn) { + bool showNegative = m_balance.isNegative(); + if(m_account.accountGroup() == MyMoneyAccount::Liability && !m_balance.isZero() ) + showNegative = !showNegative; + if(showNegative) + painter->setPen(KMyMoneyGlobalSettings::listNegativeValueColor()); + } + return true; +} + +void Transaction::paintRegisterCellFocus(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) +{ + + if(m_focus) { + QPen oldPen = painter->pen(); + QPen newPen = oldPen; + newPen.setWidth(0); + + painter->setFont(KMyMoneyGlobalSettings::listCellFont()); + painter->setPen(newPen); + painter->setPen(cg.foreground()); + painter->setPen(Qt::DotLine); + // for the first Row, we need to paint the top + QPoint start, end; +#if 0 + if(row == 0) { + start = QPoint(r.x(), r.y() + 1); + end = QPoint(r.x() + r.width(), r.y() + 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last Row, we need to paint the bottom + if(row == numRows() - 1) { + start = QPoint(r.x(), r.y() + r.height() - 1); + end = QPoint(r.x() + r.width(), r.y() + r.height() - 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the first col, we need to paint the left + if(col == 0) { + start = QPoint(r.x() + 1, r.y()); + end = QPoint(r.x() + 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRows()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last col, we need to paint the left + if(col == m_parent->lastCol()) { + start = QPoint(r.x() + r.width() - 1, r.y()); + end = QPoint(r.x() + r.width() - 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRows()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } +#endif + if(row == 0) { + start = QPoint(r.x(), r.y()); + end = QPoint(r.x() + r.width(), r.y() + 1); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last Row, we need to paint the bottom + if(row == numRowsRegister() - 1) { + start = QPoint(r.x(), r.y() + r.height() - 2); + end = QPoint(r.x() + r.width(), r.y() + r.height() - 2); + if(col == 0) { + start.rx()++; + } else if(col == m_parent->lastCol()) { + end.rx()--; + } + // painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the first col, we need to paint the left + if(col == 0) { + start = QPoint(r.x() + 1, r.y()); + end = QPoint(r.x() + 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRowsRegister()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + // for the last col, we need to paint the left + if(col == m_parent->lastCol()) { + start = QPoint(r.x() + r.width() - 1, r.y()); + end = QPoint(r.x() + r.width() - 1, r.y() + r.height()); + if(row == 0) { + start.ry()++; + } else if(row == numRowsRegister()-1) { + end.ry()--; + } + //painter->drawLine(start, end); + painter->drawWinFocusRect(QRect(start, end)); + } + painter->setPen(oldPen); + } +} + +void Transaction::registerCellText(QString& txt, int row, int col) +{ + int align = 0; + registerCellText(txt, align, row, col, 0); +} + +void Transaction::paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool /* selected */, const QColorGroup& _cg) +{ + QColorGroup cg(_cg); + QRect cellRect(r); + QRect textRect; + QBrush backgroundBrush; + + painter->save(); + + if(paintRegisterCellSetup(painter, row, col, cellRect, textRect, cg, backgroundBrush)) { + // construct the text for the cell + int align = Qt::AlignVCenter; + QString txt; + if(m_transaction != MyMoneyTransaction() && !m_inRegisterEdit) { + registerCellText(txt, align, row, col, painter); + } + + paintRegisterCellBackground(painter, row, col, cellRect, backgroundBrush); + + // and paint it + paintRegisterCellText(painter, row, col, textRect, cg, align, txt); + + // paint the grid + paintRegisterGrid(painter, row, col, cellRect, cg); + + // possible icons + paintRegisterIcons(painter, row, col, cellRect, cg); + + // and the focus + paintRegisterCellFocus(painter, row, col, cellRect, cg); + } + + painter->restore(); +} + +void Transaction::paintRegisterIcons(QPainter* painter, int row, int col, const QRect& /*r*/, const QColorGroup& /*cg*/) +{ + if(row == 0 && col == DetailColumn && painter) { + if(m_erronous || !m_transaction.value("kmm-attachment").isEmpty()) { + QRect cellRect; + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->columnWidth(col)); + cellRect.setHeight(m_parent->rowHeight(m_startRow + row)); + if(m_erronous) { + markAsErronous(painter, row, col, cellRect); + } + if(!m_transaction.value("kmm-attachment").isEmpty()) { + markAttachment(painter, row, col, cellRect); + } + } + } +} +void Transaction::paintRegisterCellBackground(QPainter* painter, int row, int col, const QRect& r, const QBrush& backgroundBrush) +{ + Q_UNUSED(row); + Q_UNUSED(col); + + // fill the background + painter->fillRect(r, backgroundBrush); +} + +void Transaction::paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& _cg) const +{ + Q_UNUSED(_cg); + + // if a grid is selected, we paint it right away + if (KMyMoneyGlobalSettings::showGrid()) { + painter->setPen(KMyMoneyGlobalSettings::listGridColor()); + if(col != 0) + painter->drawLine(r.x(), 0, r.x(), r.height()-1); + if(row == numRowsRegister()-1) + painter->drawLine(r.x(), r.height()-1, r.width(), r.height()-1); + } +} + +void Transaction::paintRegisterCellText(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& _cg, int align, const QString& txt) +{ + Q_UNUSED(row); + Q_UNUSED(col); + Q_UNUSED(r); + Q_UNUSED(_cg); + + // make sure, we clear the cell + if(txt.isEmpty()) + painter->drawText(r, align, " "); + else + painter->drawText(r, align, txt); +} + +int Transaction::formRowHeight(int /*row*/) +{ + if(m_formRowHeight < 0) { + m_formRowHeight = formRowHeight(); + } + return m_formRowHeight; +} + +int Transaction::formRowHeight(void) const +{ + if(m_formRowHeight < 0) { + // determine the height of the objects in the table + kMyMoneyDateInput dateInput; + KMyMoneyCategory category(0,0,true); + + return QMAX(dateInput.sizeHint().height(), category.sizeHint().height()); + } + return m_formRowHeight; +} + +void Transaction::setupForm(TransactionForm* form) +{ + m_form = form; + form->verticalHeader()->setUpdatesEnabled(false); + form->horizontalHeader()->setUpdatesEnabled(false); + + form->setNumRows(numRowsForm()); + form->setNumCols(numColsForm()); + + // Force all cells to have some text (so that paintCell is called for each cell) + for(int r = 0; r < numRowsForm(); ++r) { + for(int c = 0; c < numColsForm(); ++c) { + form->setText(r, c, "x"); + if(r == 0 && form->columnWidth(c) == 0) { + form->setColumnWidth(c, 10); + } + } + } + form->horizontalHeader()->setUpdatesEnabled(true); + form->verticalHeader()->setUpdatesEnabled(true); + + loadTab(form); +} + +void Transaction::paintFormCell(QPainter* painter, int row, int col, const QRect& /*r*/, bool /*selected*/, const QColorGroup& _cg) +{ + if(!m_form) + return; + + QRect cellRect = m_form->cellRect(row, col); + + QRect textRect(cellRect); + textRect.setX(1); + textRect.setY(1); + textRect.setWidth(textRect.width()-2); + textRect.setHeight(textRect.height()-2); + + painter->fillRect(cellRect, _cg.background()); + painter->setPen(_cg.text()); + + QString txt; + int align = Qt::AlignVCenter; + bool editField = formCellText(txt, align, row, col, painter); + + // if we have an editable field and don't currently edit the transaction + // show the background in a different color + if(editField && !m_inEdit) { + painter->fillRect(textRect, _cg.base()); + } + + // make sure, we clear the cell + // in case of an editable field and edit mode, we just clear the field + if(txt.isEmpty() || (editField && m_inEdit)) + painter->drawText(textRect, align, " "); + else + painter->drawText(textRect, align, txt); + +} + +void Transaction::setupPalette(const QPalette& palette, QMap<QString, QWidget*>& editWidgets) +{ + QMap<QString, QWidget*>::iterator it_w; + for(it_w = editWidgets.begin(); it_w != editWidgets.end(); ++it_w) { + if(*it_w) { + (*it_w)->setPalette(palette); + } + } +} + +void Transaction::setupFormPalette(QMap<QString, QWidget*>& editWidgets) +{ + setupPalette(m_parent->palette(), editWidgets); +} + +void Transaction::setupRegisterPalette(QMap<QString, QWidget*>& editWidgets) +{ + // make sure, we're using the right palette + QPalette palette = m_parent->palette(); + + // use the highlight color as background + palette.setColor(QPalette::Active, QColorGroup::Background, palette.color(QPalette::Active, QColorGroup::Highlight)); + + setupPalette(palette, editWidgets); +} + +QWidget* Transaction::focusWidget(QWidget* w) const +{ + if(w) { + while(w->focusProxy()) + w = w->focusProxy(); + } + return w; +} + +void Transaction::arrangeWidget(QTable* tbl, int row, int col, QWidget* w) const +{ + if(w) { + tbl->setCellWidget(row, col, w); + // remove the widget from the QTable's eventFilter so that all + // events will be directed to the edit widget + w->removeEventFilter(tbl); + } else + qDebug("No widget for %d,%d", row, col); +} + +bool Transaction::haveNumberField(void) const +{ + bool rc = true; + switch(m_account.accountType()) { + case MyMoneyAccount::Savings: + case MyMoneyAccount::Cash: + case MyMoneyAccount::Loan: + case MyMoneyAccount::AssetLoan: + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + case MyMoneyAccount::Equity: + rc = KMyMoneyGlobalSettings::alwaysShowNrField(); + break; + + case MyMoneyAccount::Checkings: + case MyMoneyAccount::CreditCard: + // the next case is used for the editor when the account + // is unknown (eg. when creating new schedules) + case MyMoneyAccount::UnknownAccountType: + break; + + default: + rc = false; + break; + } + return rc; +} + +bool Transaction::maybeTip(const QPoint& cpos, int row, int col, QRect& r, QString& msg) +{ + if(col != DetailColumn) + return false; + + if(!m_erronous && m_transaction.splitCount() < 3) + return false; + + int h = m_parent->rowHeightHint(); + + // check for detail column in row 0 of the transaction for a possible exclamation mark + r = m_parent->cellGeometry(m_startRow + 0, col); + // qDebug("r is %d,%d,%d,%d", r.x(), r.y(), r.width(), r.height()); + r.setBottomLeft(QPoint(r.x() + (r.width() - h), r.y() + h)); + // qDebug("r is %d,%d,%d,%d", r.x(), r.y(), r.width(), r.height()); + // qDebug("p is in r = %d", r.contains(cpos)); + if(r.contains(cpos) && m_erronous) { + if(m_transaction.splits().count() < 2) { + msg = QString("<qt>%1</qt>").arg(i18n("Transaction is missing a category assignment.")); + } else { + const MyMoneySecurity& sec = MyMoneyFile::instance()->security(m_account.currencyId()); + msg = QString("<qt>%1</qt>").arg(i18n("The transaction has a missing assignment of <b>%1</b>.").arg(m_transaction.splitSum().abs().formatMoney(m_account, sec))); + } + return true; + } + + // check for detail column in row 1 of the transaction for a possible exclamation mark + r = m_parent->cellGeometry(m_startRow + 1, col); + if(row == 1 && r.contains(cpos) && m_transaction.splitCount() > 2) { + MyMoneyFile* file = MyMoneyFile::instance(); + QValueList<MyMoneySplit>::const_iterator it_s; + QString txt; + const MyMoneySecurity& sec = file->security(m_transaction.commodity()); + MyMoneyMoney factor(1, 1); + if(!m_split.value().isNegative()) + factor = -factor; + + for(it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) { + if(*it_s == m_split) + continue; + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + QString category = file->accountToCategory(acc.id()); + QString amount = ((*it_s).value() * factor).formatMoney(acc, sec); + + txt += QString("<tr><td><nobr>%1</nobr></td><td align=right><nobr>%2</nobr></td></tr>").arg(category, amount); + } + msg = QString("<table>%1</table>").arg(txt); + return true; + } + + return false; +} + +QString Transaction::reconcileState(bool text) const +{ + QString txt = KMyMoneyUtils::reconcileStateToString(m_split.reconcileFlag(), text); + + if((text == true) + && (txt == i18n("Unknown")) + && (m_transaction == MyMoneyTransaction())) + txt = QString(); + return txt; +} + +void Transaction::startEditMode(void) +{ + m_inEdit = true; + // only update the number of lines displayed if we edit inside the register + if(m_inRegisterEdit) + setNumRowsRegister(numRowsRegister(true)); +} + +void Transaction::leaveEditMode(void) +{ + m_inEdit = false; + m_inRegisterEdit = false; + setFocus(hasFocus(), true); +} + +void Transaction::singleLineMemo(QString& txt, const MyMoneySplit& split) const +{ + txt = split.memo(); + // remove empty lines + txt.replace("\n\n", "\n"); + // replace '\n' with ", " + txt.replace('\n', ", "); +} + +int Transaction::rowHeightHint(void) const +{ + return m_inEdit ? formRowHeight()-4 : RegisterItem::rowHeightHint(); +} + + +bool Transaction::matches(const QString& txt) const +{ + if(txt.isEmpty() || m_transaction.splitCount() == 0) + return true; + + MyMoneyFile* file = MyMoneyFile::instance(); + QString s(txt); + s.replace(MyMoneyMoney::thousandSeparator(), QString()); + + const QValueList<MyMoneySplit>&list = m_transaction.splits(); + QValueList<MyMoneySplit>::const_iterator it_s; + for(it_s = list.begin(); it_s != list.end(); ++it_s) { + // check if the text is contained in one of the fields + // memo, number, payee, account + if((*it_s).memo().contains(txt, false) + || (*it_s).number().contains(txt, false)) + return true; + + if(!(*it_s).payeeId().isEmpty()) { + const MyMoneyPayee& payee = file->payee((*it_s).payeeId()); + if(payee.name().contains(txt, false)) + return true; + } + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + if(acc.name().contains(txt, false)) + return true; + + if(!s.isEmpty()) { + // check if any of the value field matches if a value has been entered + QString r = (*it_s).value().formatMoney(m_account.fraction(), false); + if(r.contains(s, false)) + return true; + const MyMoneyAccount& acc = file->account((*it_s).accountId()); + r = (*it_s).shares().formatMoney(acc.fraction(), false); + if(r.contains(s, false)) + return true; + } + } + + return false; +} + +void Transaction::setShowBalance(bool showBalance) +{ + m_showBalance = showBalance; +} + +void Transaction::setVisible(bool visible) +{ + if(visible != isVisible()) { + RegisterItem::setVisible(visible); + RegisterItem* p; + Transaction* t; + if(!visible) { + // if we are hidden, we need to inform all previous transactions + // about it so that they don't show the balance + p = prevItem(); + while(p) { + t = dynamic_cast<Transaction*>(p); + if(t) { + if(!t->m_showBalance) + break; + t->m_showBalance = false; + } + p = p->prevItem(); + } + } else { + // if we are shown, we need to check if the next transaction + // is visible and change the display of the balance + p = this; + do { + p = p->nextItem(); + t = dynamic_cast<Transaction*>(p); + } while(!t && p); + + // if the next transaction is visible or I am the last one + if((t && t->m_showBalance) || !t) { + m_showBalance = true; + p = prevItem(); + while(p && p->isVisible()) { + t = dynamic_cast<Transaction*>(p); + if(t) { + if(t->m_showBalance) + break; + t->m_showBalance = true; + } + p = p->prevItem(); + } + } + } + } +} + +void Transaction::setSelected(bool selected) +{ + if(!selected || (selected && isVisible())) + m_selected = selected; +} + +StdTransaction::StdTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId), + m_showAccountRow(false) +{ + try { + m_categoryHeader = i18n("Category"); + switch(transaction.splitCount()) { + default: + m_category = i18n("Split transaction (category replacement)", "Split transaction"); + break; + + case 0: // the empty transaction + case 1: + break; + + case 2: + setupFormHeader(m_transaction.splitByAccount(m_split.accountId(), false).accountId()); + break; + } + } catch(MyMoneyException *e) { + kdDebug(2) << "Problem determining the category for transaction '" << m_transaction.id() << "'. Reason: " << e->what() << "\n"; + delete e; + } + m_rowsForm = 6; + + if(KMyMoneyUtils::transactionType(m_transaction) == KMyMoneyUtils::InvestmentTransaction) { + MyMoneySplit split = KMyMoneyUtils::stockSplit(m_transaction); + m_payee = MyMoneyFile::instance()->account(split.accountId()).name(); + QString addon; + if(split.action() == MyMoneySplit::ActionBuyShares) { + if(split.value().isNegative()) { + addon = i18n("Sell"); + } else { + addon = i18n("Buy"); + } + } else if(split.action() == MyMoneySplit::ActionDividend) { + addon = i18n("Dividend"); + } else if(split.action() == MyMoneySplit::ActionYield) { + addon = i18n("Yield"); + } + if(!addon.isEmpty()) { + m_payee += QString(" (%1)").arg(addon); + } + m_payeeHeader = i18n("Activity"); + m_category = i18n("Investment transaction"); + } + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void StdTransaction::setupFormHeader(const QString& id) +{ + m_category = MyMoneyFile::instance()->accountToCategory(id); + switch(MyMoneyFile::instance()->account(id).accountGroup()) { + case MyMoneyAccount::Asset: + case MyMoneyAccount::Liability: + m_categoryHeader = m_split.shares().isNegative() ? i18n("Transfer to") : i18n("Transfer from"); + break; + + default: + m_categoryHeader = i18n("Category"); + break; + } +} + +KMyMoneyRegister::Action StdTransaction::actionType(void) const +{ + KMyMoneyRegister::Action action=ActionNone; + + // if at least one split is referencing an income or + // expense account, we will not call it a transfer + QValueList<MyMoneySplit>::const_iterator it_s; + + for(it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) { + if((*it_s).accountId() == m_split.accountId()) + continue; + MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); + if(acc.accountGroup() == MyMoneyAccount::Income + || acc.accountGroup() == MyMoneyAccount::Expense) { + // otherwise, we have to determine between deposit and withdrawal + action = m_split.shares().isNegative() ? ActionWithdrawal : ActionDeposit; + break; + } + } + // otherwise, it's a transfer + if(it_s == m_transaction.splits().end()) + action = ActionTransfer; + + return action; +} + +void StdTransaction::loadTab(TransactionForm* form) +{ + TabBar* bar = form->tabBar(); + bar->setSignalEmission(TabBar::SignalNever); + for(int i = 0; i < bar->count(); ++i) { + bar->setTabEnabled(bar->tabAt(i)->identifier(), true); + } + + if(m_transaction.splitCount() > 0) { + bar->setCurrentTab(actionType()); + } + bar->setSignalEmission(TabBar::SignalAlways); +} + +void StdTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + + QTableItem* memo = form->item(3, ValueColumn1); + memo->setSpan(3, 1); +} + +bool StdTransaction::showRowInForm(int row) const +{ + return row == 0 ? m_showAccountRow : true; +} + +void StdTransaction::setShowRowInForm(int row, bool show) +{ + if(row == 0) + m_showAccountRow = show; +} + +bool StdTransaction::formCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + // if(m_transaction != MyMoneyTransaction()) { + switch(row) { + case 0: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Account"); + break; + } + break; + + case 1: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_payeeHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_payee; + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveNumberField()) + txt = i18n("Number"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveNumberField()) + txt = m_split.number(); + break; + } + break; + + case 2: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_categoryHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_category; + if(m_transaction != MyMoneyTransaction()) { + if(txt.isEmpty() && !m_split.value().isZero()) + txt = i18n("*** UNASSIGNED ***"); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(m_transaction != MyMoneyTransaction()) + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + } + break; + + case 3: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + if(m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(m_transaction != MyMoneyTransaction()) { + txt = (m_split.value(m_transaction.commodity(), m_splitCurrencyId).abs()).formatMoney(m_account.fraction()); + } + break; + } + break; + + case 5: + switch(col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + txt = reconcileState(); + break; + } + } + // } + if(col == ValueColumn2 && row == 1) { + return haveNumberField(); + } + return (col == ValueColumn1 && row < 4) || (col == ValueColumn2 && row > 0 && row != 4); +} + +void StdTransaction::registerCellText(QString& txt, int& align, int row, int col, QPainter* painter) +{ + switch(row) { + case 0: + switch(col) { + case NumberColumn: + align |= Qt::AlignLeft; + if(haveNumberField()) + txt = m_split.number(); + break; + + case DateColumn: + align |= Qt::AlignLeft; + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + txt = m_payee; + if(txt.isEmpty() && m_rowsRegister < 3) { + singleLineMemo(txt, m_split); + } + if(txt.isEmpty() && m_rowsRegister < 2) { + if(m_account.accountType() != MyMoneyAccount::Income + && m_account.accountType() != MyMoneyAccount::Expense) { + txt = m_category; + if(txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if(painter) + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + } + } + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case PaymentColumn: + align |= Qt::AlignRight; + if(m_split.value().isNegative()) { + txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); + } + break; + + case DepositColumn: + align |= Qt::AlignRight; + if(!m_split.value().isNegative()) { + txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if(m_showBalance) + txt = m_balance.formatMoney(m_account.fraction()); + else + txt = "----"; + break; + + case AccountColumn: + // txt = m_objects->account(m_transaction.splits()[0].accountId()).name(); + txt = MyMoneyFile::instance()->account(m_split.accountId()).name(); + break; + + default: + break; + } + break; + + case 1: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + txt = m_category; + if(txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if(painter) + painter->setPen(KMyMoneyGlobalSettings::listErronousTransactionColor()); + } + break; + + default: + break; + } + break; + + case 2: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int StdTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + int firstRow = 0, lastRow = 0; + + int nw = 0; + for(int i = firstRow; i <= lastRow; ++i) { + int align; + registerCellText(txt, align, i, col, 0); + int w = cellFontMetrics.width(txt+" "); + if(w > nw) + nw = w; + } + return nw; +} + +void StdTransaction::arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["account"]); + arrangeWidget(m_form, 1, LabelColumn1, editWidgets["cashflow"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["payee"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["category"]->parentWidget()); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["memo"]); + if(haveNumberField()) { + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["number-label"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["number"]); + } + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["date-label"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["amount"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["status"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["category-label"]); + + // get rid of the hints. we don't need them for the form + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast<KMyMoneyCombo*>(*it); + kMyMoneyLineEdit* edit = dynamic_cast<kMyMoneyLineEdit*>(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(*it); + if(combo) + combo->setHint(QString()); + if(edit) + edit->setHint(QString()); + if(payee) + payee->setHint(QString()); + } + + // drop the tabbar on top of the original + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast<KMyMoneyTransactionForm::TransactionForm*>(m_form); + TabBar* w = dynamic_cast<TabBar*>(editWidgets["tabbar"]); + if(w) { + w->reparent(form->tabBar(), QPoint(0, 0), true); + } +} + +void StdTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = QStringList::split(",", KMyMoneyGlobalSettings::stdTransactionFormTabOrder()); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while(it_s != taborder.constEnd()) { + if(*it_s == "account") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + } else if(*it_s == "cashflow") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, LabelColumn1))); + } else if(*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + } else if(*it_s == "category") { + // make sure to have the category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_form->cellWidget(2, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + } else if(*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn1))); + } else if(*it_s == "number") { + if(haveNumberField()) { + if((w = focusWidget(m_form->cellWidget(1, ValueColumn2)))) + tabOrderWidgets.append(w); + } + } else if(*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + } else if(*it_s == "amount") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + } else if(*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn2))); + } + ++it_s; + } +} + +void StdTransaction::arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_parent) + return; + + setupRegisterPalette(editWidgets); + + if(haveNumberField()) + arrangeWidget(m_parent, m_startRow+0, NumberColumn, editWidgets["number"]); + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["payee"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["category"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, PaymentColumn, editWidgets["payment"]); + arrangeWidget(m_parent, m_startRow + 0, DepositColumn, editWidgets["deposit"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow+2, m_parent->rowHeightHint() * 3); +} + +void StdTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = QStringList::split(",", KMyMoneyGlobalSettings::stdTransactionRegisterTabOrder()); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while(it_s != taborder.constEnd()) { + if(*it_s == "number") { + if(haveNumberField()) { + if((w = focusWidget(m_parent->cellWidget(m_startRow + 0, NumberColumn)))) + tabOrderWidgets.append(w); + } + } else if(*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + } else if(*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + } else if(*it_s == "category") { + // make sure to have the category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 1, DetailColumn); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + } else if(*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, DetailColumn))); + } else if(*it_s == "payment") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PaymentColumn))); + } else if(*it_s == "deposit") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DepositColumn))); + } else if(*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); + } + ++it_s; + } +} + +int StdTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if(expanded) { + numRows = 3; + if(!m_inEdit) { + if(m_payee.isEmpty()) { + numRows--; + } + if(m_split.memo().isEmpty()) { + numRows--; + } + // For income and expense accounts that only have + // two splits we only show one line, because the + // account name is already contained in the account column. + if(m_account.accountType() == MyMoneyAccount::Income + || m_account.accountType() == MyMoneyAccount::Expense) { + if(numRows > 2 && m_transaction.splitCount() == 2) + numRows = 1; + } + } + } + return numRows; +} + +TransactionEditor* StdTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ + m_inRegisterEdit = regForm == m_parent; + return new StdTransactionEditor(regForm, this, list, lastPostDate); +} + +InvestTransaction::InvestTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId) +{ + // dissect the transaction into its type, splits, currency, security etc. + InvestTransactionEditor::dissectTransaction(m_transaction, m_split, + m_assetAccountSplit, + m_feeSplits, + m_interestSplits, + m_security, + m_currency, + m_transactionType); + + QValueList<MyMoneySplit>::ConstIterator it_s; + for(it_s = m_feeSplits.begin(); it_s != m_feeSplits.end(); ++it_s) { + m_feeAmount += (*it_s).value(); + } + for(it_s = m_interestSplits.begin(); it_s != m_interestSplits.end(); ++it_s) { + m_interestAmount += (*it_s).value(); + } + + // check the count of the fee splits and setup the text + switch(m_feeSplits.count()) { + case 0: + break; + + case 1: + m_feeCategory = MyMoneyFile::instance()->accountToCategory(m_feeSplits[0].accountId()); + break; + + default: + m_feeCategory = i18n("Split transaction (category replacement)", "Split transaction"); + break; + } + + // check the count of the interest splits and setup the text + switch(m_interestSplits.count()) { + case 0: + break; + + case 1: + m_interestCategory = MyMoneyFile::instance()->accountToCategory(m_interestSplits[0].accountId()); + break; + + default: + m_interestCategory = i18n("Split transaction (category replacement)", "Split transaction"); + break; + } + + m_rowsForm = 7; + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void InvestTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + + QTableItem* memo = form->item(5, 1); + memo->setSpan(2, 1); +} + +void InvestTransaction::activity(QString& txt, MyMoneySplit::investTransactionTypeE type) const +{ + switch(type) { + case MyMoneySplit::AddShares: + txt = i18n("Add shares"); + break; + case MyMoneySplit::RemoveShares: + txt = i18n("Remove shares"); + break; + case MyMoneySplit::BuyShares: + txt = i18n("Buy shares"); + break; + case MyMoneySplit::SellShares: + txt = i18n("Sell shares"); + break; + case MyMoneySplit::Dividend: + txt = i18n("Dividend"); + break; + case MyMoneySplit::ReinvestDividend: + txt = i18n("Reinvest Dividend"); + break; + case MyMoneySplit::Yield: + txt = i18n("Yield"); + break; + case MyMoneySplit::SplitShares: + txt = i18n("Split shares"); + break; + default: + txt = i18n("Unknown"); + break; + } +} + +bool InvestTransaction::formCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + bool fieldEditable = false; + + switch(row) { + case 0: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Activity"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + activity(txt, m_transactionType); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + if(m_transaction != MyMoneyTransaction()) + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + } + break; + + case 1: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Security"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + if(m_account.isInvest()) + txt = m_security.name(); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveShares()) { + txt = i18n("Shares"); + } else if(haveSplitRatio()) { + txt = i18n("Ratio"); + } + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = haveShares()) == true) { + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + } else if(haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + } + break; + + case 2: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveAssetAccount()) + txt = i18n("Account"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveAssetAccount()) == true) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(havePrice()) + txt = i18n("Price/share"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = havePrice()) == true && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney("", KMyMoneyGlobalSettings::pricePrecision()); + } + break; + } + break; + + case 3: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveFees()) + txt = i18n("Fees"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveFees()) == true) { + txt = m_feeCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveFees() && !m_feeCategory.isEmpty()) + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveFees()) { + if((fieldEditable = !m_feeCategory.isEmpty()) == true) { + txt = m_feeAmount.formatMoney(m_currency); + } + } + break; + } + break; + + case 4: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if(haveInterest()) + txt = i18n("Interest"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if((fieldEditable = haveInterest()) == true) { + txt = m_interestCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveInterest() && !m_interestCategory.isEmpty()) + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if(haveInterest()) { + if((fieldEditable = !m_interestCategory.isEmpty()) == true) { + txt = (-m_interestAmount).formatMoney(m_currency); + } + } + break; + } + break; + + case 5: + switch(col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + fieldEditable = true; + if(m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if(haveAmount()) + txt = i18n("Total"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if((fieldEditable = haveAmount()) == true) { + txt = m_assetAccountSplit.value().abs().formatMoney(m_currency); + } + } + break; + + case 6: + switch(col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + txt = reconcileState(); + break; + } + } + + return fieldEditable; +} + +void InvestTransaction::registerCellText(QString& txt, int& align, int row, int col, QPainter* /* painter */) +{ + switch(row) { + case 0: + switch(col) { + case DateColumn: + align |= Qt::AlignLeft; + txt = KGlobal::locale()->formatDate(m_transaction.postDate(), true); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + activity(txt, m_transactionType); + break; + + case SecurityColumn: + align |= Qt::AlignLeft; + if(m_account.isInvest()) + txt = m_security.name(); + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveShares()) + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else if(haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + + case PriceColumn: + align |= Qt::AlignRight; + if(havePrice() && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney(m_currency.tradingSymbol(), KMyMoneyGlobalSettings::pricePrecision()); + } + break; + + case ValueColumn: + align |= Qt::AlignRight; + if(haveAmount()) { + txt = m_assetAccountSplit.value().abs().formatMoney(m_currency); + + } else if(haveInterest()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if(m_showBalance) + txt = m_balance.formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else + txt = "----"; + break; + + default: + break; + } + break; + + case 1: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } else if(haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + // txt = m_interestAmount.abs().formatMoney(m_currency); + } else if(haveInterest() && m_interestSplits.count()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 2: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = (-m_interestAmount).formatMoney(m_currency); + } else if(haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 3: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = m_feeAmount.formatMoney(m_currency); + } + break; + + default: + break; + } + break; + + case 4: + switch(col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int InvestTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + MyMoneyMoney amount; + int nw = 0; + + // for now just check all rows in that column + for(int row = 0; row < m_rowsRegister; ++row) { + int w; + Transaction::registerCellText(txt, row, col); + w = cellFontMetrics.width(txt+" "); + nw = QMAX(nw, w); + } + + // TODO the optimized way would be to base the size on the contents of a single row + // as we do it in StdTransaction::registerColWidth() +#if 0 + switch(col) { + default: + break; + + case PriceColumn: + if(havePrice()) { + txt = (m_split.value() / m_split.shares()).formatMoney("", KMyMoneyGlobalSettings::pricePrecision()); + nw = cellFontMetrics.width(txt+" "); + } + break; + } +#endif + return nw; +} + +void InvestTransaction::arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + // arrange the edit widgets + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["activity"]); + arrangeWidget(m_form, 0, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["security"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["shares"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["asset-account"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["price"]); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["fee-amount"]); + arrangeWidget(m_form, 4, ValueColumn1, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_form, 4, ValueColumn2, editWidgets["interest-amount"]); + arrangeWidget(m_form, 5, ValueColumn1, editWidgets["memo"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["total"]); + arrangeWidget(m_form, 6, ValueColumn2, editWidgets["status"]); + + // arrange dynamic labels + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["shares-label"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["asset-label"]); + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["price-label"]); + arrangeWidget(m_form, 3, LabelColumn1, editWidgets["fee-label"]); + arrangeWidget(m_form, 3, LabelColumn2, editWidgets["fee-amount-label"]); + arrangeWidget(m_form, 4, LabelColumn1, editWidgets["interest-label"]); + arrangeWidget(m_form, 4, LabelColumn2, editWidgets["interest-amount-label"]); + arrangeWidget(m_form, 5, LabelColumn2, editWidgets["total-label"]); + + // get rid of the hints. we don't need them for the form + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast<KMyMoneyCombo*>(*it); + kMyMoneyLineEdit* lineedit = dynamic_cast<kMyMoneyLineEdit*>(*it); + kMyMoneyEdit* edit = dynamic_cast<kMyMoneyEdit*>(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast<KMyMoneyPayeeCombo*>(*it); + if(combo) + combo->setHint(QString()); + if(edit) + edit->setHint(QString()); + if(lineedit) + lineedit->setHint(QString()); + if(payee) + payee->setHint(QString()); + } +} + +void InvestTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + // activity + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + + // date + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn2))); + + // security + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + + // shares + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn2))); + + // account + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn1))); + + // price + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + + // make sure to have the fee category field and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + QWidget* w = m_form->cellWidget(3, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + + // the same applies for the interest categories + w = m_form->cellWidget(4, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(4, ValueColumn2))); + + // memo + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn1))); + + // total + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn2))); + + // state + tabOrderWidgets.append(focusWidget(m_form->cellWidget(6, ValueColumn2))); +} + +void InvestTransaction::arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets) +{ + if(!m_parent) + return; + + setupRegisterPalette(editWidgets); + + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 0, SecurityColumn, editWidgets["security"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["activity"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["asset-account"]); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 3, DetailColumn, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 4, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, QuantityColumn, editWidgets["shares"]); + arrangeWidget(m_parent, m_startRow + 0, PriceColumn, editWidgets["price"]); + arrangeWidget(m_parent, m_startRow + 2, QuantityColumn, editWidgets["interest-amount"]); + arrangeWidget(m_parent, m_startRow + 3, QuantityColumn, editWidgets["fee-amount"]); + arrangeWidget(m_parent, m_startRow + 0, ValueColumn, editWidgets["total"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow+4, m_parent->rowHeightHint() * 3); +} + +void InvestTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QWidget* w; + + // date + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + // security + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, SecurityColumn))); + // activity + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + // shares + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, QuantityColumn))); + // price + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PriceColumn))); + // asset account + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DetailColumn))); + + // make sure to have the category fields and the split button as seperate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 2, DetailColumn); // interest account + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, QuantityColumn))); + + w = m_parent->cellWidget(m_startRow + 3, DetailColumn); // fee account + tabOrderWidgets.append(focusWidget(w)); + w = dynamic_cast<QWidget*>(w->child("splitButton")); + if(w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 3, QuantityColumn))); + + // memo + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 4, DetailColumn))); + + // status + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); +} + +int InvestTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if(expanded) { + if(!m_inEdit) { + if(haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) + ++numRows; + if(haveInterest() && m_interestSplits.count()) + ++numRows; + if(haveFees() && m_feeSplits.count()) + ++numRows; + if(!m_split.memo().isEmpty()) + ++numRows; + } else + numRows = 5; + } + return numRows; +} + +bool InvestTransaction::haveShares(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::Dividend: + case MyMoneySplit::Yield: + case MyMoneySplit::SplitShares: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveFees(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::AddShares: + case MyMoneySplit::RemoveShares: + case MyMoneySplit::SplitShares: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveInterest(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::Dividend: + case MyMoneySplit::ReinvestDividend: + case MyMoneySplit::Yield: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::havePrice(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::ReinvestDividend: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAmount(void) const +{ + bool rc = false; + switch(m_transactionType) { + case MyMoneySplit::BuyShares: + case MyMoneySplit::SellShares: + case MyMoneySplit::Dividend: + case MyMoneySplit::Yield: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAssetAccount(void) const +{ + bool rc = true; + switch(m_transactionType) { + case MyMoneySplit::AddShares: + case MyMoneySplit::RemoveShares: + case MyMoneySplit::SplitShares: + case MyMoneySplit::ReinvestDividend: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveSplitRatio(void) const +{ + return m_transactionType == MyMoneySplit::SplitShares; +} + +void InvestTransaction::splits(MyMoneySplit& assetAccountSplit, QValueList<MyMoneySplit>& interestSplits, QValueList<MyMoneySplit>& feeSplits) const +{ + assetAccountSplit = m_assetAccountSplit; + interestSplits = m_interestSplits; + feeSplits = m_feeSplits; +} + +TransactionEditor* InvestTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ + m_inRegisterEdit = regForm == m_parent; + return new InvestTransactionEditor(regForm, this, list, lastPostDate); +} + diff --git a/kmymoney2/widgets/transaction.h b/kmymoney2/widgets/transaction.h new file mode 100644 index 0000000..060d025 --- /dev/null +++ b/kmymoney2/widgets/transaction.h @@ -0,0 +1,420 @@ +/*************************************************************************** + transaction.h - description + ------------------- + begin : Tue Jun 13 2006 + copyright : (C) 2000-2006 by Thomas Baumgart + email : Thomas Baumgart <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 TRANSACTION_H +#define TRANSACTION_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qpalette.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/registeritem.h> +#include <kmymoney/mymoneytransaction.h> +#include <kmymoney/mymoneysplit.h> +#include <kmymoney/mymoneysecurity.h> +#include <kmymoney/selectedtransaction.h> +#include <kmymoney/mymoneyaccount.h> + +class QTable; +class TransactionEditor; +class TransactionEditorContainer; + +namespace KMyMoneyTransactionForm { + class TransactionForm; +}; // namespace + +namespace KMyMoneyRegister { + +// keep the following list in sync with code in the constructor +// of KMyMoneyRegister::Register in register.cpp +typedef enum { + NumberColumn = 0, + DateColumn, + AccountColumn, + SecurityColumn, + DetailColumn, + ReconcileFlagColumn, + PaymentColumn, + DepositColumn, + QuantityColumn, + PriceColumn, + ValueColumn, + BalanceColumn, + // insert new values above this line + MaxColumns +} Column; + +class Transaction : public RegisterItem +{ +public: + Transaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~Transaction() {} + + virtual const char* className(void) { return "Transaction"; } + + bool isSelectable(void) const { return true; } + bool isSelected(void) const { return m_selected; } + void setSelected(bool selected); + + bool canHaveFocus(void) const { return true; } + bool hasFocus(void) const { return m_focus; } + bool hasEditorOpen(void) const { return m_inEdit; } + + virtual bool isScheduled(void) const { return false; } + + void setFocus(bool focus, bool updateLens = true); + + bool isErronous(void) const { return m_erronous; } + + virtual const QDate& sortPostDate(void) const { return m_transaction.postDate(); } + virtual int sortSamePostDate(void) const { return 2; } + virtual const QDate& sortEntryDate(void) const { return m_transaction.entryDate(); } + virtual const QString& sortPayee(void) const { return m_payee; } + virtual const MyMoneyMoney& sortValue(void) const { return m_split.shares(); } + virtual const QString& sortNumber(void) const { return m_split.number(); } + virtual const QString& sortEntryOrder(void) const { return m_uniqueId; } + virtual CashFlowDirection sortType(void) const { return m_split.shares().isNegative() ? Payment : Deposit; } + virtual const QString& sortCategory(void) const { return m_category; } + virtual MyMoneySplit::reconcileFlagE sortReconcileState(void) const { return m_split.reconcileFlag(); } + + virtual const QString& id(void) const { return m_uniqueId; } + const MyMoneyTransaction& transaction(void) const { return m_transaction; } + const MyMoneySplit& split(void) const { return m_split; } + + void setBalance(const MyMoneyMoney& balance) { m_balance = balance; } + const MyMoneyMoney& balance(void) const { return m_balance; } + + virtual int rowHeightHint(void) const; + + /** + * This method sets the general paramaters required for the painting of a cell + * in the register. These are: + * + * - background color (alternating) + * - background color (imported transaction) + * - background color (matched transaction) + * - background color (selected transaction) + * - cellRect (area covering the cell) + * - textRect (area covering the text) + * - color of the pen to do the painting of text and lines + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param cellRect ref to QRect object receiving the area information for the cell + * @param textRect ref to QRect object receiving the area information for the text + * @param cg ref to QColorGroup object receiving the color information to be used + */ + virtual bool paintRegisterCellSetup(QPainter* painter, int& row, int& col, QRect& cellRect, QRect& textRect, QColorGroup& cg, QBrush& brush); + + /** + * paints the focus if the current cell defined by (@a row, @a col) has the focus. + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param r area covering the cell + * @param cg the color definitions to be used + */ + void paintRegisterCellFocus(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg); + + /** + * paints a cell of the register for the transaction. Uses paintRegisterCellSetup(), paintRegisterCellText() + * paintRegisterGrid(), paintRegisterIcons() and paintRegisterCellFocus() to actually do the job. + * + * @param painter pointer to the QPainter object + * @param row vertical index of cell in register + * @param col horizontal index of cell in register + * @param r area covering the cell + * @param selected unused but kept for compatibility + * @param cg the color definitions to be used + * + */ + virtual void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + virtual void paintRegisterCellText(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg, int align, const QString& txt); + virtual void paintRegisterCellBackground(QPainter* painter, int row, int col, const QRect& r, const QBrush& backgroundBrush); + virtual void paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) const; + virtual void paintRegisterIcons(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg); + + virtual void paintFormCell(QPainter* /* painter */, int /* row */, int /* col */, const QRect& /* r */, bool /* selected */, const QColorGroup& /* cg */); + + virtual bool formCellText(QString& /* txt */, int& /* align */, int /* row */, int /* col */, QPainter* /* painter */) { return false; } + virtual void registerCellText(QString& /* txt */, int& /* align */, int /* row */, int /* col */, QPainter* /* painter */) {} + virtual int registerColWidth(int /* col */, const QFontMetrics& /* cellFontMetrics */) { return 0; } + + /** + * Helper method for the above method. + */ + void registerCellText(QString& txt, int row, int col); + + virtual int formRowHeight(int row); + virtual int formRowHeight(void) const; + + virtual void setupForm(KMyMoneyTransactionForm::TransactionForm* form); + virtual void setupFormPalette(QMap<QString, QWidget*>& editWidgets); + virtual void setupRegisterPalette(QMap<QString, QWidget*>& editWidgets); + virtual void loadTab(KMyMoneyTransactionForm::TransactionForm* form) = 0; + + virtual void arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets) = 0; + virtual void arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets) = 0; + virtual void tabOrderInForm(QWidgetList& tabOrderWidgets) const = 0; + virtual void tabOrderInRegister(QWidgetList& tabOrderWidgets) const = 0; + + virtual KMyMoneyRegister::Action actionType(void) const = 0; + + QWidget* focusWidget(QWidget*) const; + void arrangeWidget(QTable* tbl, int row, int col, QWidget* w) const; + + bool haveNumberField(void) const; + + bool matches(const QString&) const; + + /** + * Checks if the mouse hovered over an area that has a tooltip associated with it. + * The mouse position is given in relative coordinates to the @a startRow and the + * @a row and @a col of the item are also passed as relative values. + * + * If a tooltip shall be shown, this method presets the rectangle @a r with the + * area in register coordinates and @a msg with the string that will be passed + * to QToolTip::tip. @a true is returned in this case. + * + * If no tooltip is available, @a false will be returned. + */ + virtual bool maybeTip(const QPoint& relpos, int row, int col, QRect& r, QString& msg); + + /** + * This method returns the number of register rows required for a certain + * item in expanded (@p expanded equals @a true) or collapsed (@p expanded + * is @a false) mode. + * + * @param expanded returns number of maximum rows required for this item to + * display all information (used for ledger lens and register + * edit mode) or the minimum number of rows required. + * @return number of rows required for mode selected by @p expanded + */ + virtual int numRowsRegister(bool expanded) const = 0; + + virtual int numRowsRegister(void) const = 0; + + void leaveEditMode(void); + void startEditMode(void); + + /** + * This method creates an editor for the transaction + */ + virtual TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) = 0; + + virtual void setVisible(bool visible); + + virtual void setShowBalance(bool showBalance); + + /** + * Return information if @a row should be shown (@a true ) + * or hidden (@a false ) in the form. Default is true. + */ + virtual bool showRowInForm(int row) const { Q_UNUSED(row) return true; } + + /** + * Control visibility of @a row in the transaction form. + * Only row 0 has an effect, others return @a true. + */ + virtual void setShowRowInForm(int row, bool show) { Q_UNUSED(row); Q_UNUSED(show) } + + virtual void setReducedIntensity(bool reduced) { m_reducedIntensity = reduced; } + +protected: + virtual void markAsErronous(QPainter* p, int row, int col, const QRect& r); + virtual void markAttachment(QPainter* painter, int row, int col, const QRect& r); + + /** + * This method converts m_split.reconcileFlag() into a readable string + * + * @param text Return textual representation e.g. "Cleared" (@a true) or just + * a flag e.g. "C" (@a false). Defaults to textual representation. + * @return Textual representation or flag as selected via @p text of the + * reconciliation state of the split + */ + QString reconcileState(bool text = true) const; + + /** + * Helper method to reduce a multi line memo text into a single line. + * + * @param txt QString that will receive the single line memo text + * @param split const reference to the split to take the memo from + */ + void singleLineMemo(QString& txt, const MyMoneySplit& split) const; + + virtual void setupPalette(const QPalette& palette, QMap<QString, QWidget*>& editWidgets); + +protected: + MyMoneyTransaction m_transaction; + MyMoneySplit m_split; + MyMoneyAccount m_account; + MyMoneyMoney m_balance; + QTable* m_form; + QString m_category; + QString m_payee; + QString m_payeeHeader; + QString m_categoryHeader; + QString m_splitCurrencyId; + QString m_uniqueId; + int m_formRowHeight; + bool m_selected; + bool m_focus; + bool m_erronous; + bool m_inEdit; + bool m_inRegisterEdit; + bool m_showBalance; + bool m_reducedIntensity; +}; + +class StdTransaction : public Transaction +{ +public: + StdTransaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransaction() {} + + virtual const char* className(void) { return "StdTransaction"; } + + bool formCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + void registerCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + + int registerColWidth(int col, const QFontMetrics& cellFontMetrics); + void setupForm(KMyMoneyTransactionForm::TransactionForm* form); + void loadTab(KMyMoneyTransactionForm::TransactionForm* form); + + int numColsForm(void) const { return 4; } + + void arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets); + void arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets); + void tabOrderInForm(QWidgetList& tabOrderWidgets) const; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; + KMyMoneyRegister::Action actionType(void) const; + + int numRowsRegister(bool expanded) const; + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister(void) const { return RegisterItem::numRowsRegister(); } + + TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); + + /** + * Return information if @a row should be shown (@a true ) + * or hidden (@a false ) in the form. Default is true. + */ + virtual bool showRowInForm(int row) const; + + /** + * Control visibility of @a row in the transaction form. + * Only row 0 has an effect, others return @a true. + */ + virtual void setShowRowInForm(int row, bool show); + +protected: + void setupFormHeader(const QString& id); + +private: + bool m_showAccountRow; +}; + +class InvestTransaction : public Transaction +{ +public: + InvestTransaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~InvestTransaction() {} + + virtual const QString& sortSecurity(void) const { return m_security.name(); } + virtual const char* className(void) { return "InvestTransaction"; } + + // virtual void paintRegisterCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + + bool formCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + void registerCellText(QString& txt, int& align, int row, int col, QPainter* painter = 0); + + int registerColWidth(int col, const QFontMetrics& cellFontMetrics); + void setupForm(KMyMoneyTransactionForm::TransactionForm* form); + + /** + * provide NOP here as the investment transaction form does not supply a tab + */ + void loadTab(KMyMoneyTransactionForm::TransactionForm* /* form */) {} + + int numColsForm(void) const { return 4; } + + void arrangeWidgetsInForm(QMap<QString, QWidget*>& editWidgets); + void arrangeWidgetsInRegister(QMap<QString, QWidget*>& editWidgets); + void tabOrderInForm(QWidgetList& tabOrderWidgets) const; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; + KMyMoneyRegister::Action actionType(void) const { return KMyMoneyRegister::ActionNone; } + + int numRowsRegister(bool expanded) const; + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister(void) const { return RegisterItem::numRowsRegister(); } + + TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); + + void splits(MyMoneySplit& assetAccountSplit, QValueList<MyMoneySplit>& interestSplits, QValueList<MyMoneySplit>& feeSplits) const; + +protected: + bool haveShares(void) const; + bool haveFees(void) const; + bool haveInterest(void) const; + bool havePrice(void) const; + bool haveAmount(void) const; + bool haveAssetAccount(void) const; + bool haveSplitRatio(void) const; + + /** + * Returns textual representation of the activity identified + * by @p type. + * + * @param txt reference to QString where to store the result + * @param type activity represented as investTransactionTypeE + */ + void activity(QString& txt, MyMoneySplit::investTransactionTypeE type) const; + +private: + QValueList<MyMoneySplit> m_feeSplits; + QValueList<MyMoneySplit> m_interestSplits; + MyMoneySplit m_assetAccountSplit; + MyMoneySecurity m_security; + MyMoneySecurity m_currency; + MyMoneySplit::investTransactionTypeE m_transactionType; + QString m_feeCategory; + QString m_interestCategory; + MyMoneyMoney m_feeAmount; + MyMoneyMoney m_interestAmount; + MyMoneyMoney m_totalAmount; +}; + +}; // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: + diff --git a/kmymoney2/widgets/transactioneditorcontainer.cpp b/kmymoney2/widgets/transactioneditorcontainer.cpp new file mode 100644 index 0000000..4750025 --- /dev/null +++ b/kmymoney2/widgets/transactioneditorcontainer.cpp @@ -0,0 +1,29 @@ +/*************************************************************************** + transactioneditorcontainer.cpp + ---------- + begin : Wed Jun 07 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/transactioneditorcontainer.h> + + diff --git a/kmymoney2/widgets/transactioneditorcontainer.h b/kmymoney2/widgets/transactioneditorcontainer.h new file mode 100644 index 0000000..01474ee --- /dev/null +++ b/kmymoney2/widgets/transactioneditorcontainer.h @@ -0,0 +1,58 @@ +/*************************************************************************** + transactioneditorcontainer.h + ---------- + begin : Wed Jun 07 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 TRANSACTIONEDITORCONTAINER_H +#define TRANSACTIONEDITORCONTAINER_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qmap.h> +#include <qstring.h> +#include <qtable.h> +class QWidget; + +// ---------------------------------------------------------------------------- +// KDE Includes + + +// ---------------------------------------------------------------------------- +// Project Includes + +namespace KMyMoneyRegister { class Transaction; }; + +typedef enum { + ProtectNone = 0, + ProtectTransfer, + ProtectNonTransfer, + ProtectAll +} ProtectedAction; + +class TransactionEditorContainer : public QTable +{ +public: + TransactionEditorContainer(QWidget* parent, const char* name) : QTable(parent, name) {} + + virtual void arrangeEditWidgets(QMap<QString, QWidget*>& editWidgets, KMyMoneyRegister::Transaction* t) = 0; + virtual void removeEditWidgets(QMap<QString, QWidget*>& editWidgets) = 0; + virtual void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const = 0; + // FIXME remove tabbar + // virtual int action(QMap<QString, QWidget*>& editWidgets) const = 0; + // virtual void setProtectedAction(QMap<QString, QWidget*>& editWidgets, ProtectedAction action) = 0; +}; + +#endif diff --git a/kmymoney2/widgets/transactionform.cpp b/kmymoney2/widgets/transactionform.cpp new file mode 100644 index 0000000..7097b6d --- /dev/null +++ b/kmymoney2/widgets/transactionform.cpp @@ -0,0 +1,467 @@ +/*************************************************************************** + transactionform.cpp + ------------------- + begin : Sun May 14 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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. * + * * + ***************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qstring.h> +#include <qpainter.h> +#include <qtimer.h> +#include <qapplication.h> +#include <qlayout.h> +#include <qtabbar.h> +#include <qpalette.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kcombobox.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/kmymoneydateinput.h> +#include <kmymoney/kmymoneyedit.h> +#include <kmymoney/kmymoneycategory.h> + +#include "transactionform.h" +#include "../kmymoneyutils.h" +#include "../kmymoneyglobalsettings.h" + +using namespace KMyMoneyTransactionForm; + +TabBar::TabBar(QWidget* parent, const char* name) : + QTabBar(parent, name), + m_signalType(SignalNormal) +{ + connect(this, SIGNAL(selected(int)), this, SLOT(slotTabSelected(int))); +} + +TabBar::SignalEmissionE TabBar::setSignalEmission(TabBar::SignalEmissionE type) +{ + TabBar::SignalEmissionE _type = m_signalType; + m_signalType = type; + return _type; +} + +int TabBar::currentTab(void) const +{ + QMap<int, int>::const_iterator it; + it = m_idMap.find(QTabBar::currentTab()); + if(it != m_idMap.end()) + return *it; + return -1; +} + +void TabBar::setCurrentTab(int id) +{ + if (tab(id)) // there are no tabs in an expense/income ledger + if (tab(id)->isEnabled()) + setCurrentTab(tab(id)); +} + +QTab* TabBar::tab(int id) const +{ + /* if a QAccel calls setCurrentTab, id will be as set by qt. + * however if we call it programmatically, id will + * be our own id. We do tell QTab about our id but + * in qt3.3 I (woro) am not able to make sure that + * QAccel also gets it. See registeritem.h: We defined + * new values for our own ids which should lie way + * outside of the range that qt uses + */ + QTab *result=QTabBar::tab(id); + QMap<int, int>::const_iterator it; + for(it = m_idMap.begin(); it != m_idMap.end(); ++it) + if(*it == id) + result=QTabBar::tab(it.key()); + return result; +} + +void TabBar::setCurrentTab(QTab* tab) +{ + if(m_signalType != SignalNormal) + blockSignals(true); + + QTabBar::setCurrentTab(tab); + + if(m_signalType != SignalNormal) + blockSignals(false); + + if(m_signalType == SignalAlways) + emit selected(tab->identifier()); +} + +void TabBar::addTab(QTab* tab, int id) +{ + QTabBar::addTab(tab); + setIdentifier(tab, id); +} + +void TabBar::setIdentifier(QTab* tab, int newId) +{ + m_idMap[tab->identifier()] = newId; +} + +void TransactionForm::enableTabBar(bool b) +{ + m_tabBar->setEnabled(b); +} + +void TabBar::slotTabSelected(int id) +{ + QMap<int, int>::const_iterator it; + it = m_idMap.find(id); + if(it != m_idMap.end()) + emit tabSelected(*it); + else + emit tabSelected(id); +} + +void TabBar::show(void) +{ + // make sure we don't emit a signal when simply showing the widget + if(m_signalType != SignalNormal) + blockSignals(true); + + QTabBar::show(); + + if(m_signalType != SignalNormal) + blockSignals(false); +} + +void TabBar::copyTabs(const TabBar* otabbar) +{ + // remove all existing tabs + while(count()) { + removeTab(tabAt(0)); + } + // now create new ones. copy text, icon and identifier + for(int i=0; i < otabbar->count(); ++i) { + QTab* otab = otabbar->tabAt(i); + QTab* ntab = new QTab(otab->text()); + int nid = QTabBar::addTab(ntab); + m_idMap[nid] = otabbar->m_idMap[otab->identifier()]; + ntab->setEnabled(otab->isEnabled()); + if(otab->identifier() == otabbar->currentTab()) + setCurrentTab(ntab); + } +} + +TransactionForm::TransactionForm(QWidget *parent, const char *name) : + TransactionEditorContainer(parent, name), + m_transaction(0), + m_tabBar(0) +{ + setBackgroundOrigin(QTable::WindowOrigin); + setFrameShape( QTable::NoFrame); + setShowGrid( false ); + setSelectionMode( QTable::NoSelection ); + verticalHeader()->hide(); + horizontalHeader()->hide(); + setLeftMargin(0); + setTopMargin(0); + setReadOnly(true); // display only + + // make sure, that the table is 'invisible' by setting up the right background + // keep the original color group for painting the cells though + QPalette p = palette(); + QColorGroup cg = p.active(); + m_cellColorGroup = cg; + cg.setBrush(QColorGroup::Base, cg.brush(QColorGroup::Background)); + p.setActive(cg); + p.setInactive(cg); + p.setDisabled(cg); + setPalette(p); + + // never show vertical scroll bars + setVScrollBarMode(QScrollView::AlwaysOff); + + slotSetTransaction(0); +} + +void TransactionForm::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) +{ + // the QTable::drawContents() method does not honor the block update flag + // so we take care of it here + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + QTable::drawContents(p, cx, cy, cw, ch); +} + +bool TransactionForm::focusNextPrevChild(bool next) +{ + return QFrame::focusNextPrevChild(next); +} + +void TransactionForm::clear(void) +{ + slotSetTransaction(0); +} + +void TransactionForm::slotSetTransaction(KMyMoneyRegister::Transaction* transaction) +{ + m_transaction = transaction; + + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + + if(m_transaction) { + // the next call sets up a back pointer to the form and also sets up the col and row span + // as well as the tab of the form + m_transaction->setupForm(this); + + } else { + setNumRows(5); + setNumCols(1); + } + + kMyMoneyDateInput dateInput(0, "editDate"); + KMyMoneyCategory category(0, "category", true); + + // extract the maximal sizeHint height + int height = QMAX(dateInput.sizeHint().height(), category.sizeHint().height()); + + for(int row = 0; row < numRows(); ++row) { + if(!transaction || transaction->showRowInForm(row)) { + showRow(row); + QTable::setRowHeight(row, height); + } else + hideRow(row); + } + + // adjust vertical size of form table + height *= numRows(); + setMaximumHeight(height); + setMinimumHeight(height); + + setUpdatesEnabled(enabled); + + // force resizeing of the columns + QTimer::singleShot(0, this, SLOT(resize())); +} + +void TransactionForm::paintCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& /* cg */) +{ + if(m_transaction) { + m_transaction->paintFormCell(painter, row, col, r, selected, m_cellColorGroup); + } +} + +TabBar* TransactionForm::tabBar(QWidget* parent) +{ + if(!m_tabBar && parent) { + // determine the height of the objects in the table + // create the tab bar + m_tabBar = new TabBar( parent ); + m_tabBar->setSignalEmission(TabBar::SignalAlways); + m_tabBar->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)0, 0, 0, m_tabBar->sizePolicy().hasHeightForWidth() ) ); + connect(m_tabBar, SIGNAL(tabSelected(int)), this, SLOT(slotActionSelected(int))); + } + return m_tabBar; +} + +void TransactionForm::slotActionSelected(int id) +{ + emit newTransaction(static_cast<KMyMoneyRegister::Action>(id)); +} + +void TransactionForm::setupForm(const MyMoneyAccount& acc) +{ + // remove all tabs from the tabbar + QTab* tab; + for(tab = m_tabBar->tabAt(0); tab; tab = m_tabBar->tabAt(0)) { + m_tabBar->removeTab(tab); + } + + m_tabBar->show(); + + // important: one needs to add the new tabs first and then + // change the identifier. Otherwise, addTab() will assign + // a different value + switch(acc.accountType()) { + default: + tab = new QTab(i18n("&Deposit")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionDeposit); + tab = new QTab(i18n("&Transfer")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionTransfer); + tab = new QTab(i18n("&Withdrawal")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionWithdrawal); + break; + + case MyMoneyAccount::CreditCard: + tab = new QTab(i18n("&Payment")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionDeposit); + tab = new QTab(i18n("&Transfer")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionTransfer); + tab = new QTab(i18n("&Charge")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionWithdrawal); + break; + + case MyMoneyAccount::Liability: + case MyMoneyAccount::Loan: + tab = new QTab(i18n("&Decrease")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionDeposit); + tab = new QTab(i18n("&Transfer")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionTransfer); + tab = new QTab(i18n("&Increase")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionWithdrawal); + break; + + case MyMoneyAccount::Asset: + case MyMoneyAccount::AssetLoan: + tab = new QTab(i18n("&Increase")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionDeposit); + tab = new QTab(i18n("&Transfer")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionTransfer); + tab = new QTab(i18n("&Decrease")); + m_tabBar->addTab(tab, KMyMoneyRegister::ActionWithdrawal); + break; + + case MyMoneyAccount::Income: + case MyMoneyAccount::Expense: + case MyMoneyAccount::Investment: + case MyMoneyAccount::Stock: + m_tabBar->hide(); + break; + } +} + +void TransactionForm::resize(void) +{ + resize(ValueColumn1); +} + +void TransactionForm::resize(int col) +{ + bool enabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + + // resize the register + int w = visibleWidth(); + int nc = numCols(); + + // check which space we need + if(nc >= LabelColumn1 && columnWidth(LabelColumn1)) + adjustColumn(LabelColumn1); + if(nc >= LabelColumn2 && columnWidth(LabelColumn2)) + adjustColumn(LabelColumn2); + if(nc >= ValueColumn2 && columnWidth(ValueColumn2)) + adjustColumn(ValueColumn2); + + for(int i = 0; i < nc; ++i) { + if(i == col) + continue; + + w -= columnWidth(i); + } + if(col < nc && w >= 0) + setColumnWidth(col, w); + + setUpdatesEnabled(enabled); + updateContents(); +} + +// needed to duplicate this here, as the QTable::tableSize method is private :-( +QSize TransactionForm::tableSize(void) const +{ + return QSize(columnPos(numCols()-1) + columnWidth(numCols()-1) + 10, + rowPos(numRows()-1) + rowHeight(numRows()-1) + 10); +} + +QSize TransactionForm::sizeHint(void) const +{ + // I've taken this from qtable.cpp, QTable::sizeHint() + int vmargin = QApplication::reverseLayout() ? rightMargin() : leftMargin(); + return QSize(tableSize().width() + vmargin + 5, tableSize().height() + topMargin() + 10); +} + +void TransactionForm::adjustColumn(Column col) +{ + int w = 0; + + // preset the width of the right value column with the width of + // the possible edit widgets so that they fit if they pop up + if(col == ValueColumn2) { + kMyMoneyDateInput dateInput; + kMyMoneyEdit valInput; + w = QMAX(dateInput.sizeHint().width(), valInput.sizeHint().width()); + } + + if(m_transaction) { + QString txt; + QFontMetrics fontMetrics(KMyMoneyGlobalSettings::listCellFont()); + + // scan through the rows + for ( int i = numRows()-1; i >= 0; --i ) { + int align; + m_transaction->formCellText(txt, align, i, static_cast<int>(col), 0); + QWidget* cw = cellWidget(i, col); + if(cw) { + w = QMAX(w, cw->sizeHint().width()+10); + } + w = QMAX(w, fontMetrics.width(txt)+10); + } + } + + if(col < numCols()) + setColumnWidth( col, w ); +} + +void TransactionForm::arrangeEditWidgets(QMap<QString, QWidget*>& editWidgets, KMyMoneyRegister::Transaction* t) +{ + t->arrangeWidgetsInForm(editWidgets); + resize(ValueColumn1); +} + +void TransactionForm::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const +{ + t->tabOrderInForm(tabOrderWidgets); +} + +void TransactionForm::removeEditWidgets(QMap<QString, QWidget*>& editWidgets) +{ + QMap<QString, QWidget*>::iterator it; + for(it = editWidgets.begin(); it != editWidgets.end(); ) { + if((*it)->parentWidget() == this) { + editWidgets.remove(it); + it = editWidgets.begin(); + } else + ++it; + } + + for(int row = 0; row < numRows(); ++row) { + for(int col = 0; col < numCols(); ++col) { + if(cellWidget(row, col)) + clearCellWidget(row, col); + } + } + resize(ValueColumn1); + + // delete all remaining edit widgets (e.g. tabbar) + for(it = editWidgets.begin(); it != editWidgets.end(); ) { + delete (*it); // ->deleteLater(); + editWidgets.remove(it); + it = editWidgets.begin(); + } +} + +#include "transactionform.moc" diff --git a/kmymoney2/widgets/transactionform.h b/kmymoney2/widgets/transactionform.h new file mode 100644 index 0000000..13116da --- /dev/null +++ b/kmymoney2/widgets/transactionform.h @@ -0,0 +1,223 @@ +/*************************************************************************** + transactionform.h + ---------- + begin : Sun May 14 2006 + copyright : (C) 2006 by Thomas Baumgart + email : Thomas Baumgart <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 TRANSACTIONFORM_H +#define TRANSACTIONFORM_H + +// ---------------------------------------------------------------------------- +// QT Includes + +#include <qtable.h> +#include <qvaluelist.h> +#include <qvaluevector.h> +#include <qpalette.h> +#include <qwidgetlist.h> +#include <qtabbar.h> + +// ---------------------------------------------------------------------------- +// KDE Includes + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/mymoneyaccount.h> +#include <kmymoney/mymoneyobject.h> +#include <kmymoney/register.h> + +#include "../kmymoneysettings.h" + +class MyMoneyObjectContainer; + +namespace KMyMoneyTransactionForm { + +/** + * @author Thomas Baumgart + */ +class TabBar : public QTabBar +{ + Q_OBJECT +public: + typedef enum { + SignalNormal = 0, // standard signal behaviour + SignalNever, // don't signal selection of a tab at all + SignalAlways // always signal selection of a tab + } SignalEmissionE; + + TabBar(QWidget* parent = 0, const char* name = 0); + virtual ~TabBar() {} + + SignalEmissionE setSignalEmission(SignalEmissionE type); + + void copyTabs(const TabBar* otabbar); + + void addTab(QTab* tab, int id); + + void setIdentifier(QTab* tab, int newId); + + QTab* tab(int id) const; + + int currentTab(void) const; + +public slots: + /** + * overridden for internal reasons, API not changed + */ + virtual void setCurrentTab( int ); + + /** + * overridden for internal reasons, API not changed + */ + virtual void setCurrentTab( QTab * ); + + /** + * overridden for internal reasons, API not changed + */ + virtual void show(void); + +protected slots: + void slotTabSelected(int id); + +signals: + void tabSelected(int id); + +private: + SignalEmissionE m_signalType; + + /** + * maps our internal action ids to those used by + * qt3. Since it does not seem possible to tell + * qt3 to use our ids everywhere (in QAccel) we + * need to know which is which + */ + QMap<int, int> m_idMap; + + +}; + +typedef enum { + LabelColumn1 = 0, + ValueColumn1, + LabelColumn2, + ValueColumn2, + // insert new values above this line + MaxColumns +} Column; + +/** + * @author Thomas Baumgart + */ +class TransactionForm : public TransactionEditorContainer +{ + Q_OBJECT +public: + TransactionForm(QWidget *parent = 0, const char *name = 0); + virtual ~TransactionForm() {} + + /** + * Override the QTable member function to avoid display of focus + */ + void paintFocus(QPainter* /*p*/, const QRect& /*cr*/ ) {} + + QSize tableSize(void) const; + QSize sizeHint(void) const; + void adjustColumn(Column col); + void clear(void); + + void paintCell(QPainter* painter, int row, int col, const QRect& r, bool selected, const QColorGroup& cg); + + void resize(int col); + + void arrangeEditWidgets(QMap<QString, QWidget*>& editWidgets, KMyMoneyRegister::Transaction* t); + void removeEditWidgets(QMap<QString, QWidget*>& editWidgets); + void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; + + /** + * reimplemented to prevent normal cell selection behavior + */ + void setCurrentCell(int, int) {} + + TabBar* tabBar(QWidget* parent = 0); + + void setupForm(const MyMoneyAccount& acc); + + void enableTabBar(bool b); + + protected: + /** + * reimplemented to support QWidget::WState_BlockUpdates + */ + void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + + /** + * reimplemented to prevent normal mouse press behavior + */ + void contentsMousePressEvent(QMouseEvent* ev) { ev->ignore(); } + + /** + * reimplemented to prevent normal mouse move behavior + */ + void contentsMouseMoveEvent(QMouseEvent* ev) { ev->ignore(); } + + /** + * reimplemented to prevent normal mouse release behavior + */ + void contentsMouseReleaseEvent(QMouseEvent* ev) { ev->ignore(); } + + /** + * reimplemented to prevent normal mouse double click behavior + */ + void contentsMouseDoubleClickEvent(QMouseEvent* ev) { ev->ignore(); } + + /** + * reimplemented to prevent normal keyboard behavior + */ + void keyPressEvent(QKeyEvent* ev) { ev->ignore(); } + + /** + * Override logic and use standard QFrame behaviour + */ + bool focusNextPrevChild(bool next); + +public slots: + void slotSetTransaction(KMyMoneyRegister::Transaction* item); + +protected slots: + void resize(void); + + /** + * Helper method to convert @a int into @a KMyMoneyRegister::Action + */ + void slotActionSelected(int); + +signals: + /** + * This signal is emitted when a user selects a tab. @a id + * contains the tab's id (e.g. KMyMoneyRegister::ActionDeposit) + */ + void newTransaction(KMyMoneyRegister::Action id); + +protected: + KMyMoneyRegister::Transaction* m_transaction; + QColorGroup m_cellColorGroup; + TabBar* m_tabBar; +}; + + +} // namespace + +#endif +// vim:cin:si:ai:et:ts=2:sw=2: diff --git a/kmymoney2/widgets/transactionsortoption.ui b/kmymoney2/widgets/transactionsortoption.ui new file mode 100644 index 0000000..23de01b --- /dev/null +++ b/kmymoney2/widgets/transactionsortoption.ui @@ -0,0 +1,287 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>TransactionSortOption</class> +<author>Thomas Baumgart <ipwizard@users.sourceforge.net></author> +<widget class="QWidget"> + <property name="name"> + <cstring>TransactionSortOption</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>486</width> + <height>228</height> + </rect> + </property> + <property name="caption"> + <string>TransactionSortOptionDecl</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Sort options</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_availableList</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + <property name="itemsMovable"> + <bool>false</bool> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>67</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_addButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_removeButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>67</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Sort order</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_selectedList</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>AllColumns</enum> + </property> + <property name="itemsMovable"> + <bool>false</bool> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>67</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_upButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>m_downButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>67</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> +</widget> +<connections> + <connection> + <sender>m_addButton</sender> + <signal>clicked()</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotAddItem()</slot> + </connection> + <connection> + <sender>m_availableList</sender> + <signal>selectionChanged(QListViewItem*)</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotAvailableSelected(QListViewItem*)</slot> + </connection> + <connection> + <sender>m_downButton</sender> + <signal>clicked()</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotDownItem()</slot> + </connection> + <connection> + <sender>m_removeButton</sender> + <signal>clicked()</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotRemoveItem()</slot> + </connection> + <connection> + <sender>m_selectedList</sender> + <signal>selectionChanged(QListViewItem*)</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotSelectedSelected(QListViewItem*)</slot> + </connection> + <connection> + <sender>m_upButton</sender> + <signal>clicked()</signal> + <receiver>TransactionSortOption</receiver> + <slot>slotUpItem()</slot> + </connection> + <connection> + <sender>m_selectedList</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>TransactionSortOption</receiver> + <slot>toggleDirection(QListViewItem*)</slot> + </connection> + <connection> + <sender>m_selectedList</sender> + <signal>spacePressed(QListViewItem*)</signal> + <receiver>TransactionSortOption</receiver> + <slot>toggleDirection(QListViewItem*)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">transactionsortoption.ui.h</include> +</includes> +<signals> + <signal>settingsChanged(const QString&)</signal> +</signals> +<slots> + <slot>setSettings( const QString & settings )</slot> + <slot>toggleDirection( QListViewItem * item )</slot> + <slot access="protected" specifier="non virtual">slotAvailableSelected( QListViewItem * item )</slot> + <slot access="protected" specifier="non virtual">slotSelectedSelected( QListViewItem * item )</slot> + <slot access="protected" specifier="non virtual">slotAddItem( void )</slot> + <slot access="protected" specifier="non virtual">slotRemoveItem( void )</slot> + <slot access="protected" specifier="non virtual">slotUpItem( void )</slot> + <slot access="protected" specifier="non virtual">slotDownItem( void )</slot> +</slots> +<functions> + <function specifier="non virtual">init()</function> + <function access="protected" specifier="non virtual" returnType="QListViewItem *">addEntry( KListView * p, QListViewItem * after, int idx )</function> + <function specifier="non virtual" returnType="QString">settings( void ) const</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmymoney2/widgets/transactionsortoption.ui.h b/kmymoney2/widgets/transactionsortoption.ui.h new file mode 100644 index 0000000..3784e0b --- /dev/null +++ b/kmymoney2/widgets/transactionsortoption.ui.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <kiconloader.h> +#include <klocale.h> +#include <kpushbutton.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include <kmymoney/register.h> +#include "sortoptionlistitem.h" + + +void TransactionSortOption::init() +{ + KIconLoader* il = KGlobal::iconLoader(); + m_addButton->setIconSet(QIconSet(il->loadIcon("1rightarrow", KIcon::Small, KIcon::SizeSmall))); + m_removeButton->setIconSet(QIconSet(il->loadIcon("1leftarrow", KIcon::Small, KIcon::SizeSmall))); + m_upButton->setIconSet(QIconSet(il->loadIcon("1uparrow", KIcon::Small, KIcon::SizeSmall))); + m_downButton->setIconSet(QIconSet(il->loadIcon("1downarrow", KIcon::Small, KIcon::SizeSmall))); + + // don't allow sorting of the selected entries + m_selectedList->setSortColumn(-1); + + // defaults to "post date, value" sorting + // setSettings(QString("1,4")); + setSettings(QString()); + + QListViewItem* p; + if((p = m_availableList->firstChild()) != 0) { + m_availableList->setSelected(p, true); + } +} + +/** + * Setup the two lists according to the elements found in @a list. + * If an item is negative, it will show up in the available list, + * if positive, it shows up in the selected list. + * + * Special care is taken about the two values @a EntryDateSort and + * @a EntryOrderSort. These two entries cannot (should not) exist + * alone. Inside this widget, only the @a EntryOrderSort is used. + * + * setSettings() takes care of hiding the @a EntryDateSort item and if + * it exists in @p settings without @a EntryOrderSort being present, it + * will add @a EntryOrderSort. + */ +void TransactionSortOption::setSettings(const QString& settings) +{ + m_availableList->clear(); + m_selectedList->clear(); + + QStringList list = QStringList::split(',', settings); + QMap<int, bool> selectedMap; + + // fill selected list + QStringList::const_iterator it_s; + QListViewItem* last = 0; + int dateSign = 1; + for(it_s = list.begin(); it_s != list.end(); ++it_s) { + int val = (*it_s).toInt(); + selectedMap[abs(val)] = true; + // skip EntryDateSort but keep sign + if(abs(val) == static_cast<int>(KMyMoneyRegister::EntryDateSort)) { + dateSign = (val < 0) ? -1 : 1; + continue; + } + last = addEntry(m_selectedList, last, val); + } + + // make sure to create EntryOrderSort if missing but required + if(selectedMap.find(static_cast<int>(KMyMoneyRegister::EntryDateSort)) != selectedMap.end() + && selectedMap.find(static_cast<int>(KMyMoneyRegister::EntryOrderSort)) == selectedMap.end()) { + int val = dateSign * static_cast<int>(KMyMoneyRegister::EntryOrderSort); + selectedMap[static_cast<int>(KMyMoneyRegister::EntryOrderSort)] = true; + last = addEntry(m_selectedList, last, val); + } + + // fill available list + QMap<int, bool>::const_iterator it_m; + for(int i = static_cast<int>(KMyMoneyRegister::PostDateSort); + i < static_cast<int>(KMyMoneyRegister::MaxSortFields); ++i) { + // Never add EntryDateSort + if(i == static_cast<int>(KMyMoneyRegister::EntryDateSort)) + continue; + // Only add those, that are not present in the list of selected items + if(selectedMap.find(i) == selectedMap.end()) { + int val = i; + if(i == static_cast<int>(KMyMoneyRegister::ValueSort)) + val = -val; + addEntry(m_availableList, 0, val); + } + } +} + +QListViewItem* TransactionSortOption::addEntry( KListView * p, QListViewItem* after, int idx ) +{ + QString txt = KMyMoneyRegister::sortOrderToText(static_cast<KMyMoneyRegister::TransactionSortField>(abs(idx))); + if(txt.isEmpty()) + txt = "Unknown"; // i18n should be handled in sortOptionToText() + + return new SortOptionListItem(p, after, txt, idx); +} + +void TransactionSortOption::toggleDirection(QListViewItem* item) +{ + SortOptionListItem* p = dynamic_cast<SortOptionListItem*>(item); + if(p) { + p->toggleDirection(); + emit settingsChanged(settings()); + } +} + +QString TransactionSortOption::settings( void ) const +{ + QString rc; + SortOptionListItem* item = dynamic_cast<SortOptionListItem*>(m_selectedList->firstChild()); + while(item) { + int option = KMyMoneyRegister::textToSortOrder(item->text(0)); + // if we look at the EntryOrderSort option, we have to make + // sure, that the EntryDateSort is prepended + if(option == KMyMoneyRegister::EntryOrderSort) { + rc += QString::number(static_cast<int>(KMyMoneyRegister::EntryDateSort)*item->direction())+","; + } + rc += QString::number(KMyMoneyRegister::textToSortOrder(item->text(0))*item->direction()); + item = dynamic_cast<SortOptionListItem*>(item->itemBelow()); + if(item != 0) + rc += ","; + } + return rc; +} + +void TransactionSortOption::slotAvailableSelected( QListViewItem * item ) +{ + m_addButton->setEnabled(item != 0); + m_removeButton->setDisabled(true); + m_upButton->setDisabled(true); + m_downButton->setDisabled(true); + + QListViewItem* p = m_selectedList->currentItem(); + if(p) { + m_selectedList->setSelected(p, false); + } +} + +void TransactionSortOption::slotSelectedSelected( QListViewItem * item ) +{ + m_addButton->setDisabled(true); + m_removeButton->setEnabled(item != 0); + if(item) { + m_upButton->setEnabled(item->itemAbove() != 0); + m_downButton->setEnabled(item->itemBelow() != 0); + } else { + m_upButton->setEnabled(false); + m_downButton->setEnabled(false); + } + + QListViewItem* p = m_availableList->currentItem(); + if(p) { + m_availableList->setSelected(p, false); + } +} + +void TransactionSortOption::slotAddItem( void ) +{ + QListViewItem* item; + if((item = m_availableList->currentItem()) != 0) { + QListViewItem* next = item->itemBelow(); + if(!next) + next = item->itemAbove(); + m_availableList->takeItem(item); + m_selectedList->insertItem(item); + m_addButton->setEnabled(m_availableList->firstChild() != 0); + if(next) { + m_availableList->setCurrentItem(next); + m_availableList->setSelected(next, true); + } + emit settingsChanged(settings()); + } +} + +void TransactionSortOption::slotRemoveItem( void ) +{ + QListViewItem* item; + if((item = m_selectedList->currentItem()) != 0) { + QListViewItem* next = item->itemBelow(); + if(!next) + next = item->itemAbove(); + m_selectedList->takeItem(item); + m_availableList->insertItem(item); + m_removeButton->setEnabled(m_selectedList->firstChild() != 0); + if(next) { + m_selectedList->setCurrentItem(next); + m_selectedList->setSelected(next, true); + } + emit settingsChanged(settings()); + } +} + +void TransactionSortOption::slotUpItem( void ) +{ + QListViewItem* item; + if((item = m_selectedList->currentItem()) != 0) { + QListViewItem* prev = item->itemAbove(); + if(prev) { + prev->moveItem(item); + m_selectedList->setCurrentItem(item); + m_selectedList->setSelected(item, true); + m_upButton->setEnabled(item->itemAbove() != 0); + m_downButton->setEnabled(item->itemBelow() != 0); + emit settingsChanged(settings()); + } + } +} + +void TransactionSortOption::slotDownItem( void ) +{ + QListViewItem* item; + if((item = m_selectedList->currentItem()) != 0) { + QListViewItem* next = item->itemBelow(); + if(next) { + item->moveItem(next); + m_selectedList->setCurrentItem(item); + m_selectedList->setSelected(item, true); + m_upButton->setEnabled(item->itemAbove() != 0); + m_downButton->setEnabled(item->itemBelow() != 0); + emit settingsChanged(settings()); + } + } +} |