r18889 - gnucash/trunk/src/gnc - Enable editing of the "Description" column in the split list view - WITH UNDO!

Christian Stimming cstim at code.gnucash.org
Wed Mar 10 16:40:58 EST 2010


Author: cstim
Date: 2010-03-10 16:40:58 -0500 (Wed, 10 Mar 2010)
New Revision: 18889
Trac: http://svn.gnucash.org/trac/changeset/18889

Added:
   gnucash/trunk/src/gnc/Cmd.cpp
Modified:
   gnucash/trunk/src/gnc/CMakeLists.txt
   gnucash/trunk/src/gnc/Cmd.hpp
   gnucash/trunk/src/gnc/RecentFileMenu.cpp
   gnucash/trunk/src/gnc/Split.hpp
   gnucash/trunk/src/gnc/SplitListModel.cpp
   gnucash/trunk/src/gnc/SplitListModel.hpp
   gnucash/trunk/src/gnc/Transaction.hpp
   gnucash/trunk/src/gnc/mainwindow.cpp
Log:
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!

Modified: gnucash/trunk/src/gnc/CMakeLists.txt
===================================================================
--- gnucash/trunk/src/gnc/CMakeLists.txt	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/CMakeLists.txt	2010-03-10 21:40:58 UTC (rev 18889)
@@ -12,6 +12,7 @@
 SET (gnc_SOURCES
   AccountItemModel.cpp
   Book.cpp
+  Cmd.cpp
   Numeric.cpp
   RecentFileMenu.cpp
   Session.cpp
@@ -30,6 +31,7 @@
 SET (gnc_HEADERS ${gnc_QOBJECT_HEADERS}
   Account.hpp
   Book.hpp
+  Cmd.hpp
   Session.hpp
   Split.hpp
   Transaction.hpp

Added: gnucash/trunk/src/gnc/Cmd.cpp
===================================================================
--- gnucash/trunk/src/gnc/Cmd.cpp	                        (rev 0)
+++ gnucash/trunk/src/gnc/Cmd.cpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -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 at 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

Modified: gnucash/trunk/src/gnc/Cmd.hpp
===================================================================
--- gnucash/trunk/src/gnc/Cmd.hpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/Cmd.hpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -24,11 +24,16 @@
 #define GNC_CMD_HPP
 
 #include <QUndoCommand>
-#include <gnc/Split.hpp>
+#include <gnc/WeakPointer.hpp>
+#include <gnc/Numeric.hpp>
 
 namespace gnc
 {
 
+class Split;
+class Account;
+class Transaction;
+
 template<class TargetT, class ValueT>
 class Cmd : public QUndoCommand
 {
@@ -38,26 +43,27 @@
     typedef void (TargetT::*setter_func)(const value_type&);
     typedef value_type (TargetT::*getter_func)() const;
 
-    Cmd(const QString& text,
-        TargetT& target, setter_func setter,
-        const value_type& previousValue,
-        const value_type& newValue,
-        QUndoCommand *parent = 0)
-            : QUndoCommand(text, parent)
-            , m_target(target)
-            , m_setter(setter)
-            , m_previousValue(previousValue)
-            , m_newValue(newValue)
-    {
-    }
+//     Cmd(const QString& text,
+//         TargetT& target, setter_func setter,
+//         const value_type& previousValue,
+//         const value_type& newValue,
+//         QUndoCommand *parent = 0)
+//             : QUndoCommand(text, parent)
+//             , m_target(target)
+//             , m_setter(setter)
+//             , m_previousValue(previousValue)
+//             , m_newValue(newValue)
+//     {
+//     }
 
     Cmd(const QString& text,
-        TargetT& target, setter_func setter,
+        WeakPointer<typename TargetT::element_type>& targetPtr,
+        setter_func setter,
         getter_func getter,
         const value_type& newValue,
         QUndoCommand *parent = 0)
             : QUndoCommand(text, parent)
-            , m_target(target)
+            , m_target(targetPtr.get())
             , m_setter(setter)
             , m_previousValue((m_target.*getter)())
             , m_newValue(newValue)
@@ -77,7 +83,7 @@
 
 
 protected:
-    TargetT& m_target;
+    TargetT m_target;
     setter_func m_setter;
     value_type m_previousValue;
     value_type m_newValue;
@@ -86,12 +92,18 @@
 namespace cmd
 {
 
-QUndoCommand* setSplitMemo(Split& split, const QString& newValue)
-{
-    return new Cmd<Split, QString>(QWidget::tr("Edit Split Memo"),
-                                   split, &Split::setMemo,
-                                   &Split::getMemo, newValue);
-}
+// This is the collection of command objects which are already
+// provided for the different data types and their simple
+// members. Just create one of those, add it to a QUndoStack, and
+// magically the values will change with undo/redo back and
+// 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
 

Modified: gnucash/trunk/src/gnc/RecentFileMenu.cpp
===================================================================
--- gnucash/trunk/src/gnc/RecentFileMenu.cpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/RecentFileMenu.cpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -80,7 +80,7 @@
         const QString& qs = m_fileNames.at(i);
         QAction *act = m_actionRecentFile[i];
         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->setData(qs);
     }

Modified: gnucash/trunk/src/gnc/Split.hpp
===================================================================
--- gnucash/trunk/src/gnc/Split.hpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/Split.hpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -68,7 +68,7 @@
     void setAction(const QString& v) { xaccSplitSetAction(get(), v.toUtf8()); }
 
     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()); }
 

Modified: gnucash/trunk/src/gnc/SplitListModel.cpp
===================================================================
--- gnucash/trunk/src/gnc/SplitListModel.cpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/SplitListModel.cpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -22,14 +22,17 @@
 
 #include "SplitListModel.hpp"
 #include "gnc/Transaction.hpp"
+#include "gnc/Cmd.hpp"
 #include <QDebug>
+#include <QUndoStack>
 
 namespace gnc
 {
 
-SplitListModel::SplitListModel(const SplitQList splits, QObject *parent)
+SplitListModel::SplitListModel(const SplitQList splits, QUndoStack* undoStack, QObject *parent)
         : QAbstractItemModel(parent)
         , m_list(splits)
+        , m_undoStack(undoStack)
 {
 }
 
@@ -100,8 +103,16 @@
     if (!index.isValid())
         return 0;
 
-    // Ensure read-only access only
-    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+    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
+        return result;
+    }
 }
 
 QVariant SplitListModel::headerData(int section, Qt::Orientation orientation, int role) const
@@ -133,5 +144,28 @@
         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

Modified: gnucash/trunk/src/gnc/SplitListModel.hpp
===================================================================
--- gnucash/trunk/src/gnc/SplitListModel.hpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/SplitListModel.hpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -26,6 +26,7 @@
 #include "gnc/Split.hpp"
 
 #include <QAbstractItemModel>
+class QUndoStack;
 
 namespace gnc
 {
@@ -36,7 +37,7 @@
 {
     Q_OBJECT
 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(); }
     int rowCount(const QModelIndex& parent = QModelIndex()) const { return m_list.size(); }
@@ -47,9 +48,12 @@
 
     QVariant data(const QModelIndex& index, int role) const;
     QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+    bool setData(const QModelIndex &index, const QVariant &value, int role);
 
+
 protected:
     SplitQList m_list;
+    QUndoStack* m_undoStack;
 };
 
 } // END namespace gnc

Modified: gnucash/trunk/src/gnc/Transaction.hpp
===================================================================
--- gnucash/trunk/src/gnc/Transaction.hpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/Transaction.hpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -61,8 +61,8 @@
 
     int countSplits() const { return xaccTransCountSplits(get()); }
 
-    void setDatePosted(const QDateTime& t);
-    void setDateEntered(const QDateTime& t);
+    void setDatePosted(const QDateTime& t) { xaccTransSetDatePostedSecs(get(), t.toTime_t()); }
+    void setDateEntered(const QDateTime& t) { xaccTransSetDateEnteredSecs(get(), t.toTime_t()); }
     QDateTime getDatePosted() const { return toQDateTime(xaccTransRetDatePostedTS(get())); }
     QDateTime getDateEntered() const { return toQDateTime(xaccTransRetDateEnteredTS(get())); }
 

Modified: gnucash/trunk/src/gnc/mainwindow.cpp
===================================================================
--- gnucash/trunk/src/gnc/mainwindow.cpp	2010-03-10 19:38:03 UTC (rev 18888)
+++ gnucash/trunk/src/gnc/mainwindow.cpp	2010-03-10 21:40:58 UTC (rev 18889)
@@ -57,9 +57,6 @@
 namespace gnc
 {
 
-// Explicit instantiations to check for compiler errors
-template class Cmd<Account, QString>;
-
 inline QString errorToString(QofBackendError err)
 {
     return errorToStringPair(err).first;
@@ -420,7 +417,7 @@
     // We create a new model for this list of splits and also a view
     // widget for this list.
     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->setAlternatingRowColors(true);
 



More information about the gnucash-changes mailing list