recentrequeststablemodel.cpp raw

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