mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Enable editing of the "Description" column in the split list view - WITH UNDO!
The Qt Undo framework is almost like magic. We just have to create a command object instead of directly manipulating the value, and suddenly the undo/redo just works. This is fun! git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18889 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
0a2d901f71
commit
81ec044f82
@ -12,6 +12,7 @@ LINK_DIRECTORIES (${GLIB2_LIBRARY_DIRS}
|
|||||||
SET (gnc_SOURCES
|
SET (gnc_SOURCES
|
||||||
AccountItemModel.cpp
|
AccountItemModel.cpp
|
||||||
Book.cpp
|
Book.cpp
|
||||||
|
Cmd.cpp
|
||||||
Numeric.cpp
|
Numeric.cpp
|
||||||
RecentFileMenu.cpp
|
RecentFileMenu.cpp
|
||||||
Session.cpp
|
Session.cpp
|
||||||
@ -30,6 +31,7 @@ SET (gnc_QOBJECT_HEADERS
|
|||||||
SET (gnc_HEADERS ${gnc_QOBJECT_HEADERS}
|
SET (gnc_HEADERS ${gnc_QOBJECT_HEADERS}
|
||||||
Account.hpp
|
Account.hpp
|
||||||
Book.hpp
|
Book.hpp
|
||||||
|
Cmd.hpp
|
||||||
Session.hpp
|
Session.hpp
|
||||||
Split.hpp
|
Split.hpp
|
||||||
Transaction.hpp
|
Transaction.hpp
|
||||||
|
91
src/gnc/Cmd.cpp
Normal file
91
src/gnc/Cmd.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Cmd.hpp
|
||||||
|
* Copyright (C) 2010 Christian Stimming
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, contact:
|
||||||
|
*
|
||||||
|
* Free Software Foundation Voice: +1-617-542-5942
|
||||||
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
|
||||||
|
* Boston, MA 02110-1301, USA gnu@gnu.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Cmd.hpp"
|
||||||
|
#include "gnc/Split.hpp"
|
||||||
|
#include "gnc/Account.hpp"
|
||||||
|
#include "gnc/Transaction.hpp"
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace gnc
|
||||||
|
{
|
||||||
|
|
||||||
|
// Explicit instantiations to check for compiler errors
|
||||||
|
template class Cmd<Account, QString>;
|
||||||
|
|
||||||
|
namespace cmd
|
||||||
|
{
|
||||||
|
|
||||||
|
QUndoCommand* setSplitMemo(Split& t, const QString& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Split, QString>(QObject::tr("Edit Split Memo"),
|
||||||
|
t, &Split::setMemo,
|
||||||
|
&Split::getMemo, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setSplitAction(Split& t, const QString& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Split, QString>(QObject::tr("Edit Split Action"),
|
||||||
|
t, &Split::setAction,
|
||||||
|
&Split::getAction, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setSplitReconcile(Split& t, char newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Split, char>(QObject::tr("Edit Split Reconcile"),
|
||||||
|
t, &Split::setReconcile,
|
||||||
|
&Split::getReconcile, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setTransactionNum(Transaction& t, const QString& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Transaction, QString>(QObject::tr("Edit Transaction Number"),
|
||||||
|
t, &Transaction::setNum,
|
||||||
|
&Transaction::getNum, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setTransactionDescription(Transaction& t, const QString& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Transaction, QString>(QObject::tr("Edit Transaction Description"),
|
||||||
|
t, &Transaction::setDescription,
|
||||||
|
&Transaction::getDescription, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setTransactionNotes(Transaction& t, const QString& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Transaction, QString>(QObject::tr("Edit Transaction Notes"),
|
||||||
|
t, &Transaction::setNotes,
|
||||||
|
&Transaction::getNotes, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoCommand* setTransactionDate(Transaction& t, const QDateTime& newValue)
|
||||||
|
{
|
||||||
|
return new Cmd<Transaction, QDateTime>(QObject::tr("Edit Transaction Date"),
|
||||||
|
t, &Transaction::setDatePosted,
|
||||||
|
&Transaction::getDatePosted, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // END namespace cmd
|
||||||
|
|
||||||
|
|
||||||
|
} // END namespace gnc
|
@ -24,11 +24,16 @@
|
|||||||
#define GNC_CMD_HPP
|
#define GNC_CMD_HPP
|
||||||
|
|
||||||
#include <QUndoCommand>
|
#include <QUndoCommand>
|
||||||
#include <gnc/Split.hpp>
|
#include <gnc/WeakPointer.hpp>
|
||||||
|
#include <gnc/Numeric.hpp>
|
||||||
|
|
||||||
namespace gnc
|
namespace gnc
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Split;
|
||||||
|
class Account;
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
template<class TargetT, class ValueT>
|
template<class TargetT, class ValueT>
|
||||||
class Cmd : public QUndoCommand
|
class Cmd : public QUndoCommand
|
||||||
{
|
{
|
||||||
@ -38,26 +43,27 @@ public:
|
|||||||
typedef void (TargetT::*setter_func)(const value_type&);
|
typedef void (TargetT::*setter_func)(const value_type&);
|
||||||
typedef value_type (TargetT::*getter_func)() const;
|
typedef value_type (TargetT::*getter_func)() const;
|
||||||
|
|
||||||
Cmd(const QString& text,
|
// Cmd(const QString& text,
|
||||||
TargetT& target, setter_func setter,
|
// TargetT& target, setter_func setter,
|
||||||
const value_type& previousValue,
|
// const value_type& previousValue,
|
||||||
const value_type& newValue,
|
// const value_type& newValue,
|
||||||
QUndoCommand *parent = 0)
|
// QUndoCommand *parent = 0)
|
||||||
: QUndoCommand(text, parent)
|
// : QUndoCommand(text, parent)
|
||||||
, m_target(target)
|
// , m_target(target)
|
||||||
, m_setter(setter)
|
// , m_setter(setter)
|
||||||
, m_previousValue(previousValue)
|
// , m_previousValue(previousValue)
|
||||||
, m_newValue(newValue)
|
// , m_newValue(newValue)
|
||||||
{
|
// {
|
||||||
}
|
// }
|
||||||
|
|
||||||
Cmd(const QString& text,
|
Cmd(const QString& text,
|
||||||
TargetT& target, setter_func setter,
|
WeakPointer<typename TargetT::element_type>& targetPtr,
|
||||||
|
setter_func setter,
|
||||||
getter_func getter,
|
getter_func getter,
|
||||||
const value_type& newValue,
|
const value_type& newValue,
|
||||||
QUndoCommand *parent = 0)
|
QUndoCommand *parent = 0)
|
||||||
: QUndoCommand(text, parent)
|
: QUndoCommand(text, parent)
|
||||||
, m_target(target)
|
, m_target(targetPtr.get())
|
||||||
, m_setter(setter)
|
, m_setter(setter)
|
||||||
, m_previousValue((m_target.*getter)())
|
, m_previousValue((m_target.*getter)())
|
||||||
, m_newValue(newValue)
|
, m_newValue(newValue)
|
||||||
@ -77,7 +83,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TargetT& m_target;
|
TargetT m_target;
|
||||||
setter_func m_setter;
|
setter_func m_setter;
|
||||||
value_type m_previousValue;
|
value_type m_previousValue;
|
||||||
value_type m_newValue;
|
value_type m_newValue;
|
||||||
@ -86,12 +92,18 @@ protected:
|
|||||||
namespace cmd
|
namespace cmd
|
||||||
{
|
{
|
||||||
|
|
||||||
QUndoCommand* setSplitMemo(Split& split, const QString& newValue)
|
// This is the collection of command objects which are already
|
||||||
{
|
// provided for the different data types and their simple
|
||||||
return new Cmd<Split, QString>(QWidget::tr("Edit Split Memo"),
|
// members. Just create one of those, add it to a QUndoStack, and
|
||||||
split, &Split::setMemo,
|
// magically the values will change with undo/redo back and
|
||||||
&Split::getMemo, newValue);
|
// forth. Spooky, IMHO.
|
||||||
}
|
QUndoCommand* setSplitMemo(Split& split, const QString& newValue);
|
||||||
|
QUndoCommand* setSplitAction(Split& t, const QString& newValue);
|
||||||
|
QUndoCommand* setSplitReconcile(Split& t, char newValue);
|
||||||
|
QUndoCommand* setTransactionNum(Transaction& t, const QString& newValue);
|
||||||
|
QUndoCommand* setTransactionDescription(Transaction& t, const QString& newValue);
|
||||||
|
QUndoCommand* setTransactionNotes(Transaction& t, const QString& newValue);
|
||||||
|
QUndoCommand* setTransactionDate(Transaction& t, const QDateTime& newValue);
|
||||||
|
|
||||||
} // END namespace cmd
|
} // END namespace cmd
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ void RecentFileMenu::updateMenu()
|
|||||||
const QString& qs = m_fileNames.at(i);
|
const QString& qs = m_fileNames.at(i);
|
||||||
QAction *act = m_actionRecentFile[i];
|
QAction *act = m_actionRecentFile[i];
|
||||||
act->setVisible(true);
|
act->setVisible(true);
|
||||||
act->setText(tr("&%1 %2").arg(i+1).arg(QFileInfo(qs).fileName()));
|
act->setText(tr("&%1 %2").arg(i + 1).arg(QFileInfo(qs).fileName()));
|
||||||
act->setStatusTip(qs);
|
act->setStatusTip(qs);
|
||||||
act->setData(qs);
|
act->setData(qs);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public:
|
|||||||
void setAction(const QString& v) { xaccSplitSetAction(get(), v.toUtf8()); }
|
void setAction(const QString& v) { xaccSplitSetAction(get(), v.toUtf8()); }
|
||||||
|
|
||||||
char getReconcile() const { return xaccSplitGetReconcile(get()); }
|
char getReconcile() const { return xaccSplitGetReconcile(get()); }
|
||||||
void setReconcile(char v) { xaccSplitSetReconcile(get(), v); }
|
void setReconcile(const char& v) { xaccSplitSetReconcile(get(), v); }
|
||||||
|
|
||||||
Split getOtherSplit() const { return xaccSplitGetOtherSplit(get()); }
|
Split getOtherSplit() const { return xaccSplitGetOtherSplit(get()); }
|
||||||
|
|
||||||
|
@ -22,14 +22,17 @@
|
|||||||
|
|
||||||
#include "SplitListModel.hpp"
|
#include "SplitListModel.hpp"
|
||||||
#include "gnc/Transaction.hpp"
|
#include "gnc/Transaction.hpp"
|
||||||
|
#include "gnc/Cmd.hpp"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QUndoStack>
|
||||||
|
|
||||||
namespace gnc
|
namespace gnc
|
||||||
{
|
{
|
||||||
|
|
||||||
SplitListModel::SplitListModel(const SplitQList splits, QObject *parent)
|
SplitListModel::SplitListModel(const SplitQList splits, QUndoStack* undoStack, QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
, m_list(splits)
|
, m_list(splits)
|
||||||
|
, m_undoStack(undoStack)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +103,16 @@ Qt::ItemFlags SplitListModel::flags(const QModelIndex &index) const
|
|||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
Qt::ItemFlags result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
// Allow write access as well
|
||||||
|
return result | Qt::ItemIsEditable;
|
||||||
|
default:
|
||||||
// Ensure read-only access only
|
// Ensure read-only access only
|
||||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant SplitListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant SplitListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
@ -133,5 +144,28 @@ QVariant SplitListModel::headerData(int section, Qt::Orientation orientation, in
|
|||||||
return QString("%1").arg(1 + section);
|
return QString("%1").arg(1 + section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SplitListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (index.isValid() && role == Qt::EditRole)
|
||||||
|
{
|
||||||
|
Split split(static_cast< ::Split*>(index.internalPointer()));
|
||||||
|
Transaction trans(split.getParent());
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
// We allow to edit column 2. "Editing" is done by
|
||||||
|
// creating a Cmd-object and adding it to the undo
|
||||||
|
// stack. That's in fact all that was needed to create an
|
||||||
|
// *undoable* edit of this data cell. Seems almost spooky,
|
||||||
|
// doesn't it?
|
||||||
|
m_undoStack->push(cmd::setTransactionDescription(trans, value.toString()));
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // END namespace gnc
|
} // END namespace gnc
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "gnc/Split.hpp"
|
#include "gnc/Split.hpp"
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
class QUndoStack;
|
||||||
|
|
||||||
namespace gnc
|
namespace gnc
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ class SplitListModel : public QAbstractItemModel
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SplitListModel(const SplitQList splits, QObject *parent = 0);
|
SplitListModel(const SplitQList splits, QUndoStack* undoStack, QObject *parent = 0);
|
||||||
|
|
||||||
QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); }
|
QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); }
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_list.size(); }
|
int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_list.size(); }
|
||||||
@ -47,9 +48,12 @@ public:
|
|||||||
|
|
||||||
QVariant data(const QModelIndex& index, int role) const;
|
QVariant data(const QModelIndex& index, int role) const;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SplitQList m_list;
|
SplitQList m_list;
|
||||||
|
QUndoStack* m_undoStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // END namespace gnc
|
} // END namespace gnc
|
||||||
|
@ -61,8 +61,8 @@ public:
|
|||||||
|
|
||||||
int countSplits() const { return xaccTransCountSplits(get()); }
|
int countSplits() const { return xaccTransCountSplits(get()); }
|
||||||
|
|
||||||
void setDatePosted(const QDateTime& t);
|
void setDatePosted(const QDateTime& t) { xaccTransSetDatePostedSecs(get(), t.toTime_t()); }
|
||||||
void setDateEntered(const QDateTime& t);
|
void setDateEntered(const QDateTime& t) { xaccTransSetDateEnteredSecs(get(), t.toTime_t()); }
|
||||||
QDateTime getDatePosted() const { return toQDateTime(xaccTransRetDatePostedTS(get())); }
|
QDateTime getDatePosted() const { return toQDateTime(xaccTransRetDatePostedTS(get())); }
|
||||||
QDateTime getDateEntered() const { return toQDateTime(xaccTransRetDateEnteredTS(get())); }
|
QDateTime getDateEntered() const { return toQDateTime(xaccTransRetDateEnteredTS(get())); }
|
||||||
|
|
||||||
|
@ -57,9 +57,6 @@ extern "C"
|
|||||||
namespace gnc
|
namespace gnc
|
||||||
{
|
{
|
||||||
|
|
||||||
// Explicit instantiations to check for compiler errors
|
|
||||||
template class Cmd<Account, QString>;
|
|
||||||
|
|
||||||
inline QString errorToString(QofBackendError err)
|
inline QString errorToString(QofBackendError err)
|
||||||
{
|
{
|
||||||
return errorToStringPair(err).first;
|
return errorToStringPair(err).first;
|
||||||
@ -420,7 +417,7 @@ void MainWindow::accountItemActivated(const QModelIndex & index)
|
|||||||
// We create a new model for this list of splits and also a view
|
// We create a new model for this list of splits and also a view
|
||||||
// widget for this list.
|
// widget for this list.
|
||||||
QTableView *tableView = new QTableView(ui->tabWidget); // FIXME: Is this parent correct?
|
QTableView *tableView = new QTableView(ui->tabWidget); // FIXME: Is this parent correct?
|
||||||
SplitListModel *smodel = new SplitListModel(Split::fromGList(slist), tableView);
|
SplitListModel *smodel = new SplitListModel(Split::fromGList(slist), m_undoStack, tableView);
|
||||||
tableView->setModel(smodel);
|
tableView->setModel(smodel);
|
||||||
tableView->setAlternatingRowColors(true);
|
tableView->setAlternatingRowColors(true);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user