recentrequeststablemodel.cpp raw

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