/ src / qt / recentrequeststablemodel.cpp
recentrequeststablemodel.cpp
  1  // Copyright (c) 2011-2022 The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <qt/recentrequeststablemodel.h>
  6  
  7  #include <qt/bitcoinunits.h>
  8  #include <qt/guiutil.h>
  9  #include <qt/optionsmodel.h>
 10  #include <qt/walletmodel.h>
 11  
 12  #include <clientversion.h>
 13  #include <interfaces/wallet.h>
 14  #include <key_io.h>
 15  #include <streams.h>
 16  #include <util/string.h>
 17  
 18  #include <utility>
 19  
 20  #include <QLatin1Char>
 21  #include <QLatin1String>
 22  
 23  RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
 24      QAbstractTableModel(parent), walletModel(parent)
 25  {
 26      // Load entries from wallet
 27      for (const std::string& request : parent->wallet().getAddressReceiveRequests()) {
 28          addNewRequest(request);
 29      }
 30  
 31      /* These columns must match the indices in the ColumnIndex enumeration */
 32      columns << tr("Date") << tr("Label") << tr("Message") << getAmountTitle();
 33  
 34      connect(walletModel->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &RecentRequestsTableModel::updateDisplayUnit);
 35  }
 36  
 37  RecentRequestsTableModel::~RecentRequestsTableModel() = default;
 38  
 39  int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const
 40  {
 41      if (parent.isValid()) {
 42          return 0;
 43      }
 44      return list.length();
 45  }
 46  
 47  int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const
 48  {
 49      if (parent.isValid()) {
 50          return 0;
 51      }
 52      return columns.length();
 53  }
 54  
 55  QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) const
 56  {
 57      if(!index.isValid() || index.row() >= list.length())
 58          return QVariant();
 59  
 60      if(role == Qt::DisplayRole || role == Qt::EditRole)
 61      {
 62          const RecentRequestEntry *rec = &list[index.row()];
 63          switch(index.column())
 64          {
 65          case Date:
 66              return GUIUtil::dateTimeStr(rec->date);
 67          case Label:
 68              if(rec->recipient.label.isEmpty() && role == Qt::DisplayRole)
 69              {
 70                  return tr("(no label)");
 71              }
 72              else
 73              {
 74                  return rec->recipient.label;
 75              }
 76          case Message:
 77              if(rec->recipient.message.isEmpty() && role == Qt::DisplayRole)
 78              {
 79                  return tr("(no message)");
 80              }
 81              else
 82              {
 83                  return rec->recipient.message;
 84              }
 85          case Amount:
 86              if (rec->recipient.amount == 0 && role == Qt::DisplayRole)
 87                  return tr("(no amount requested)");
 88              else if (role == Qt::EditRole)
 89                  return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::SeparatorStyle::NEVER);
 90              else
 91                  return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount);
 92          }
 93      }
 94      else if (role == Qt::TextAlignmentRole)
 95      {
 96          if (index.column() == Amount)
 97              return (int)(Qt::AlignRight|Qt::AlignVCenter);
 98      }
 99      return QVariant();
100  }
101  
102  bool RecentRequestsTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
103  {
104      return true;
105  }
106  
107  QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orientation, int role) const
108  {
109      if(orientation == Qt::Horizontal)
110      {
111          if(role == Qt::DisplayRole && section < columns.size())
112          {
113              return columns[section];
114          }
115      }
116      return QVariant();
117  }
118  
119  /** Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table headers to react. */
120  void RecentRequestsTableModel::updateAmountColumnTitle()
121  {
122      columns[Amount] = getAmountTitle();
123      Q_EMIT headerDataChanged(Qt::Horizontal,Amount,Amount);
124  }
125  
126  /** Gets title for amount column including current display unit if optionsModel reference available. */
127  QString RecentRequestsTableModel::getAmountTitle()
128  {
129      if (!walletModel->getOptionsModel()) return {};
130      return tr("Requested") +
131             QLatin1String(" (") +
132             BitcoinUnits::shortName(this->walletModel->getOptionsModel()->getDisplayUnit()) +
133             QLatin1Char(')');
134  }
135  
136  QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const
137  {
138      Q_UNUSED(parent);
139  
140      return createIndex(row, column);
141  }
142  
143  bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex &parent)
144  {
145      Q_UNUSED(parent);
146  
147      if(count > 0 && row >= 0 && (row+count) <= list.size())
148      {
149          for (int i = 0; i < count; ++i)
150          {
151              const RecentRequestEntry* rec = &list[row+i];
152              if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(rec->recipient.address.toStdString()), ToString(rec->id), ""))
153                  return false;
154          }
155  
156          beginRemoveRows(parent, row, row + count - 1);
157          list.erase(list.begin() + row, list.begin() + row + count);
158          endRemoveRows();
159          return true;
160      } else {
161          return false;
162      }
163  }
164  
165  Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const
166  {
167      return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
168  }
169  
170  // called when adding a request from the GUI
171  void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient)
172  {
173      RecentRequestEntry newEntry;
174      newEntry.id = ++nReceiveRequestsMaxId;
175      newEntry.date = QDateTime::currentDateTime();
176      newEntry.recipient = recipient;
177  
178      DataStream ss{};
179      ss << newEntry;
180  
181      if (!walletModel->wallet().setAddressReceiveRequest(DecodeDestination(recipient.address.toStdString()), ToString(newEntry.id), ss.str()))
182          return;
183  
184      addNewRequest(newEntry);
185  }
186  
187  // called from ctor when loading from wallet
188  void RecentRequestsTableModel::addNewRequest(const std::string &recipient)
189  {
190      std::vector<uint8_t> data(recipient.begin(), recipient.end());
191      DataStream ss{data};
192  
193      RecentRequestEntry entry;
194      ss >> entry;
195  
196      if (entry.id == 0) // should not happen
197          return;
198  
199      if (entry.id > nReceiveRequestsMaxId)
200          nReceiveRequestsMaxId = entry.id;
201  
202      addNewRequest(entry);
203  }
204  
205  // actually add to table in GUI
206  void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
207  {
208      beginInsertRows(QModelIndex(), 0, 0);
209      list.prepend(recipient);
210      endInsertRows();
211  }
212  
213  void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
214  {
215      std::sort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
216      Q_EMIT dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
217  }
218  
219  void RecentRequestsTableModel::updateDisplayUnit()
220  {
221      updateAmountColumnTitle();
222  }
223  
224  bool RecentRequestEntryLessThan::operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const
225  {
226      const RecentRequestEntry* pLeft = &left;
227      const RecentRequestEntry* pRight = &right;
228      if (order == Qt::DescendingOrder)
229          std::swap(pLeft, pRight);
230  
231      switch(column)
232      {
233      case RecentRequestsTableModel::Date:
234          return pLeft->date.toSecsSinceEpoch() < pRight->date.toSecsSinceEpoch();
235      case RecentRequestsTableModel::Label:
236          return pLeft->recipient.label < pRight->recipient.label;
237      case RecentRequestsTableModel::Message:
238          return pLeft->recipient.message < pRight->recipient.message;
239      case RecentRequestsTableModel::Amount:
240          return pLeft->recipient.amount < pRight->recipient.amount;
241      default:
242          return pLeft->id < pRight->id;
243      }
244  }