32 #include <QApplication>
36 #include <QHeaderView>
38 #include <QMessageBox>
40 #include <QStringList>
42 #include <QTreeWidgetItem>
57 #define NB_STICKY_ITEM 3
61 , m_properties(properties)
69 setRootIsDecorated(
false);
70 setAllColumnsShowFocus(
true);
71 setItemsExpandable(
false);
72 setSelectionMode(QAbstractItemView::ExtendedSelection);
73 header()->setStretchLastSection(
false);
74 header()->setTextElideMode(Qt::ElideRight);
77 setColumnHidden(
COL_URL,
false);
82 if ((columnWidth(i) <= 0) && !isColumnHidden(i))
83 resizeColumnToContents(i);
85 setContextMenuPolicy(Qt::CustomContextMenu);
88 header()->setContextMenuPolicy(Qt::CustomContextMenu);
95 m_DHTItem =
new QTreeWidgetItem({
"",
"** [DHT] **",
"",
"0",
"",
"",
"0" });
98 m_PEXItem =
new QTreeWidgetItem({
"",
"** [PeX] **",
"",
"0",
"",
"",
"0" });
101 m_LSDItem =
new QTreeWidgetItem({
"",
"** [LSD] **",
"",
"0",
"",
"",
"0" });
106 const Qt::Alignment alignment = (Qt::AlignRight | Qt::AlignVCenter);
121 headerItem()->setTextAlignment(
COL_TIER, alignment);
122 headerItem()->setTextAlignment(
COL_PEERS, alignment);
123 headerItem()->setTextAlignment(
COL_SEEDS, alignment);
124 headerItem()->setTextAlignment(
COL_LEECHES, alignment);
128 const auto *editHotkey =
new QShortcut(Qt::Key_F2,
this,
nullptr,
nullptr, Qt::WidgetShortcut);
130 const auto *deleteHotkey =
new QShortcut(QKeySequence::Delete,
this,
nullptr,
nullptr, Qt::WidgetShortcut);
132 const auto *copyHotkey =
new QShortcut(QKeySequence::Copy,
this,
nullptr,
nullptr, Qt::WidgetShortcut);
140 unused.setVerticalHeader(header());
141 header()->setParent(
this);
142 unused.setVerticalHeader(
new QHeaderView(Qt::Horizontal));
152 const QList<QTreeWidgetItem *> selectedTrackerItems = selectedItems();
153 QVector<QTreeWidgetItem *> selectedTrackers;
154 selectedTrackers.reserve(selectedTrackerItems.size());
156 for (QTreeWidgetItem *item : selectedTrackerItems)
159 selectedTrackers << item;
162 return selectedTrackers;
167 const int nbColumns = columnCount();
168 QTreeWidgetItem *item = topLevelItem(row);
169 for (
int i = 0; i < nbColumns; ++i)
170 item->setData(i, Qt::ForegroundRole, color);
182 if (selectedTrackerItems.isEmpty())
return;
185 for (QTreeWidgetItem *item : selectedTrackerItems)
187 int index = indexOfTopLevelItem(item);
190 insertTopLevelItem(index - 1, takeTopLevelItem(index));
197 QItemSelectionModel *selection = selectionModel();
198 for (QTreeWidgetItem *item : selectedTrackerItems)
199 selection->select(indexFromItem(item), (QItemSelectionModel::Rows | QItemSelectionModel::Select));
201 setSelectionModel(selection);
203 QVector<BitTorrent::TrackerEntry> trackers;
204 trackers.reserve(topLevelItemCount());
207 const QString trackerURL = topLevelItem(i)->data(
COL_URL, Qt::DisplayRole).toString();
226 if (selectedTrackerItems.isEmpty())
return;
229 for (
int i = selectedItems().size() - 1; i >= 0; --i)
231 int index = indexOfTopLevelItem(selectedTrackerItems.at(i));
232 if (index < (topLevelItemCount() - 1))
234 insertTopLevelItem(index + 1, takeTopLevelItem(index));
241 QItemSelectionModel *selection = selectionModel();
242 for (QTreeWidgetItem *item : selectedTrackerItems)
243 selection->select(indexFromItem(item), (QItemSelectionModel::Rows | QItemSelectionModel::Select));
245 setSelectionModel(selection);
247 QVector<BitTorrent::TrackerEntry> trackers;
248 trackers.reserve(topLevelItemCount());
251 const QString trackerURL = topLevelItem(i)->data(
COL_URL, Qt::DisplayRole).toString();
282 const QString working {tr(
"Working")};
283 const QString disabled {tr(
"Disabled")};
284 const QString torrentDisabled {tr(
"Disabled for this torrent")};
290 else if (!session->isDHTEnabled())
298 else if (!session->isPeXEnabled())
306 else if (!session->isLSDEnabled())
313 QString privateMsg = tr(
"This torrent is private");
321 uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
324 if (peer.isConnecting())
continue;
361 if (!torrent)
return;
370 const QString trackerURL = entry.url;
372 QTreeWidgetItem *item =
m_trackerItems.value(trackerURL,
nullptr);
375 item =
new QTreeWidgetItem();
376 item->setText(
COL_URL, trackerURL);
377 item->setToolTip(
COL_URL, trackerURL);
378 addTopLevelItem(item);
383 oldTrackerURLs.removeOne(trackerURL);
386 item->setText(
COL_TIER, QString::number(entry.tier));
388 switch (entry.status)
400 item->setText(
COL_STATUS, tr(
"Not contacted yet"));
404 item->setText(
COL_MSG, entry.message);
405 item->setToolTip(
COL_MSG, entry.message);
406 item->setText(
COL_PEERS, ((entry.numPeers > -1)
407 ? QString::number(entry.numPeers)
409 item->setText(
COL_SEEDS, ((entry.numSeeds > -1)
410 ? QString::number(entry.numSeeds)
412 item->setText(
COL_LEECHES, ((entry.numLeeches > -1)
413 ? QString::number(entry.numLeeches)
416 ? QString::number(entry.numDownloaded)
419 const Qt::Alignment alignment = (Qt::AlignRight | Qt::AlignVCenter);
420 item->setTextAlignment(
COL_TIER, alignment);
421 item->setTextAlignment(
COL_PEERS, alignment);
422 item->setTextAlignment(
COL_SEEDS, alignment);
428 for (
const QString &tracker :
asConst(oldTrackerURLs))
436 if (!torrent)
return;
438 QVector<BitTorrent::TrackerEntry> trackers;
440 trackers.append({tracker});
448 if (selectedTrackerItems.isEmpty())
return;
450 QStringList urlsToCopy;
451 for (
const QTreeWidgetItem *item : selectedTrackerItems)
453 QString trackerURL = item->data(
COL_URL, Qt::DisplayRole).toString();
454 qDebug() << QString(
"Copy: ") + trackerURL;
455 urlsToCopy << trackerURL;
457 QApplication::clipboard()->setText(urlsToCopy.join(
'\n'));
471 if (selectedTrackerItems.isEmpty())
return;
473 QStringList urlsToRemove;
474 for (
const QTreeWidgetItem *item : selectedTrackerItems)
476 QString trackerURL = item->data(
COL_URL, Qt::DisplayRole).toString();
477 urlsToRemove << trackerURL;
483 const QVector<BitTorrent::TrackerEntry> trackers = torrent->
trackers();
484 QVector<BitTorrent::TrackerEntry> remainingTrackers;
485 remainingTrackers.reserve(trackers.size());
489 if (!urlsToRemove.contains(entry.url))
490 remainingTrackers.push_back(entry);
502 if (!torrent)
return;
505 if (selectedTrackerItems.isEmpty())
return;
508 const QUrl trackerURL = selectedTrackerItems.last()->text(
COL_URL);
512 QLineEdit::Normal, trackerURL.toString(), &ok).trimmed();
515 if (!newTrackerURL.isValid())
517 QMessageBox::warning(
this, tr(
"Tracker editing failed"), tr(
"The tracker URL entered is invalid."));
520 if (newTrackerURL == trackerURL)
return;
522 QVector<BitTorrent::TrackerEntry> trackers = torrent->
trackers();
526 if (newTrackerURL == QUrl(entry.url))
528 QMessageBox::warning(
this, tr(
"Tracker editing failed"), tr(
"The tracker URL already exists."));
532 if (!match && (trackerURL == QUrl(entry.url)))
535 entry.url = newTrackerURL.toString();
547 const QList<QTreeWidgetItem *> selItems = selectedItems();
548 if (selItems.isEmpty())
return;
551 if (!torrent)
return;
553 const QVector<BitTorrent::TrackerEntry> trackers = torrent->
trackers();
555 for (
const QTreeWidgetItem *item : selItems)
565 for (
int i = 0; i < trackers.size(); ++i)
567 if (item->text(
COL_URL) == trackers[i].url)
581 if (!torrent)
return;
583 QMenu *menu =
new QMenu(
this);
584 menu->setAttribute(Qt::WA_DeleteOnClose);
604 menu->addSeparator();
614 menu->popup(QCursor::pos());
647 if (!isColumnHidden(i))
656 QMenu *menu =
new QMenu(
this);
657 menu->setAttribute(Qt::WA_DeleteOnClose);
658 menu->setTitle(tr(
"Column visibility"));
663 myAct->setCheckable(
true);
664 myAct->setChecked(!isColumnHidden(i));
668 connect(menu, &QMenu::triggered,
this, [
this](
const QAction *
action)
670 const int col =
action->data().toInt();
676 setColumnHidden(col, !isColumnHidden(col));
678 if (!isColumnHidden(col) && (columnWidth(col) <= 5))
679 resizeColumnToContents(col);
684 menu->popup(QCursor::pos());
static QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode=QLineEdit::Normal, const QString &text={}, bool *ok=nullptr, bool excludeExtension=false, Qt::InputMethodHints inputMethodHints=Qt::ImhNone)
static Session * instance()
virtual QVector< PeerInfo > peers() const =0
virtual bool isDHTDisabled() const =0
virtual void forceReannounce(int index=-1)=0
virtual void forceDHTAnnounce()=0
virtual bool isPEXDisabled() const =0
virtual bool isLSDDisabled() const =0
virtual bool isPaused() const =0
virtual QVector< TrackerEntry > trackers() const =0
virtual void addTrackers(const QVector< TrackerEntry > &trackers)=0
virtual void replaceTrackers(const QVector< TrackerEntry > &trackers)=0
virtual bool isPrivate() const =0
static Preferences * instance()
void setPropTrackerListState(const QByteArray &state)
static QStringList askForTrackers(QWidget *parent, BitTorrent::Torrent *const torrent)
static UIThemeManager * instance()
constexpr std::add_const_t< T > & asConst(T &t) noexcept