Cutecash: Add deletion of rows/transactions (through "Cut"). With Undo. Hee Hee.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18927 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Christian Stimming 2010-03-17 21:28:51 +00:00
parent 4e3be7b89c
commit 07d25ab17b
10 changed files with 253 additions and 20 deletions

View File

@ -178,6 +178,53 @@ QUndoCommand* setTransactionDate(Transaction& t, const QDate& newValue)
t.getDatePosted(), newValue);
}
// ////////////////////////////////////////////////////////////
class TransactionDestroyCmd : public QUndoCommand
{
public:
typedef QUndoCommand base_class;
typedef Transaction target_type;
/** Constructor
*/
TransactionDestroyCmd(const QString& text,
WeakPointer<target_type::element_type>& targetPtr,
QUndoCommand *parent = 0)
: base_class(text, parent)
, m_target(targetPtr.get())
, m_previousValue(m_target)
, m_book(m_target.getBook())
{
Q_ASSERT(m_target);
}
virtual void redo()
{
xaccTransDestroy(m_target.get());
m_target.reset();
}
virtual void undo()
{
m_target.reset(xaccMallocTransaction (m_book.get()));
m_target.beginEdit();
m_previousValue.copyTo(m_target);
m_target.commitEdit();
}
protected:
target_type m_target;
TmpTransaction m_previousValue;
Book m_book;
};
QUndoCommand* destroyTransaction(Transaction& t)
{
return new TransactionDestroyCmd(QObject::tr("Delete Transaction"),
t);
}
} // END namespace cmd

View File

@ -166,6 +166,7 @@ QUndoCommand* setTransactionDescription(Transaction& t, const QString& newValue)
QUndoCommand* setTransactionNotes(Transaction& t, const QString& newValue);
QUndoCommand* setTransactionDate(Transaction& t, const QDate& newValue);
QUndoCommand* setSplitValueAndAmount(Split& t, const Numeric& newValue);
QUndoCommand* destroyTransaction(Transaction& t);
} // END namespace cmd

View File

@ -52,18 +52,16 @@ inline const char* getQofType(QofInstance* obj)
* The receiver's class is the first template argument; the argument
* type of the to-be-called member function is the second template
* (usually a pointer type). */
template<class ReceiverT, class ValuePtrT, typename SlotFunc = void (ReceiverT::*)(ValuePtrT)>
template<class ReceiverT, class ValuePtrT, typename SlotFunc = void (ReceiverT::*)(ValuePtrT, QofEventId)>
class QofEventWrapper
{
public:
QofEventWrapper(ReceiverT& receiver,
SlotFunc recvSlot,
const char* qof_type,
QofEventId event_type)
const char* qof_type)
: m_receiver(receiver)
, m_receiveFunc(recvSlot)
, m_qof_type(qof_type)
, m_event_type(event_type)
{
m_handler_id = qof_event_register_handler(QofEventWrapper::event_handler, this);
}
@ -91,23 +89,22 @@ private:
// correct event type
if (!QOF_CHECK_TYPE(entity, m_qof_type))
return;
if ((event_type & m_event_type) == 0)
return;
// if ((event_type & m_event_type) == 0)
// return;
qDebug() << "private_event_handler, id=" << qofEventToString(event_type)
<< "entity=" << getQofType(entity);
// qDebug() << "private_event_handler, id=" << qofEventToString(event_type)
// << "entity=" << getQofType(entity);
ValuePtrT vptr = reinterpret_cast<ValuePtrT>(entity);
// Call the pointer-to-member function with that weird C++
// syntax
(m_receiver.*m_receiveFunc)(vptr);
(m_receiver.*m_receiveFunc)(vptr, event_type);
}
ReceiverT& m_receiver;
SlotFunc m_receiveFunc;
const char* m_qof_type;
QofEventId m_event_type;
gint m_handler_id;
};

View File

@ -33,11 +33,33 @@ Book Split::getBook() const { return xaccSplitGetBook(get()); }
Account Split::getAccount() const { return xaccSplitGetAccount(get()); }
void Split::setAccount(Account& acc) { xaccSplitSetAccount(get(), acc.get()); }
void Split::setAccount(::Account* acc) { xaccSplitSetAccount(get(), acc); }
Transaction Split::getParent() const { return xaccSplitGetParent(get()); }
void Split::setParent(Transaction& trans) { xaccSplitSetParent(get(), trans.get()); }
TmpSplit::TmpSplit(const Split& s, const TmpTransaction* parent_trans)
: account(s.getAccount().get())
, parent(parent_trans)
, memo(s.getMemo())
, action(s.getAction())
, reconcile(s.getReconcile())
, amount(s.getAmount())
, value(s.getValue())
{}
void TmpSplit::copyInto(Transaction& t)
{
Split s(xaccMallocSplit(t.getBook().get()));
s.setAccount(account);
s.setParent(t);
s.setMemo(memo);
s.setAction(action);
s.setReconcile(reconcile);
s.setAmount(amount);
s.setValue(value);
}
} // END namespace gnc

View File

@ -42,6 +42,7 @@ namespace gnc
class Book;
class Account;
class Transaction;
class TmpTransaction;
typedef QList< ::Split*> SplitQList;
@ -64,6 +65,7 @@ public:
Book getBook() const;
Account getAccount() const;
void setAccount(Account& acc);
void setAccount(::Account* acc);
Transaction getParent() const;
void setParent(Transaction& trans);
@ -110,6 +112,22 @@ public:
}
};
class TmpSplit
{
public:
TmpSplit(const Split& s, const TmpTransaction* parent_trans);
TmpSplit()
{}
void copyInto(Transaction& t);
::Account* account;
const TmpTransaction* parent;
QString memo;
QString action;
char reconcile;
Numeric amount;
Numeric value;
};
} // END namespace gnc
#endif

View File

@ -21,6 +21,7 @@
*/
#include "SplitListModel.hpp"
#include "engine/gnc-event.h" // for GNC_EVENT_ITEM_ADDED
#include "gnc/Transaction.hpp"
#include "gnc/Cmd.hpp"
#include "gnc/Session.hpp"
@ -39,16 +40,30 @@ namespace gnc
SplitListModel::SplitListModel(const Account& acc, QUndoStack* undoStack, QObject *parent)
: QAbstractItemModel(parent)
, m_account(acc)
, m_list(Split::fromGList(acc.getSplitList()))
, m_list()
, m_undoStack(undoStack)
, m_eventWrapper(*this, &SplitListModel::transactionModified,
GNC_ID_TRANS, QOF_EVENT_MODIFY)
, m_eventWrapper(*this, &SplitListModel::transactionEvent, GNC_ID_TRANS)
, m_eventWrapperAccount(*this, &SplitListModel::accountEvent, GNC_ID_ACCOUNT)
{
recreateCache();
}
void SplitListModel::recreateCache()
{
SplitQList newSplits = Split::fromGList(m_account.getSplitList());
bool doReset = (newSplits.size() != m_list.size());
m_list = newSplits;
// Cache the mapping of transactions to split in the m_hash
m_hash.clear();
for (int k = 0; k < m_list.size(); ++k)
{
m_hash.insert(Split(m_list[k]).getParent().get(), k);
}
if (doReset)
reset();
}
SplitListModel::~SplitListModel()
@ -72,6 +87,29 @@ QModelIndex SplitListModel::index(int row, int column,
return QModelIndex();
}
bool SplitListModel::insertRows(int position, int rows, const QModelIndex &index)
{
beginInsertRows(QModelIndex(), position, position + rows - 1);
endInsertRows();
return true;
}
bool SplitListModel::removeRows(int position, int rows, const QModelIndex &index)
{
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row = position; row < position + rows; ++row)
{
Split s(m_list.at(row));
Q_ASSERT(s);
Transaction t = s.getParent();
Q_ASSERT(t);
QUndoCommand* cmd = cmd::destroyTransaction(t);
m_undoStack->push(cmd);
}
endRemoveRows();
return true;
}
int SplitListModel::columnCount(const QModelIndex& parent) const
{
//qDebug() << "columnCount()" << parent;
@ -344,14 +382,39 @@ bool SplitListModel::setData(const QModelIndex &index, const QVariant &value, in
return false;
}
void SplitListModel::transactionModified( ::Transaction* trans)
void SplitListModel::transactionEvent( ::Transaction* trans, QofEventId event_type)
{
if (m_hash.contains(trans))
qDebug() << "transactionEvent, id=" << qofEventToString(event_type);
switch (event_type)
{
int row = m_hash.value(trans);
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
case QOF_EVENT_MODIFY:
if (m_hash.contains(trans))
{
int row = m_hash.value(trans);
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
}
break;
default:
break;
}
}
void SplitListModel::accountEvent( ::Account* acc, QofEventId event_type)
{
if (acc != m_account.get())
return;
qDebug() << "accountEvent, id=" << qofEventToString(event_type);
switch (event_type)
{
case GNC_EVENT_ITEM_REMOVED:
case GNC_EVENT_ITEM_ADDED:
recreateCache();
break;
default:
break;
}
}
} // END namespace gnc

View File

@ -59,9 +59,15 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
public slots:
void transactionModified( ::Transaction* trans);
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
public slots:
void transactionEvent( ::Transaction* trans, QofEventId event_type);
void accountEvent( ::Account* trans, QofEventId event_type);
private:
void recreateCache();
protected:
Account m_account;
SplitQList m_list;
@ -71,6 +77,7 @@ protected:
/** The wrapper for receiving events from gnc. */
QofEventWrapper<SplitListModel, ::Transaction*> m_eventWrapper;
QofEventWrapper<SplitListModel, ::Account*> m_eventWrapperAccount;
};
} // END namespace gnc

View File

@ -58,6 +58,8 @@ public:
: base_class(ptr)
{ }
Book getBook() const { return xaccTransGetBook(get()); }
void beginEdit() { xaccTransBeginEdit(get()); }
void commitEdit() { xaccTransCommitEdit(get()); }
void rollbackEdit() { xaccTransRollbackEdit(get()); }
@ -78,7 +80,7 @@ public:
void appendSplit(Split& split) { xaccSplitSetParent(split.get(), get()); }
Split getSplit(int i) const { return xaccTransGetSplit(get(), i); }
int getSplitIndex(const Split& split) const { return xaccTransGetSplitIndex(get(), split.get()); }
SplitList* getSplitList() const { return xaccTransGetSplitList(get()); }
::SplitList* getSplitList() const { return xaccTransGetSplitList(get()); }
Commodity getCurrency() const { return xaccTransGetCurrency(get()); }
void setCurrency(const Commodity& c) { xaccTransSetCurrency(get(), c.get()); }
@ -94,6 +96,46 @@ public:
};
class TmpTransaction
{
public:
TmpTransaction(const Transaction& t)
: num(t.getNum())
, description(t.getDescription())
, notes(t.getNotes())
, commodity(t.getCurrency())
, datePosted(t.getDatePosted())
, dateTimeEntered(t.getDateEntered())
{
SplitQList slist = Split::fromGList(t.getSplitList());
Q_FOREACH(Split s, slist)
{
splits.push_back(TmpSplit(s, this));
}
}
void copyTo(Transaction& t)
{
t.setNum(num);
t.setDescription(description);
if (!notes.isEmpty())
t.setNotes(notes);
t.setCurrency(commodity);
t.setDatePosted(datePosted);
t.setDateEntered(dateTimeEntered);
Q_FOREACH(TmpSplit s, splits)
{
s.copyInto(t);
}
}
QString num;
QString description;
QString notes;
QList<TmpSplit> splits;
Commodity commodity;
QDate datePosted;
QDateTime dateTimeEntered;
};
} // END namespace gnc
#endif

View File

@ -435,11 +435,44 @@ void MainWindow::accountItemActivated(const QModelIndex & index)
tableView->scrollToBottom();
if (smodel->rowCount() > 0)
tableView->setCurrentIndex(smodel->index(smodel->rowCount() - 1, 0));
ui->actionCut->setEnabled(smodel->rowCount() > 0);
// Insert this as a new tab
tableView->setProperty(PROPERTY_TAB_PREVIOUSPOS, ui->tabWidget->currentIndex());
ui->tabWidget->addTab(tableView, account.getName());
ui->tabWidget->setCurrentWidget(tableView);
connect(tableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection & )));
}
void MainWindow::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected )
{
ui->actionCut->setEnabled(!selected.isEmpty());
//ui->actionCopy->setEnabled(!selected.isEmpty());
}
// Auto-connected to actionCut's signal triggered()
void MainWindow::on_actionCut_triggered()
{
QWidget *widget = ui->tabWidget->currentWidget();
QTableView *tableView = qobject_cast<QTableView *>(widget);
if (tableView)
{
// QModelIndexList selection = tableView->selectionModel()->selectedIndexes();
// QSet<int> rows;
// Q_FOREACH (QModelIndex index, selection)
// {
// rows.insert(index.row());
// }
// qDebug() << "Removing number of rows:" << rows.size();
QModelIndex index = tableView->currentIndex();
if (!index.isValid())
return;
QAbstractItemModel *model = tableView->model();
Q_ASSERT(model);
model->removeRow(index.row());
}
}
// ////////////////////////////////////////////////////////////

View File

@ -24,6 +24,7 @@
#define MAINWINDOW_H
#include <QMainWindow>
#include <QItemSelection>
#include "gnc/Session.hpp"
#include "gnc/AccountItemModel.hpp"
@ -72,6 +73,7 @@ private slots:
void on_actionAbout_triggered();
bool on_actionSave_as_triggered();
void on_actionCloseTab_triggered();
void on_actionCut_triggered();
void on_tabWidget_tabCloseRequested(int index);
void on_tabWidget_currentChanged(int index);
void on_textBrowser_anchorClicked(const QUrl &);
@ -79,6 +81,7 @@ private slots:
void on_actionViewAccountList_triggered(bool checked);
void on_actionViewWelcomepage_triggered(bool checked);
void documentWasModified();
void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected );
private:
void createActions();