recentrequeststablemodel.cpp
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 }