File indexing completed on 2024-05-05 04:59:13
0001 /*************************************************************************** 0002 * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * 0003 * joris.guisson@gmail.com * 0004 * ivasic@gmail.com * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, write to the * 0018 * Free Software Foundation, Inc., * 0019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0020 ***************************************************************************/ 0021 #include "peerviewmodel.h" 0022 0023 #include <KLocalizedString> 0024 0025 #include <QLocale> 0026 0027 #include <QDebug> 0028 #include <interfaces/torrentinterface.h> 0029 #include <util/functions.h> 0030 0031 using namespace bt; 0032 0033 namespace kt 0034 { 0035 static QIcon yes, no; 0036 static bool icons_loaded = false; 0037 0038 PeerViewModel::Item::Item(bt::PeerInterface *peer) 0039 : peer(peer) 0040 { 0041 stats = peer->getStats(); 0042 if (!icons_loaded) { 0043 yes = QIcon::fromTheme("dialog-ok"); 0044 no = QIcon::fromTheme("dialog-cancel"); 0045 } 0046 } 0047 /* 0048 bool PeerViewModel::Item::changed() const 0049 { 0050 const PeerInterface::Stats & s = peer->getStats(); 0051 0052 0053 if (s.download_rate != stats.download_rate || 0054 s.upload_rate != stats.upload_rate || 0055 s.choked != stats.choked || 0056 s.snubbed != stats.snubbed || 0057 s.perc_of_file != stats.perc_of_file || 0058 s.aca_score != stats.aca_score || 0059 s.has_upload_slot != stats.has_upload_slot || 0060 s.num_down_requests != stats.num_down_requests || 0061 s.num_up_requests != stats.num_up_requests || 0062 s.bytes_downloaded != stats.bytes_downloaded || 0063 s.bytes_uploaded != stats.bytes_uploaded || 0064 s.interested != stats.interested || 0065 s.am_interested != stats.am_interested) 0066 { 0067 stats = s; 0068 return true; 0069 } 0070 return false; 0071 } 0072 */ 0073 bool PeerViewModel::Item::changed(int col, bool &modified) const 0074 { 0075 const PeerInterface::Stats &s = peer->getStats(); 0076 bool ret = false; 0077 0078 switch (col) { 0079 case 3: 0080 ret = s.download_rate != stats.download_rate; 0081 break; 0082 case 4: 0083 ret = s.upload_rate != stats.upload_rate; 0084 break; 0085 case 5: 0086 ret = s.choked != stats.choked; 0087 break; 0088 case 6: 0089 ret = s.snubbed != stats.snubbed; 0090 break; 0091 case 7: 0092 ret = s.perc_of_file != stats.perc_of_file; 0093 break; 0094 case 9: 0095 ret = s.aca_score != stats.aca_score; 0096 break; 0097 case 10: 0098 ret = s.has_upload_slot != stats.has_upload_slot; 0099 break; 0100 case 11: 0101 ret = (s.num_down_requests != stats.num_down_requests || s.num_up_requests != stats.num_up_requests); 0102 break; 0103 case 12: 0104 ret = s.bytes_downloaded != stats.bytes_downloaded; 0105 break; 0106 case 13: 0107 ret = s.bytes_uploaded != stats.bytes_uploaded; 0108 break; 0109 case 14: 0110 ret = s.interested != stats.interested; 0111 break; 0112 case 15: 0113 ret = s.am_interested != stats.am_interested; 0114 break; 0115 default: 0116 ret = false; 0117 break; 0118 } 0119 0120 modified = s.download_rate != stats.download_rate || s.upload_rate != stats.upload_rate || s.choked != stats.choked || s.snubbed != stats.snubbed 0121 || s.perc_of_file != stats.perc_of_file || s.aca_score != stats.aca_score || s.has_upload_slot != stats.has_upload_slot 0122 || s.num_down_requests != stats.num_down_requests || s.num_up_requests != stats.num_up_requests || s.bytes_downloaded != stats.bytes_downloaded 0123 || s.bytes_uploaded != stats.bytes_uploaded || s.interested != stats.interested || s.am_interested != stats.am_interested; 0124 stats = s; 0125 return ret; 0126 } 0127 0128 QVariant PeerViewModel::Item::data(int col) const 0129 { 0130 switch (col) { 0131 case 0: 0132 return stats.ip_address; 0133 case 1: 0134 return stats.client; 0135 case 2: 0136 if (stats.download_rate >= 103) 0137 return BytesPerSecToString(stats.download_rate); 0138 else 0139 return QVariant(); 0140 case 3: 0141 if (stats.upload_rate >= 103) 0142 return BytesPerSecToString(stats.upload_rate); 0143 else 0144 return QVariant(); 0145 case 4: 0146 return stats.choked ? i18nc("Choked", "Yes") : i18nc("Not choked", "No"); 0147 case 5: 0148 return stats.snubbed ? i18nc("Snubbed", "Yes") : i18nc("Not snubbed", "No"); 0149 case 6: 0150 return QString("%1 %").arg(QLocale().toString(stats.perc_of_file, 'g', 2)); 0151 case 7: 0152 return QVariant(); 0153 case 8: 0154 return QLocale().toString(stats.aca_score, 'g', 2); 0155 case 9: 0156 return QVariant(); 0157 case 10: 0158 return QString("%1 / %2").arg(stats.num_down_requests).arg(stats.num_up_requests); 0159 case 11: 0160 return BytesToString(stats.bytes_downloaded); 0161 case 12: 0162 return BytesToString(stats.bytes_uploaded); 0163 case 13: 0164 return stats.interested ? i18nc("Interested", "Yes") : i18nc("Not Interested", "No"); 0165 case 14: 0166 return stats.am_interested ? i18nc("Interesting", "Yes") : i18nc("Not Interesting", "No"); 0167 default: 0168 return QVariant(); 0169 } 0170 return QVariant(); 0171 } 0172 0173 bool PeerViewModel::Item::lessThan(int col, const Item *other) const 0174 { 0175 switch (col) { 0176 case 0: 0177 return stats.ip_address < other->stats.ip_address; 0178 case 1: 0179 return QString::localeAwareCompare(stats.client, other->stats.client) < 0; 0180 case 2: 0181 return stats.download_rate < other->stats.download_rate; 0182 case 3: 0183 return stats.upload_rate < other->stats.upload_rate; 0184 case 4: 0185 return stats.choked < other->stats.choked; 0186 case 5: 0187 return stats.snubbed < other->stats.snubbed; 0188 case 6: 0189 return stats.perc_of_file < other->stats.perc_of_file; 0190 case 7: 0191 return stats.dht_support < other->stats.dht_support; 0192 case 8: 0193 return stats.aca_score < other->stats.aca_score; 0194 case 9: 0195 return stats.has_upload_slot < other->stats.has_upload_slot; 0196 case 10: 0197 return stats.num_down_requests + stats.num_up_requests < other->stats.num_down_requests + other->stats.num_up_requests; 0198 case 11: 0199 return stats.bytes_downloaded < other->stats.bytes_downloaded; 0200 case 12: 0201 return stats.bytes_uploaded < other->stats.bytes_uploaded; 0202 case 13: 0203 return stats.interested < other->stats.interested; 0204 case 14: 0205 return stats.am_interested < other->stats.am_interested; 0206 default: 0207 return false; 0208 } 0209 return false; 0210 } 0211 0212 QVariant PeerViewModel::Item::decoration(int col) const 0213 { 0214 switch (col) { 0215 case 0: 0216 if (stats.encrypted) 0217 return QIcon::fromTheme("kt-encrypted"); 0218 break; 0219 case 1: 0220 return flag; 0221 case 8: 0222 return stats.dht_support ? yes : no; 0223 case 10: 0224 return stats.has_upload_slot ? yes : QIcon(); 0225 } 0226 0227 return QVariant(); 0228 } 0229 0230 ///////////////////////////////////////////////////////////// 0231 0232 PeerViewModel::PeerViewModel(QObject *parent) 0233 : QAbstractTableModel(parent) 0234 { 0235 sort_column = 0; 0236 sort_order = Qt::AscendingOrder; 0237 } 0238 0239 PeerViewModel::~PeerViewModel() 0240 { 0241 qDeleteAll(items); 0242 } 0243 0244 void PeerViewModel::peerAdded(bt::PeerInterface *peer) 0245 { 0246 items.append(new Item(peer)); 0247 insertRow(items.count() - 1); 0248 sort(sort_column, sort_order); 0249 } 0250 0251 void PeerViewModel::peerRemoved(bt::PeerInterface *peer) 0252 { 0253 int idx = 0; 0254 for (QList<Item *>::iterator i = items.begin(); i != items.end(); i++) { 0255 Item *item = *i; 0256 if (item->peer == peer) { 0257 items.erase(i); 0258 delete item; 0259 removeRow(idx); 0260 break; 0261 } 0262 idx++; 0263 } 0264 } 0265 0266 void PeerViewModel::clear() 0267 { 0268 beginResetModel(); 0269 qDeleteAll(items); 0270 items.clear(); 0271 endResetModel(); 0272 } 0273 0274 void PeerViewModel::update() 0275 { 0276 bool resort = false; 0277 Uint32 idx = 0; 0278 foreach (Item *i, items) { 0279 bool modified = false; 0280 if (i->changed(sort_column, modified)) 0281 resort = true; 0282 0283 if (modified && !resort) 0284 Q_EMIT dataChanged(index(idx, 3), index(idx, 15)); 0285 idx++; 0286 } 0287 0288 if (resort) 0289 sort(sort_column, sort_order); 0290 } 0291 0292 QModelIndex PeerViewModel::index(int row, int column, const QModelIndex &parent) const 0293 { 0294 if (!hasIndex(row, column, parent) || parent.isValid()) 0295 return QModelIndex(); 0296 else 0297 return createIndex(row, column, items[row]); 0298 } 0299 0300 int PeerViewModel::rowCount(const QModelIndex &parent) const 0301 { 0302 if (parent.isValid()) 0303 return 0; 0304 else 0305 return items.count(); 0306 } 0307 0308 int PeerViewModel::columnCount(const QModelIndex &parent) const 0309 { 0310 if (parent.isValid()) 0311 return 0; 0312 else 0313 return 16; 0314 } 0315 0316 QVariant PeerViewModel::headerData(int section, Qt::Orientation orientation, int role) const 0317 { 0318 if (orientation != Qt::Horizontal) 0319 return QVariant(); 0320 0321 if (role == Qt::DisplayRole) { 0322 switch (section) { 0323 case 0: 0324 return i18n("IP Address"); 0325 case 1: 0326 return i18n("Client"); 0327 case 2: 0328 return i18n("Down Speed"); 0329 case 3: 0330 return i18n("Up Speed"); 0331 case 4: 0332 return i18n("Choked"); 0333 case 5: 0334 return i18n("Snubbed"); 0335 case 6: 0336 return i18n("Availability"); 0337 case 7: 0338 return i18n("DHT"); 0339 case 8: 0340 return i18n("Score"); 0341 case 9: 0342 return i18n("Upload Slot"); 0343 case 10: 0344 return i18n("Requests"); 0345 case 11: 0346 return i18n("Downloaded"); 0347 case 12: 0348 return i18n("Uploaded"); 0349 case 13: 0350 return i18n("Interested"); 0351 case 14: 0352 return i18n("Interesting"); 0353 default: 0354 return QVariant(); 0355 } 0356 } else if (role == Qt::ToolTipRole) { 0357 switch (section) { 0358 case 0: 0359 return i18n("IP address of the peer"); 0360 case 1: 0361 return i18n("Which client the peer is using"); 0362 case 2: 0363 return i18n("Download speed"); 0364 case 3: 0365 return i18n("Upload speed"); 0366 case 4: 0367 return i18n("Whether or not the peer has choked us. If we are choked, the peer will not send us any data."); 0368 case 5: 0369 return i18n("Snubbed means the peer has not sent us any data in the last 2 minutes"); 0370 case 6: 0371 return i18n("How much of the torrent's data the peer has"); 0372 case 7: 0373 return i18n("Whether or not the peer has DHT enabled"); 0374 case 8: 0375 return i18n("The score of the peer. KTorrent uses this to determine who to upload to."); 0376 case 9: 0377 return i18n("Only peers which have an upload slot will get data from us"); 0378 case 10: 0379 return i18n("The number of download and upload requests"); 0380 case 11: 0381 return i18n("How much data we have downloaded from this peer"); 0382 case 12: 0383 return i18n("How much data we have uploaded to this peer"); 0384 case 13: 0385 return i18n("Whether the peer is interested in downloading data from us"); 0386 case 14: 0387 return i18n("Whether we are interested in downloading from this peer"); 0388 default: 0389 return QVariant(); 0390 } 0391 } 0392 0393 return QVariant(); 0394 } 0395 0396 QVariant PeerViewModel::data(const QModelIndex &index, int role) const 0397 { 0398 if (!index.isValid() || index.row() >= items.count() || index.row() < 0) 0399 return QVariant(); 0400 0401 Item *item = (Item *)index.internalPointer(); 0402 if (role == Qt::DisplayRole) 0403 return item->data(index.column()); 0404 else if (role == Qt::DecorationRole) 0405 return item->decoration(index.column()); 0406 0407 return QVariant(); 0408 } 0409 0410 bool PeerViewModel::removeRows(int row, int count, const QModelIndex & /*parent*/) 0411 { 0412 beginRemoveRows(QModelIndex(), row, row + count - 1); 0413 endRemoveRows(); 0414 return true; 0415 } 0416 0417 bool PeerViewModel::insertRows(int row, int count, const QModelIndex & /*parent*/) 0418 { 0419 beginInsertRows(QModelIndex(), row, row + count - 1); 0420 endInsertRows(); 0421 return true; 0422 } 0423 0424 bt::PeerInterface *PeerViewModel::indexToPeer(const QModelIndex &index) 0425 { 0426 if (!index.isValid() || index.row() >= items.count() || index.row() < 0) 0427 return nullptr; 0428 else 0429 return ((Item *)index.internalPointer())->peer; 0430 } 0431 0432 class PeerViewModelItemCmp 0433 { 0434 public: 0435 PeerViewModelItemCmp(int col, Qt::SortOrder order) 0436 : col(col) 0437 , order(order) 0438 { 0439 } 0440 0441 bool operator()(PeerViewModel::Item *a, PeerViewModel::Item *b) 0442 { 0443 if (order == Qt::AscendingOrder) 0444 return a->lessThan(col, b); 0445 else 0446 return !a->lessThan(col, b); 0447 } 0448 0449 int col; 0450 Qt::SortOrder order; 0451 }; 0452 0453 void PeerViewModel::sort(int col, Qt::SortOrder order) 0454 { 0455 sort_column = col; 0456 sort_order = order; 0457 Q_EMIT layoutAboutToBeChanged(); 0458 std::stable_sort(items.begin(), items.end(), PeerViewModelItemCmp(col, order)); 0459 Q_EMIT layoutChanged(); 0460 } 0461 } 0462 0463 #include "moc_peerviewmodel.cpp"