From ff1aaa87331b15ee5195cc401a0df4f90b5405b1 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Thu, 26 Mar 2015 03:39:45 +0200 Subject: [PATCH] Refactor side panel code. Encapsulate each widget's logic in their own subclass. --- src/core/preferences.cpp | 10 +- src/gui/transferlistfilterswidget.cpp | 791 +++++++++++++------------- src/gui/transferlistfilterswidget.h | 124 ++-- 3 files changed, 460 insertions(+), 465 deletions(-) diff --git a/src/core/preferences.cpp b/src/core/preferences.cpp index 077ab5ac9..1f0fb2599 100644 --- a/src/core/preferences.cpp +++ b/src/core/preferences.cpp @@ -1583,16 +1583,18 @@ void Preferences::setTorrentLabels(const QStringList& labels) void Preferences::addTorrentLabel(const QString& label) { QStringList labels = value("TransferListFilters/customLabels").toStringList(); - if (!labels.contains(label)) - labels << label; + if (labels.contains(label)) + return; + labels << label; setValue("TransferListFilters/customLabels", labels); } void Preferences::removeTorrentLabel(const QString& label) { QStringList labels = value("TransferListFilters/customLabels").toStringList(); - if (labels.contains(label)) - labels.removeOne(label); + if (!labels.contains(label)) + return; + labels.removeOne(label); setValue("TransferListFilters/customLabels", labels); } diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 729838716..0fc2e81a0 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -51,13 +51,21 @@ #include "downloadthread.h" #include "logger.h" -FiltersBase::FiltersBase(QWidget *parent) +FiltersBase::FiltersBase(QWidget *parent, TransferListWidget *transferList) : QListWidget(parent) + , transferList(transferList) { setStyleSheet("QListWidget { background: transparent; border: 0 }"); #if defined(Q_OS_MAC) setAttribute(Qt::WA_MacShowFocusRect, false); #endif + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showMenu(QPoint))); + connect(this, SIGNAL(currentRowChanged(int)), SLOT(applyFilter(int))); + connect(transferList->getSourceModel(), SIGNAL(torrentAdded(TorrentModelItem*)), SLOT(handleNewTorrent(TorrentModelItem*))); + connect(transferList->getSourceModel(), SIGNAL(torrentAboutToBeRemoved(TorrentModelItem*)), SLOT(torrentAboutToBeDeleted(TorrentModelItem*))); } QSize FiltersBase::sizeHint() const @@ -76,26 +84,298 @@ QSize FiltersBase::minimumSizeHint() const return size; } -LabelFiltersList::LabelFiltersList(QWidget *parent) - : FiltersBase(parent) +StatusFiltersWidget::StatusFiltersWidget(QWidget *parent, TransferListWidget *transferList) + : FiltersBase(parent, transferList) +{ + setUniformItemSizes(true); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // Height is fixed (sizeHint().height() is used) + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + setSpacing(0); + connect(transferList->getSourceModel(), SIGNAL(modelRefreshed()), SLOT(updateTorrentNumbers())); + + // Add status filters + QListWidgetItem *all = new QListWidgetItem(this); + all->setData(Qt::DisplayRole, QVariant(tr("All") + " (0)")); + all->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterall.png")); + QListWidgetItem *downloading = new QListWidgetItem(this); + downloading->setData(Qt::DisplayRole, QVariant(tr("Downloading") + " (0)")); + downloading->setData(Qt::DecorationRole, QIcon(":/icons/skin/downloading.png")); + QListWidgetItem *completed = new QListWidgetItem(this); + completed->setData(Qt::DisplayRole, QVariant(tr("Completed") + " (0)")); + completed->setData(Qt::DecorationRole, QIcon(":/icons/skin/uploading.png")); + QListWidgetItem *resumed = new QListWidgetItem(this); + resumed->setData(Qt::DisplayRole, QVariant(tr("Resumed") + " (0)")); + resumed->setData(Qt::DecorationRole, QIcon(":/icons/skin/resumed.png")); + QListWidgetItem *paused = new QListWidgetItem(this); + paused->setData(Qt::DisplayRole, QVariant(tr("Paused") + " (0)")); + paused->setData(Qt::DecorationRole, QIcon(":/icons/skin/paused.png")); + QListWidgetItem *active = new QListWidgetItem(this); + active->setData(Qt::DisplayRole, QVariant(tr("Active") + " (0)")); + active->setData(Qt::DecorationRole, QIcon(":/icons/skin/filteractive.png")); + QListWidgetItem *inactive = new QListWidgetItem(this); + inactive->setData(Qt::DisplayRole, QVariant(tr("Inactive") + " (0)")); + inactive->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterinactive.png")); + + setCurrentRow(Preferences::instance()->getTransSelFilter(), QItemSelectionModel::SelectCurrent); +} + +StatusFiltersWidget::~StatusFiltersWidget() +{ + Preferences::instance()->setTransSelFilter(currentRow()); +} + +void StatusFiltersWidget::updateTorrentNumbers() +{ + const TorrentStatusReport report = transferList->getSourceModel()->getTorrentStatusReport(); + item(TorrentFilter::ALL)->setData(Qt::DisplayRole, QVariant(tr("All (%1)").arg(report.nb_active + report.nb_inactive))); + item(TorrentFilter::DOWNLOADING)->setData(Qt::DisplayRole, QVariant(tr("Downloading (%1)").arg(report.nb_downloading))); + item(TorrentFilter::COMPLETED)->setData(Qt::DisplayRole, QVariant(tr("Completed (%1)").arg(report.nb_seeding))); + item(TorrentFilter::PAUSED)->setData(Qt::DisplayRole, QVariant(tr("Paused (%1)").arg(report.nb_paused))); + item(TorrentFilter::RESUMED)->setData(Qt::DisplayRole, QVariant(tr("Resumed (%1)").arg(report.nb_active + report.nb_inactive - report.nb_paused))); + item(TorrentFilter::ACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Active (%1)").arg(report.nb_active))); + item(TorrentFilter::INACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Inactive (%1)").arg(report.nb_inactive))); +} + +void StatusFiltersWidget::showMenu(QPoint) {} + +void StatusFiltersWidget::applyFilter(int row) +{ + transferList->applyStatusFilter(row); +} + +void StatusFiltersWidget::handleNewTorrent(TorrentModelItem*) {} + +void StatusFiltersWidget::torrentAboutToBeDeleted(TorrentModelItem*) {} + +LabelFiltersList::LabelFiltersList(QWidget *parent, TransferListWidget *transferList) + : FiltersBase(parent, transferList) + , m_totalTorrents(0) + , m_totalLabeled(0) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + connect(transferList->getSourceModel(), SIGNAL(torrentChangedLabel(TorrentModelItem*,QString,QString)), SLOT(torrentChangedLabel(TorrentModelItem*, QString, QString))); + + // Add Label filters + QListWidgetItem *allLabels = new QListWidgetItem(this); + allLabels->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); + allLabels->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + QListWidgetItem *noLabel = new QListWidgetItem(this); + noLabel->setData(Qt::DisplayRole, QVariant(tr("Unlabeled (0)"))); + noLabel->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + + QStringList labelList = Preferences::instance()->getTorrentLabels(); + for (int i=0; i < labelList.size(); ++i) + addItem(labelList[i], false); + + setCurrentRow(0, QItemSelectionModel::SelectCurrent); +} + +LabelFiltersList::~LabelFiltersList() +{ + Preferences::instance()->setTorrentLabels(m_labels.keys()); } -void LabelFiltersList::addItem(QListWidgetItem *it) +void LabelFiltersList::addItem(QString &label, bool hasTorrent) { + int labelCount = 0; + QListWidgetItem *labelItem = 0; + label = fsutils::toValidFileSystemName(label.trimmed()); + item(0)->setText(tr("All (%1)", "this is for the label filter").arg(m_totalTorrents)); + + if (label.isEmpty()) { + item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + return; + } + + bool exists = m_labels.contains(label); + if (exists) { + labelCount = m_labels.value(label); + labelItem = item(rowFromLabel(label)); + } + else { + labelItem = new QListWidgetItem(); + labelItem->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + } + + if (hasTorrent) { + ++m_totalLabeled; + ++labelCount; + } + item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + + Preferences::instance()->addTorrentLabel(label); + m_labels.insert(label, labelCount); + labelItem->setText(tr("%1 (%2)", "label_name (10)").arg(label).arg(labelCount)); + if (exists) + return; + Q_ASSERT(count() >= 2); for (int i = 2; itext().localeAwareCompare(it->text()) >= 0) { - insertItem(i, it); + bool less = false; + if (!(misc::naturalSort(label, item(i)->text(), less))) + less = (label.localeAwareCompare(item(i)->text()) < 0); + if (less) { + insertItem(i, labelItem); updateGeometry(); return; } } - QListWidget::addItem(it); + QListWidget::addItem(labelItem); + updateGeometry(); +} + +void LabelFiltersList::removeItem(const QString &label) +{ + item(0)->setText(tr("All (%1)", "this is for the label filter").arg(m_totalTorrents)); + if (label.isEmpty()) { + // In case we here from torrentAboutToBeDeleted() + item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + return; + } + + --m_totalLabeled; + item(1)->setText(tr("Unlabeled (%1)").arg(m_totalTorrents - m_totalLabeled)); + + int labelCount = m_labels.value(label) - 1; + int row = rowFromLabel(label); + if (row < 2) + return; + + QListWidgetItem *labelItem = item(row); + labelItem->setText(tr("%1 (%2)", "label_name (10)").arg(label).arg(labelCount)); + m_labels.insert(label, labelCount); +} + +void LabelFiltersList::removeSelectedLabel() +{ + const int labelRow = row(selectedItems().first()); + if (labelRow < 2) + return; + const QString &label = labelFromRow(labelRow); + Q_ASSERT(m_labels.contains(label)); + m_labels.remove(label); + // Select first label + setCurrentRow(0, QItemSelectionModel::SelectCurrent); + applyFilter(0); + // Un display filter + delete takeItem(labelRow); + transferList->removeLabelFromRows(label); + // Save custom labels to remember it was deleted + Preferences::instance()->removeTorrentLabel(label); updateGeometry(); } +void LabelFiltersList::removeUnusedLabels() +{ + QStringList unusedLabels; + QHash::const_iterator i; + for (i = m_labels.begin(); i != m_labels.end(); ++i) { + if (i.value() == 0) + unusedLabels << i.key(); + } + foreach (const QString &label, unusedLabels) { + m_labels.remove(label); + delete takeItem(rowFromLabel(label)); + Preferences::instance()->removeTorrentLabel(label); + } + + if (!unusedLabels.isEmpty()) + updateGeometry(); +} + +void LabelFiltersList::torrentChangedLabel(TorrentModelItem *torrentItem, QString old_label, QString new_label) +{ + Q_UNUSED(torrentItem); + qDebug("Torrent label changed from %s to %s", qPrintable(old_label), qPrintable(new_label)); + removeItem(old_label); + addItem(new_label, true); +} + +void LabelFiltersList::showMenu(QPoint) +{ + QMenu menu(this); + QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add label...")); + QAction *removeAct = 0; + QAction *removeUnusedAct = 0; + if (!selectedItems().empty() && row(selectedItems().first()) > 1) + removeAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove label")); + removeUnusedAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove unused labels")); + menu.addSeparator(); + QAction *startAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); + QAction *pauseAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); + QAction *deleteTorrentsAct = menu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); + QAction *act = 0; + act = menu.exec(QCursor::pos()); + if (!act) + return; + + if (act == removeAct) { + removeSelectedLabel(); + } + else if (act == removeUnusedAct) { + removeUnusedLabels(); + } + else if (act == deleteTorrentsAct) { + transferList->deleteVisibleTorrents(); + } + else if (act == startAct) { + transferList->startVisibleTorrents(); + } + else if (act == pauseAct) { + transferList->pauseVisibleTorrents(); + } + else if (act == addAct) { + bool ok; + QString label = ""; + bool invalid; + do { + invalid = false; + label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, label, &ok); + if (ok && !label.isEmpty()) { + if (fsutils::isValidFileSystemName(label)) { + addItem(label, false); + } + else { + QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); + invalid = true; + } + } + } while (invalid); + } +} + +void LabelFiltersList::applyFilter(int row) +{ + switch (row) { + case 0: + transferList->applyLabelFilterAll(); + break; + case 1: + transferList->applyLabelFilter(QString()); + break; + default: + transferList->applyLabelFilter(labelFromRow(row)); + } +} + +void LabelFiltersList::handleNewTorrent(TorrentModelItem* torrentItem) +{ + ++m_totalTorrents; + QString label = torrentItem->data(TorrentModelItem::TR_LABEL).toString(); + addItem(label, true); + // labelFilters->addItem() may have changed the label, update the model accordingly. + torrentItem->setData(TorrentModelItem::TR_LABEL, label); +} + +void LabelFiltersList::torrentAboutToBeDeleted(TorrentModelItem* torrentItem) +{ + --m_totalTorrents; + Q_ASSERT(torrentItem); + QString label = torrentItem->data(TorrentModelItem::TR_LABEL).toString(); + removeItem(label); +} + QString LabelFiltersList::labelFromRow(int row) const { Q_ASSERT(row > 1); @@ -106,7 +386,7 @@ QString LabelFiltersList::labelFromRow(int row) const return parts.join(" "); } -int LabelFiltersList::rowFromLabel(QString label) const +int LabelFiltersList::rowFromLabel(const QString &label) const { Q_ASSERT(!label.isEmpty()); for (int i = 2; isetData(Qt::DisplayRole, QVariant(tr("All trackers (0)"))); + allTrackers->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); allTrackers->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("network-server")); QListWidgetItem *noTracker = new QListWidgetItem(this); noTracker->setData(Qt::DisplayRole, QVariant(tr("Trackerless (0)"))); noTracker->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("network-server")); m_trackers.insert("", QStringList()); + + setCurrentRow(0, QItemSelectionModel::SelectCurrent); connect(m_downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(handleFavicoDownload(QString, QString))); connect(m_downloader, SIGNAL(downloadFailure(QString, QString)), SLOT(handleFavicoFailure(QString, QString))); } @@ -151,8 +426,9 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash) QStringList tmp; QListWidgetItem *trackerItem = 0; QString host = getHost(tracker); + bool exists = m_trackers.contains(host); - if (m_trackers.contains(host)) { + if (exists) { tmp = m_trackers.value(host); if (tmp.contains(hash)) return; @@ -178,6 +454,11 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash) } trackerItem->setText(tr("%1 (%2)", "openbittorrent.com (10)").arg(host).arg(tmp.size())); + if (exists) { + if (currentRow() == rowFromTracker(host)) + emit currentRowChanged(currentRow()); + return; + } Q_ASSERT(count() >= 2); for (int i = 2; itext()) < 0); if (less) { insertItem(i, trackerItem); - if (currentRow() == i) - emit currentRowChanged(i); + updateGeometry(); return; } } QListWidget::addItem(trackerItem); -} - -void TrackerFiltersList::addItem(const QTorrentHandle& handle) -{ - QString hash = handle.hash(); - std::vector trackers = handle.trackers(); - for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) - addItem(misc::toQStringU(i->url), hash); - - //Check for trackerless torrent - if (trackers.size() == 0) - addItem("", hash); + updateGeometry(); } void TrackerFiltersList::removeItem(const QString &tracker, const QString &hash) @@ -222,9 +491,10 @@ void TrackerFiltersList::removeItem(const QString &tracker, const QString &hash) trackerItem = item(row); if (tmp.empty()) { if (currentRow() == row) - setCurrentRow(0); + setCurrentRow(0, QItemSelectionModel::SelectCurrent); delete trackerItem; m_trackers.remove(host); + updateGeometry(); return; } trackerItem->setText(tr("%1 (%2)", "openbittorrent.com (10)").arg(host).arg(tmp.size())); @@ -240,29 +510,12 @@ void TrackerFiltersList::removeItem(const QString &tracker, const QString &hash) emit currentRowChanged(row); } -void TrackerFiltersList::removeItem(const QTorrentHandle& handle) -{ - QString hash = handle.hash(); - std::vector trackers = handle.trackers(); - for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) - removeItem(misc::toQStringU(i->url), hash); - - //Check for trackerless torrent - if (trackers.size() == 0) - removeItem("", hash); -} - -void TrackerFiltersList::setTorrentCount(int all) -{ - item(0)->setText(tr("All trackers (%1)").arg(all)); -} - -QStringList TrackerFiltersList::getHashes(int row) +void TrackerFiltersList::changeTrackerless(bool trackerless, const QString &hash) { - if (row == 1) - return m_trackers.value(""); + if (trackerless) + addItem("", hash); else - return m_trackers.value(trackerFromRow(row)); + removeItem("", hash); } void TrackerFiltersList::handleFavicoDownload(const QString& url, const QString& filePath) @@ -300,12 +553,62 @@ void TrackerFiltersList::handleFavicoFailure(const QString& url, const QString& Log::WARNING); } -void TrackerFiltersList::changeTrackerless(bool trackerless, const QString &hash) +void TrackerFiltersList::showMenu(QPoint) { - if (trackerless) - addItem("", hash); + QMenu menu(this); + QAction *startAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); + QAction *pauseAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); + QAction *deleteTorrentsAct = menu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); + QAction *act = 0; + act = menu.exec(QCursor::pos()); + + if (!act) + return; + + if (act == startAct) + transferList->startVisibleTorrents(); + else if (act == pauseAct) + transferList->pauseVisibleTorrents(); + else if (act == deleteTorrentsAct) + transferList->deleteVisibleTorrents(); +} + +void TrackerFiltersList::applyFilter(int row) +{ + if (row == 0) + transferList->applyTrackerFilterAll(); else + transferList->applyTrackerFilter(getHashes(row)); +} + +void TrackerFiltersList::handleNewTorrent(TorrentModelItem* torrentItem) +{ + QTorrentHandle handle = torrentItem->torrentHandle(); + QString hash = handle.hash(); + std::vector trackers = handle.trackers(); + for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) + addItem(misc::toQStringU(i->url), hash); + + //Check for trackerless torrent + if (trackers.size() == 0) + addItem("", hash); + + item(0)->setText(tr("All (%1)", "this is for the tracker filter").arg(++m_totalTorrents)); +} + +void TrackerFiltersList::torrentAboutToBeDeleted(TorrentModelItem* torrentItem) +{ + QTorrentHandle handle = torrentItem->torrentHandle(); + QString hash = handle.hash(); + std::vector trackers = handle.trackers(); + for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) + removeItem(misc::toQStringU(i->url), hash); + + //Check for trackerless torrent + if (trackers.size() == 0) removeItem("", hash); + + item(0)->setText(tr("All (%1)", "this is for the tracker filter").arg(--m_totalTorrents)); } QString TrackerFiltersList::trackerFromRow(int row) const @@ -338,102 +641,47 @@ QString TrackerFiltersList::getHost(const QString &trakcer) const return longHost.mid(index + 1); } -TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferListWidget *transferList): QFrame(parent), transferList(transferList), nb_labeled(0), nb_torrents(0) +QStringList TrackerFiltersList::getHashes(int row) +{ + if (row == 1) + return m_trackers.value(""); + else + return m_trackers.value(trackerFromRow(row)); +} + +TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferListWidget *transferList) + : QFrame(parent) + , statusFilters(0) + , trackerFilters(0) + , torrentsLabel(0) { // Construct lists - vLayout = new QVBoxLayout(); + QVBoxLayout *vLayout = new QVBoxLayout(this); vLayout->setContentsMargins(0, 4, 0, 0); QFont font; font.setBold(true); font.setCapitalization(QFont::SmallCaps); - torrentsLabel = new QLabel(tr("Torrents")); + torrentsLabel = new QLabel(tr("Torrents"), this); torrentsLabel->setIndent(2); torrentsLabel->setFont(font); vLayout->addWidget(torrentsLabel); - statusFilters = new StatusFiltersWidget(this); + statusFilters = new StatusFiltersWidget(this, transferList); vLayout->addWidget(statusFilters); - QLabel *labelsLabel = new QLabel(tr("Labels")); + QLabel *labelsLabel = new QLabel(tr("Labels"), this); labelsLabel->setIndent(2); labelsLabel->setFont(font); vLayout->addWidget(labelsLabel); - labelFilters = new LabelFiltersList(this); + LabelFiltersList *labelFilters = new LabelFiltersList(this, transferList); vLayout->addWidget(labelFilters); - QLabel *trackersLabel = new QLabel(tr("Trackers")); + QLabel *trackersLabel = new QLabel(tr("Trackers"), this); trackersLabel->setIndent(2); trackersLabel->setFont(font); vLayout->addWidget(trackersLabel); - trackerFilters = new TrackerFiltersList(this); + trackerFilters = new TrackerFiltersList(this, transferList); vLayout->addWidget(trackerFilters); setLayout(vLayout); - labelFilters->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - trackerFilters->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - statusFilters->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - statusFilters->setSpacing(0); setContentsMargins(0,0,0,0); vLayout->setSpacing(2); - // Add status filters - QListWidgetItem *all = new QListWidgetItem(statusFilters); - all->setData(Qt::DisplayRole, QVariant(tr("All") + " (0)")); - all->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterall.png")); - QListWidgetItem *downloading = new QListWidgetItem(statusFilters); - downloading->setData(Qt::DisplayRole, QVariant(tr("Downloading") + " (0)")); - downloading->setData(Qt::DecorationRole, QIcon(":/icons/skin/downloading.png")); - QListWidgetItem *completed = new QListWidgetItem(statusFilters); - completed->setData(Qt::DisplayRole, QVariant(tr("Completed") + " (0)")); - completed->setData(Qt::DecorationRole, QIcon(":/icons/skin/uploading.png")); - QListWidgetItem *resumed = new QListWidgetItem(statusFilters); - resumed->setData(Qt::DisplayRole, QVariant(tr("Resumed") + " (0)")); - resumed->setData(Qt::DecorationRole, QIcon(":/icons/skin/resumed.png")); - QListWidgetItem *paused = new QListWidgetItem(statusFilters); - paused->setData(Qt::DisplayRole, QVariant(tr("Paused") + " (0)")); - paused->setData(Qt::DecorationRole, QIcon(":/icons/skin/paused.png")); - QListWidgetItem *active = new QListWidgetItem(statusFilters); - active->setData(Qt::DisplayRole, QVariant(tr("Active") + " (0)")); - active->setData(Qt::DecorationRole, QIcon(":/icons/skin/filteractive.png")); - QListWidgetItem *inactive = new QListWidgetItem(statusFilters); - inactive->setData(Qt::DisplayRole, QVariant(tr("Inactive") + " (0)")); - inactive->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterinactive.png")); - - // SIGNAL/SLOT - connect(statusFilters, SIGNAL(currentRowChanged(int)), transferList, SLOT(applyStatusFilter(int))); - connect(transferList->getSourceModel(), SIGNAL(modelRefreshed()), SLOT(updateTorrentNumbers())); - connect(transferList->getSourceModel(), SIGNAL(torrentAdded(TorrentModelItem*)), SLOT(handleNewTorrent(TorrentModelItem*))); - connect(labelFilters, SIGNAL(currentRowChanged(int)), this, SLOT(applyLabelFilter(int))); - connect(trackerFilters, SIGNAL(currentRowChanged(int)), this, SLOT(applyTrackerFilter(int))); - connect(transferList->getSourceModel(), SIGNAL(torrentAboutToBeRemoved(TorrentModelItem*)), SLOT(torrentAboutToBeDeleted(TorrentModelItem*))); - connect(transferList->getSourceModel(), SIGNAL(torrentChangedLabel(TorrentModelItem*,QString,QString)), SLOT(torrentChangedLabel(TorrentModelItem*, QString, QString))); - - // Add Label filters - QListWidgetItem *allLabels = new QListWidgetItem(labelFilters); - allLabels->setData(Qt::DisplayRole, QVariant(tr("All labels") + " (0)")); - allLabels->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); - QListWidgetItem *noLabel = new QListWidgetItem(labelFilters); - noLabel->setData(Qt::DisplayRole, QVariant(tr("Unlabeled") + " (0)")); - noLabel->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); - - // Load settings - loadSettings(); - - labelFilters->setCurrentRow(0); - trackerFilters->setCurrentRow(0); - //labelFilters->selectionModel()->select(labelFilters->model()->index(0,0), QItemSelectionModel::Select); - - // Label menu - labelFilters->setContextMenuPolicy(Qt::CustomContextMenu); - connect(labelFilters, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showLabelMenu(QPoint))); - - // Tracker menu - trackerFilters->setContextMenuPolicy(Qt::CustomContextMenu); - connect(trackerFilters, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTrackerMenu(QPoint))); -} - -TransferListFiltersWidget::~TransferListFiltersWidget() -{ - saveSettings(); - delete statusFilters; - delete labelFilters; - delete trackerFilters; - delete vLayout; } void TransferListFiltersWidget::resizeEvent(QResizeEvent *event) @@ -443,56 +691,6 @@ void TransferListFiltersWidget::resizeEvent(QResizeEvent *event) trackerFilters->setMinimumHeight((height - minHeight) / 2); } -StatusFiltersWidget* TransferListFiltersWidget::getStatusFilters() const -{ - return statusFilters; -} - -void TransferListFiltersWidget::saveSettings() const -{ - Preferences* const pref = Preferences::instance(); - pref->setTransSelFilter(statusFilters->currentRow()); - pref->setTorrentLabels(customLabels.keys()); -} - -void TransferListFiltersWidget::loadSettings() -{ - statusFilters->setCurrentRow(Preferences::instance()->getTransSelFilter()); - const QStringList label_list = Preferences::instance()->getTorrentLabels(); - foreach (const QString &label, label_list) { - customLabels.insert(label, 0); - qDebug("Creating label QListWidgetItem: %s", qPrintable(label)); - QListWidgetItem *newLabel = new QListWidgetItem(); - newLabel->setText(label + " (0)"); - newLabel->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); - labelFilters->addItem(newLabel); - } -} - -void TransferListFiltersWidget::updateTorrentNumbers() -{ - const TorrentStatusReport report = transferList->getSourceModel()->getTorrentStatusReport(); - statusFilters->item(TorrentFilter::ALL)->setData(Qt::DisplayRole, QVariant(tr("All") + " (" + QString::number(report.nb_active + report.nb_inactive) + ")")); - statusFilters->item(TorrentFilter::DOWNLOADING)->setData(Qt::DisplayRole, QVariant(tr("Downloading") + " (" + QString::number(report.nb_downloading) + ")")); - statusFilters->item(TorrentFilter::COMPLETED)->setData(Qt::DisplayRole, QVariant(tr("Completed") + " (" + QString::number(report.nb_seeding) + ")")); - statusFilters->item(TorrentFilter::PAUSED)->setData(Qt::DisplayRole, QVariant(tr("Paused") + " (" + QString::number(report.nb_paused) + ")")); - statusFilters->item(TorrentFilter::RESUMED)->setData(Qt::DisplayRole, QVariant(tr("Resumed") + " (" + QString::number(report.nb_active + report.nb_inactive - report.nb_paused) + ")")); - statusFilters->item(TorrentFilter::ACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Active") + " (" + QString::number(report.nb_active) + ")")); - statusFilters->item(TorrentFilter::INACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Inactive") + " (" + QString::number(report.nb_inactive) + ")")); -} - -void TransferListFiltersWidget::addLabel(QString& label) -{ - label = fsutils::toValidFileSystemName(label.trimmed()); - if (label.isEmpty() || customLabels.contains(label)) return; - QListWidgetItem *newLabel = new QListWidgetItem(); - newLabel->setText(label + " (0)"); - newLabel->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); - labelFilters->addItem(newLabel); - customLabels.insert(label, 0); - Preferences::instance()->addTorrentLabel(label); -} - void TransferListFiltersWidget::addTrackers(const QStringList &trackers, const QString &hash) { foreach (const QString &tracker, trackers) @@ -509,226 +707,3 @@ void TransferListFiltersWidget::changeTrackerless(bool trackerless, const QStrin { trackerFilters->changeTrackerless(trackerless, hash); } - -void TransferListFiltersWidget::showLabelMenu(QPoint) -{ - QMenu labelMenu(labelFilters); - QAction *addAct = labelMenu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add label...")); - QAction *removeAct = 0; - QAction *removeUnusedAct = 0; - if (!labelFilters->selectedItems().empty() && labelFilters->row(labelFilters->selectedItems().first()) > 1) - removeAct = labelMenu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove label")); - else - removeUnusedAct = labelMenu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove unused labels")); - labelMenu.addSeparator(); - QAction *startAct = labelMenu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); - QAction *pauseAct = labelMenu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); - QAction *deleteTorrentsAct = labelMenu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); - QAction *act = 0; - act = labelMenu.exec(QCursor::pos()); - if (act) { - if (act == removeAct) { - removeSelectedLabel(); - return; - } - if (act == removeUnusedAct) { - removeUnusedLabels(); - return; - } - if (act == deleteTorrentsAct) { - transferList->deleteVisibleTorrents(); - return; - } - if (act == startAct) { - transferList->startVisibleTorrents(); - return; - } - if (act == pauseAct) { - transferList->pauseVisibleTorrents(); - return; - } - if (act == addAct) { - bool ok; - QString label = ""; - bool invalid; - do { - invalid = false; - label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, label, &ok); - if (ok && !label.isEmpty()) { - if (fsutils::isValidFileSystemName(label)) { - addLabel(label); - } - else { - QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); - invalid = true; - } - } - } while (invalid); - return; - } - } -} - -void TransferListFiltersWidget::showTrackerMenu(QPoint) -{ - QMenu trackerMenu(trackerFilters); - QAction *startAct = trackerMenu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); - QAction *pauseAct = trackerMenu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); - QAction *deleteTorrentsAct = trackerMenu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); - QAction *act = 0; - act = trackerMenu.exec(QCursor::pos()); - - if (act) { - if (act == startAct) - transferList->startVisibleTorrents(); - else if (act == pauseAct) - transferList->pauseVisibleTorrents(); - else if (act == deleteTorrentsAct) - transferList->deleteVisibleTorrents(); - } -} - -void TransferListFiltersWidget::removeSelectedLabel() -{ - const int row = labelFilters->row(labelFilters->selectedItems().first()); - Q_ASSERT(row > 1); - const QString &label = labelFilters->labelFromRow(row); - Q_ASSERT(customLabels.contains(label)); - customLabels.remove(label); - transferList->removeLabelFromRows(label); - // Select first label - labelFilters->setCurrentItem(labelFilters->item(0)); - labelFilters->selectionModel()->select(labelFilters->model()->index(0,0), QItemSelectionModel::Select); - applyLabelFilter(0); - // Un display filter - delete labelFilters->takeItem(row); - labelFilters->updateGeometry(); - // Save custom labels to remember it was deleted - Preferences::instance()->removeTorrentLabel(label); -} - -void TransferListFiltersWidget::removeUnusedLabels() -{ - QStringList unusedLabels; - QHash::const_iterator i; - for (i = customLabels.begin(); i != customLabels.end(); ++i) - if (i.value() == 0) - unusedLabels << i.key(); - foreach (const QString &label, unusedLabels) { - customLabels.remove(label); - delete labelFilters->takeItem(labelFilters->rowFromLabel(label)); - Preferences::instance()->removeTorrentLabel(label); - } - if (!unusedLabels.empty()) - labelFilters->updateGeometry(); -} - -void TransferListFiltersWidget::applyLabelFilter(int row) -{ - switch (row) { - case 0: - transferList->applyLabelFilterAll(); - break; - case 1: - transferList->applyLabelFilter(QString()); - break; - default: - transferList->applyLabelFilter(labelFilters->labelFromRow(row)); - } -} - -void TransferListFiltersWidget::applyTrackerFilter(int row) -{ - if (row == 0) - transferList->applyTrackerFilterAll(); - else - transferList->applyTrackerFilter(trackerFilters->getHashes(row)); -} - -void TransferListFiltersWidget::torrentChangedLabel(TorrentModelItem *torrentItem, QString old_label, QString new_label) -{ - Q_UNUSED(torrentItem); - qDebug("Torrent label changed from %s to %s", qPrintable(old_label), qPrintable(new_label)); - if (!old_label.isEmpty()) { - if (customLabels.contains(old_label)) { - const int new_count = customLabels.value(old_label, 0) - 1; - Q_ASSERT(new_count >= 0); - customLabels.insert(old_label, new_count); - const int row = labelFilters->rowFromLabel(old_label); - Q_ASSERT(row >= 2); - labelFilters->item(row)->setText(old_label + " (" + QString::number(new_count) + ")"); - } - --nb_labeled; - } - if (!new_label.isEmpty()) { - if (!customLabels.contains(new_label)) - addLabel(new_label); - const int new_count = customLabels.value(new_label, 0) + 1; - Q_ASSERT(new_count >= 1); - customLabels.insert(new_label, new_count); - const int row = labelFilters->rowFromLabel(new_label); - Q_ASSERT(row >= 2); - labelFilters->item(row)->setText(new_label + " (" + QString::number(new_count) + ")"); - ++nb_labeled; - } - updateStickyLabelCounters(); -} - -void TransferListFiltersWidget::handleNewTorrent(TorrentModelItem* torrentItem) -{ - QString label = torrentItem->data(TorrentModelItem::TR_LABEL).toString(); - qDebug("New torrent was added with label: %s", qPrintable(label)); - if (!label.isEmpty()) { - if (!customLabels.contains(label)) { - addLabel(label); - // addLabel may have changed the label, update the model accordingly. - torrentItem->setData(TorrentModelItem::TR_LABEL, label); - } - // Update label counter - Q_ASSERT(customLabels.contains(label)); - const int new_count = customLabels.value(label, 0) + 1; - customLabels.insert(label, new_count); - const int row = labelFilters->rowFromLabel(label); - qDebug("torrentAdded, Row: %d", row); - Q_ASSERT(row >= 2); - Q_ASSERT(labelFilters->item(row)); - labelFilters->item(row)->setText(label + " (" + QString::number(new_count) + ")"); - ++nb_labeled; - } - ++nb_torrents; - Q_ASSERT(nb_torrents >= 0); - Q_ASSERT(nb_labeled >= 0); - Q_ASSERT(nb_labeled <= nb_torrents); - updateStickyLabelCounters(); - trackerFilters->addItem(torrentItem->torrentHandle()); - trackerFilters->setTorrentCount(nb_torrents); -} - -void TransferListFiltersWidget::torrentAboutToBeDeleted(TorrentModelItem* torrentItem) -{ - Q_ASSERT(torrentItem); - QString label = torrentItem->data(TorrentModelItem::TR_LABEL).toString(); - if (!label.isEmpty()) { - // Update label counter - const int new_count = customLabels.value(label, 0) - 1; - customLabels.insert(label, new_count); - const int row = labelFilters->rowFromLabel(label); - Q_ASSERT(row >= 2); - labelFilters->item(row)->setText(label + " (" + QString::number(new_count) + ")"); - --nb_labeled; - } - --nb_torrents; - qDebug("nb_torrents: %d, nb_labeled: %d", nb_torrents, nb_labeled); - Q_ASSERT(nb_torrents >= 0); - Q_ASSERT(nb_labeled >= 0); - Q_ASSERT(nb_labeled <= nb_torrents); - updateStickyLabelCounters(); - trackerFilters->removeItem(torrentItem->torrentHandle()); - trackerFilters->setTorrentCount(nb_torrents); -} - -void TransferListFiltersWidget::updateStickyLabelCounters() -{ - labelFilters->item(0)->setText(tr("All labels") + " (" + QString::number(nb_torrents) + ")"); - labelFilters->item(1)->setText(tr("Unlabeled") + " (" + QString::number(nb_torrents - nb_labeled) + ")"); -} diff --git a/src/gui/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h index 21e7e3465..fdbdaf816 100644 --- a/src/gui/transferlistfilterswidget.h +++ b/src/gui/transferlistfilterswidget.h @@ -35,9 +35,7 @@ #include QT_BEGIN_NAMESPACE -class QListWidgetItem; -class QVBoxLayout; -class QDragMoveEvent; +class QResizeEvent; class QLabel; QT_END_NAMESPACE @@ -51,32 +49,72 @@ class FiltersBase: public QListWidget Q_OBJECT public: - FiltersBase(QWidget *parent); + FiltersBase(QWidget *parent, TransferListWidget *transferList); - QSize sizeHint() const; - QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + TransferListWidget *transferList; + +private slots: + virtual void showMenu(QPoint) = 0; + virtual void applyFilter(int row) = 0; + virtual void handleNewTorrent(TorrentModelItem* torrentItem) = 0; + virtual void torrentAboutToBeDeleted(TorrentModelItem* torrentItem) = 0; }; -class LabelFiltersList: public FiltersBase +class StatusFiltersWidget: public FiltersBase { Q_OBJECT public: - LabelFiltersList(QWidget *parent); + StatusFiltersWidget(QWidget *parent, TransferListWidget *transferList); + ~StatusFiltersWidget(); - // Redefine addItem() to make sure the list stays sorted - void addItem(QListWidgetItem *it); +private slots: + void updateTorrentNumbers(); - QString labelFromRow(int row) const; - int rowFromLabel(QString label) const; +private: + // These 4 methods are virtual slots in the base class. + // No need to redeclare them here as slots. + virtual void showMenu(QPoint); + virtual void applyFilter(int row); + virtual void handleNewTorrent(TorrentModelItem*); + virtual void torrentAboutToBeDeleted(TorrentModelItem*); }; -class StatusFiltersWidget: public FiltersBase +class LabelFiltersList: public FiltersBase { Q_OBJECT public: - StatusFiltersWidget(QWidget *parent); + LabelFiltersList(QWidget *parent, TransferListWidget *transferList); + ~LabelFiltersList(); + +private slots: + // Redefine addItem() to make sure the list stays sorted + void addItem(QString &label, bool hasTorrent); + void removeItem(const QString &label); + void removeSelectedLabel(); + void removeUnusedLabels(); + void torrentChangedLabel(TorrentModelItem *torrentItem, QString old_label, QString new_label); + + +private: + // These 4 methods are virtual slots in the base class. + // No need to redeclare them here as slots. + virtual void showMenu(QPoint); + virtual void applyFilter(int row); + virtual void handleNewTorrent(TorrentModelItem* torrentItem); + virtual void torrentAboutToBeDeleted(TorrentModelItem* torrentItem); + QString labelFromRow(int row) const; + int rowFromLabel(const QString &label) const; + +private: + QHash m_labels; + int m_totalTorrents; + int m_totalLabeled; }; class TrackerFiltersList: public FiltersBase @@ -84,55 +122,43 @@ class TrackerFiltersList: public FiltersBase Q_OBJECT public: - TrackerFiltersList(QWidget *parent); + TrackerFiltersList(QWidget *parent, TransferListWidget *transferList); ~TrackerFiltersList(); // Redefine addItem() to make sure the list stays sorted void addItem(const QString &tracker, const QString &hash); - void addItem(const QTorrentHandle &handle); void removeItem(const QString &tracker, const QString &hash); - void removeItem(const QTorrentHandle &handle); - void setTorrentCount(int all); - QStringList getHashes(int row); + void changeTrackerless(bool trackerless, const QString &hash); -public slots: +private slots: void handleFavicoDownload(const QString &url, const QString &filePath); void handleFavicoFailure(const QString &url, const QString &reason); - void changeTrackerless(bool trackerless, const QString &hash); private: - QHash m_trackers; + // These 4 methods are virtual slots in the base class. + // No need to redeclare them here as slots. + virtual void showMenu(QPoint); + virtual void applyFilter(int row); + virtual void handleNewTorrent(TorrentModelItem* torrentItem); + virtual void torrentAboutToBeDeleted(TorrentModelItem* torrentItem); QString trackerFromRow(int row) const; int rowFromTracker(const QString &tracker) const; QString getHost(const QString &trakcer) const; + QStringList getHashes(int row); + +private: + QHash m_trackers; DownloadThread *m_downloader; QStringList m_iconPaths; + int m_totalTorrents; }; class TransferListFiltersWidget: public QFrame { Q_OBJECT -private: - QHash customLabels; - StatusFiltersWidget* statusFilters; - LabelFiltersList* labelFilters; - TrackerFiltersList* trackerFilters; - QVBoxLayout* vLayout; - TransferListWidget *transferList; - int nb_labeled; - int nb_torrents; - //for use in resizeEvent() - QLabel *torrentsLabel; - public: TransferListFiltersWidget(QWidget *parent, TransferListWidget *transferList); - ~TransferListFiltersWidget(); - - StatusFiltersWidget* getStatusFilters() const; - - void saveSettings() const; - void loadSettings(); public slots: void addTrackers(const QStringList &trackers, const QString &hash); @@ -142,19 +168,11 @@ public slots: protected: virtual void resizeEvent(QResizeEvent *event); -protected slots: - void updateTorrentNumbers(); - void addLabel(QString& label); - void showLabelMenu(QPoint); - void showTrackerMenu(QPoint); - void removeSelectedLabel(); - void removeUnusedLabels(); - void applyLabelFilter(int row); - void applyTrackerFilter(int row); - void torrentChangedLabel(TorrentModelItem *torrentItem, QString old_label, QString new_label); - void handleNewTorrent(TorrentModelItem* torrentItem); - void torrentAboutToBeDeleted(TorrentModelItem* torrentItem); - void updateStickyLabelCounters(); +private: + StatusFiltersWidget *statusFilters; + TrackerFiltersList *trackerFilters; + //for use in resizeEvent() + QLabel *torrentsLabel; }; #endif // TRANSFERLISTFILTERSWIDGET_H