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 }