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"