From 8476d5d407645229faf3017b390f041ce0666247 Mon Sep 17 00:00:00 2001
From: Cozz Lovan <cozzlovan@yahoo.com>
Date: Tue, 14 Jan 2014 05:05:43 +0100
Subject: [Qt] Permanently store requested payments in wallet

---
 src/qt/recentrequeststablemodel.cpp | 50 ++++++++++++++++++++++++++++++++++++-
 src/qt/recentrequeststablemodel.h   | 27 +++++++++++++++++++-
 src/qt/walletmodel.cpp              | 24 ++++++++++++++++++
 src/qt/walletmodel.h                | 42 +++++++++++++++++++++++++++++--
 4 files changed, 139 insertions(+), 4 deletions(-)

(limited to 'src/qt')

diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 86c29dd02b..70614f9eae 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -12,6 +12,13 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel
     walletModel(parent)
 {
     Q_UNUSED(wallet);
+    nReceiveRequestsMaxId = 0;
+
+    // Load entries from wallet
+    std::vector<std::string> vReceiveRequests;
+    parent->loadReceiveRequests(vReceiveRequests);
+    BOOST_FOREACH(const std::string& request, vReceiveRequests)
+        addNewRequest(request);
 
     /* These columns must match the indices in the ColumnIndex enumeration */
     columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount");
@@ -104,6 +111,14 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex
 
     if(count > 0 && row >= 0 && (row+count) <= list.size())
     {
+        const RecentRequestEntry *rec;
+        for (int i = 0; i < count; ++i)
+        {
+            rec = &list[row+i];
+            if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, ""))
+                return false;
+        }
+
         beginRemoveRows(parent, row, row + count - 1);
         list.erase(list.begin() + row, list.begin() + row + count);
         endRemoveRows();
@@ -118,12 +133,45 @@ Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const
     return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
 }
 
+// called when adding a request from the GUI
 void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient)
 {
     RecentRequestEntry newEntry;
+    newEntry.id = ++nReceiveRequestsMaxId;
     newEntry.date = QDateTime::currentDateTime();
     newEntry.recipient = recipient;
+
+    CDataStream ss(SER_DISK, CLIENT_VERSION);
+    ss << newEntry;
+
+    if (!walletModel->saveReceiveRequest(recipient.address.toStdString(), newEntry.id, ss.str()))
+        return;
+
+    addNewRequest(newEntry);
+}
+
+// called from ctor when loading from wallet
+void RecentRequestsTableModel::addNewRequest(const std::string &recipient)
+{
+    std::vector<char> data(recipient.begin(), recipient.end());
+    CDataStream ss(data, SER_DISK, CLIENT_VERSION);
+
+    RecentRequestEntry entry;
+    ss >> entry;
+
+    if (entry.id == 0) // should not happen
+        return;
+
+    if (entry.id > nReceiveRequestsMaxId)
+        nReceiveRequestsMaxId = entry.id;
+
+    addNewRequest(entry);
+}
+
+// actually add to table in GUI
+void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
+{
     beginInsertRows(QModelIndex(), 0, 0);
-    list.prepend(newEntry);
+    list.prepend(recipient);
     endInsertRows();
 }
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 3aab7b0a48..f939c76666 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -13,10 +13,32 @@
 
 class CWallet;
 
-struct RecentRequestEntry
+class RecentRequestEntry
 {
+public:
+    RecentRequestEntry() : nVersion(RecentRequestEntry::CURRENT_VERSION), id(0) { }
+
+    static const int CURRENT_VERSION=1;
+    int nVersion;
+    int64_t id;
     QDateTime date;
     SendCoinsRecipient recipient;
+
+    IMPLEMENT_SERIALIZE
+    (
+        RecentRequestEntry* pthis = const_cast<RecentRequestEntry*>(this);
+
+        unsigned int nDate = date.toTime_t();
+
+        READWRITE(pthis->nVersion);
+        nVersion = pthis->nVersion;
+        READWRITE(id);
+        READWRITE(nDate);
+        READWRITE(recipient);
+
+        if (fRead)
+            pthis->date = QDateTime::fromTime_t(nDate);
+    )
 };
 
 /** Model for list of recently generated payment requests / bitcoin URIs.
@@ -51,11 +73,14 @@ public:
 
     const RecentRequestEntry &entry(int row) const { return list[row]; }
     void addNewRequest(const SendCoinsRecipient &recipient);
+    void addNewRequest(const std::string &recipient);
+    void addNewRequest(RecentRequestEntry &recipient);
 
 private:
     WalletModel *walletModel;
     QStringList columns;
     QList<RecentRequestEntry> list;
+    int64_t nReceiveRequestsMaxId;
 };
 
 #endif
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 14f29c933b..01f5a304a9 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -554,3 +554,27 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
     LOCK(wallet->cs_wallet);
     wallet->ListLockedCoins(vOutpts);
 }
+
+void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
+{
+    LOCK(wallet->cs_wallet);
+    BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
+        BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata)
+            if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request
+                vReceiveRequests.push_back(item2.second);
+}
+
+bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
+{
+    CTxDestination dest = CBitcoinAddress(sAddress).Get();
+
+    std::stringstream ss;
+    ss << nId;
+    std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
+
+    LOCK(wallet->cs_wallet);
+    if (sRequest.empty())
+        return wallet->EraseDestData(dest, key);
+    else
+        return wallet->AddDestData(dest, key, sRequest);
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 1a4d25615a..600bef346b 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -36,9 +36,9 @@ QT_END_NAMESPACE
 class SendCoinsRecipient
 {
 public:
-    explicit SendCoinsRecipient() : amount(0) { }
+    explicit SendCoinsRecipient() : amount(0), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
     explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message):
-        address(addr), label(label), amount(amount), message(message) {}
+        address(addr), label(label), amount(amount), message(message), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
 
     // If from an insecure payment request, this is used for storing
     // the addresses, e.g. address-A<br />address-B<br />address-C.
@@ -55,6 +55,41 @@ public:
     PaymentRequestPlus paymentRequest;
     // Empty if no authentication or invalid signature/cert/etc.
     QString authenticatedMerchant;
+
+    static const int CURRENT_VERSION=1;
+    int nVersion;
+
+    IMPLEMENT_SERIALIZE
+    (
+        SendCoinsRecipient* pthis = const_cast<SendCoinsRecipient*>(this);
+
+        std::string sAddress = pthis->address.toStdString();
+        std::string sLabel = pthis->label.toStdString();
+        std::string sMessage = pthis->message.toStdString();
+        std::string sPaymentRequest;
+        if (!fRead && pthis->paymentRequest.IsInitialized())
+            pthis->paymentRequest.SerializeToString(&sPaymentRequest);
+        std::string sAuthenticatedMerchant = pthis->authenticatedMerchant.toStdString();
+
+        READWRITE(pthis->nVersion);
+        nVersion = pthis->nVersion;
+        READWRITE(sAddress);
+        READWRITE(sLabel);
+        READWRITE(amount);
+        READWRITE(sMessage);
+        READWRITE(sPaymentRequest);
+        READWRITE(sAuthenticatedMerchant);
+
+        if (fRead)
+        {
+            pthis->address = QString::fromStdString(sAddress);
+            pthis->label = QString::fromStdString(sLabel);
+            pthis->message = QString::fromStdString(sMessage);
+            if (!sPaymentRequest.empty())
+                pthis->paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size()));
+            pthis->authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
+        }
+    )
 };
 
 /** Interface to Bitcoin wallet from Qt view code. */
@@ -152,6 +187,9 @@ public:
     void unlockCoin(COutPoint& output);
     void listLockedCoins(std::vector<COutPoint>& vOutpts);
 
+    void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
+    bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
+
 private:
     CWallet *wallet;
 
-- 
cgit v1.2.3


From 4d901023b732efb492d89cebd8555c689ab7663e Mon Sep 17 00:00:00 2001
From: Cozz Lovan <cozzlovan@yahoo.com>
Date: Sat, 18 Jan 2014 00:01:14 +0100
Subject: [Qt] Add sorting feature to the requested payments table

---
 src/qt/forms/receivecoinsdialog.ui  |  6 +++++-
 src/qt/receivecoinsdialog.cpp       |  2 ++
 src/qt/recentrequeststablemodel.cpp | 28 ++++++++++++++++++++++++++++
 src/qt/recentrequeststablemodel.h   | 18 +++++++++++++++++-
 4 files changed, 52 insertions(+), 2 deletions(-)

(limited to 'src/qt')

diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 8242763e77..7bf01224ee 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -207,7 +207,11 @@
        </widget>
       </item>
       <item>
-       <widget class="QTableView" name="recentRequestsView"/>
+       <widget class="QTableView" name="recentRequestsView">
+        <property name="sortingEnabled">
+         <bool>true</bool>
+        </property>
+       </widget>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 075a16dabf..38dc88f63b 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -55,6 +55,8 @@ void ReceiveCoinsDialog::setModel(WalletModel *model)
         ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch);
 #endif
         ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100);
+
+        model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
     }
 }
 
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 70614f9eae..74b43f1d24 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -175,3 +175,31 @@ void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
     list.prepend(recipient);
     endInsertRows();
 }
+
+void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
+{
+    qSort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
+    emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
+}
+
+bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const
+{
+    RecentRequestEntry *pLeft = &left;
+    RecentRequestEntry *pRight = &right;
+    if (order == Qt::DescendingOrder)
+        std::swap(pLeft, pRight);
+
+    switch(column)
+    {
+    case RecentRequestsTableModel::Date:
+        return pLeft->date.toTime_t() < pRight->date.toTime_t();
+    case RecentRequestsTableModel::Label:
+        return pLeft->recipient.label < pRight->recipient.label;
+    case RecentRequestsTableModel::Message:
+        return pLeft->recipient.message < pRight->recipient.message;
+    case RecentRequestsTableModel::Amount:
+        return pLeft->recipient.amount < pRight->recipient.amount;
+    default:
+        return pLeft->id < pRight->id;
+    }
+}
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index f939c76666..6b20402f78 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -41,6 +41,18 @@ public:
     )
 };
 
+class RecentRequestEntryLessThan
+{
+public:
+    RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder):
+        column(nColumn), order(fOrder) {}
+    bool operator()(RecentRequestEntry &left, RecentRequestEntry &right ) const;
+
+private:
+    int column;
+    Qt::SortOrder order;
+};
+
 /** Model for list of recently generated payment requests / bitcoin URIs.
  * Part of wallet model.
  */
@@ -56,7 +68,8 @@ public:
         Date = 0,
         Label = 1,
         Message = 2,
-        Amount = 3
+        Amount = 3,
+        NUMBER_OF_COLUMNS
     };
 
     /** @name Methods overridden from QAbstractTableModel
@@ -76,6 +89,9 @@ public:
     void addNewRequest(const std::string &recipient);
     void addNewRequest(RecentRequestEntry &recipient);
 
+public slots:
+    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+
 private:
     WalletModel *walletModel;
     QStringList columns;
-- 
cgit v1.2.3