qBittorrent
trackerlistwidget.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * In addition, as a special exception, the copyright holders give permission to
20  * link this program with the OpenSSL project's "OpenSSL" library (or with
21  * modified versions of it that use the same license as the "OpenSSL" library),
22  * and distribute the linked executables. You must obey the GNU General Public
23  * License in all respects for all of the code used other than "OpenSSL". If you
24  * modify file(s), you may extend this exception to your version of the file(s),
25  * but you are not obligated to do so. If you do not wish to do so, delete this
26  * exception statement from your version.
27  */
28 
29 #include "trackerlistwidget.h"
30 
31 #include <QAction>
32 #include <QApplication>
33 #include <QClipboard>
34 #include <QColor>
35 #include <QDebug>
36 #include <QHeaderView>
37 #include <QMenu>
38 #include <QMessageBox>
39 #include <QShortcut>
40 #include <QStringList>
41 #include <QTableView>
42 #include <QTreeWidgetItem>
43 #include <QUrl>
44 #include <QVector>
45 
50 #include "base/global.h"
51 #include "base/preferences.h"
53 #include "gui/uithememanager.h"
54 #include "propertieswidget.h"
55 #include "trackersadditiondialog.h"
56 
57 #define NB_STICKY_ITEM 3
58 
60  : QTreeWidget()
61  , m_properties(properties)
62 {
63  // Set header
64  // Must be set before calling loadSettings() otherwise the header is reset on restart
65  setHeaderLabels(headerLabels());
66  // Load settings
67  loadSettings();
68  // Graphical settings
69  setRootIsDecorated(false);
70  setAllColumnsShowFocus(true);
71  setItemsExpandable(false);
72  setSelectionMode(QAbstractItemView::ExtendedSelection);
73  header()->setStretchLastSection(false); // Must be set after loadSettings() in order to work
74  header()->setTextElideMode(Qt::ElideRight);
75  // Ensure that at least one column is visible at all times
76  if (visibleColumnsCount() == 0)
77  setColumnHidden(COL_URL, false);
78  // To also mitigate the above issue, we have to resize each column when
79  // its size is 0, because explicitly 'showing' the column isn't enough
80  // in the above scenario.
81  for (int i = 0; i < COL_COUNT; ++i)
82  if ((columnWidth(i) <= 0) && !isColumnHidden(i))
83  resizeColumnToContents(i);
84  // Context menu
85  setContextMenuPolicy(Qt::CustomContextMenu);
86  connect(this, &QWidget::customContextMenuRequested, this, &TrackerListWidget::showTrackerListMenu);
87  // Header
88  header()->setContextMenuPolicy(Qt::CustomContextMenu);
89  connect(header(), &QWidget::customContextMenuRequested, this, &TrackerListWidget::displayToggleColumnsMenu);
90  connect(header(), &QHeaderView::sectionMoved, this, &TrackerListWidget::saveSettings);
91  connect(header(), &QHeaderView::sectionResized, this, &TrackerListWidget::saveSettings);
92  connect(header(), &QHeaderView::sortIndicatorChanged, this, &TrackerListWidget::saveSettings);
93 
94  // Set DHT, PeX, LSD items
95  m_DHTItem = new QTreeWidgetItem({ "", "** [DHT] **", "", "0", "", "", "0" });
96  insertTopLevelItem(0, m_DHTItem);
97  setRowColor(0, QColor("grey"));
98  m_PEXItem = new QTreeWidgetItem({ "", "** [PeX] **", "", "0", "", "", "0" });
99  insertTopLevelItem(1, m_PEXItem);
100  setRowColor(1, QColor("grey"));
101  m_LSDItem = new QTreeWidgetItem({ "", "** [LSD] **", "", "0", "", "", "0" });
102  insertTopLevelItem(2, m_LSDItem);
103  setRowColor(2, QColor("grey"));
104 
105  // Set static items alignment
106  const Qt::Alignment alignment = (Qt::AlignRight | Qt::AlignVCenter);
107  m_DHTItem->setTextAlignment(COL_PEERS, alignment);
108  m_PEXItem->setTextAlignment(COL_PEERS, alignment);
109  m_LSDItem->setTextAlignment(COL_PEERS, alignment);
110  m_DHTItem->setTextAlignment(COL_SEEDS, alignment);
111  m_PEXItem->setTextAlignment(COL_SEEDS, alignment);
112  m_LSDItem->setTextAlignment(COL_SEEDS, alignment);
113  m_DHTItem->setTextAlignment(COL_LEECHES, alignment);
114  m_PEXItem->setTextAlignment(COL_LEECHES, alignment);
115  m_LSDItem->setTextAlignment(COL_LEECHES, alignment);
116  m_DHTItem->setTextAlignment(COL_DOWNLOADED, alignment);
117  m_PEXItem->setTextAlignment(COL_DOWNLOADED, alignment);
118  m_LSDItem->setTextAlignment(COL_DOWNLOADED, alignment);
119 
120  // Set header alignment
121  headerItem()->setTextAlignment(COL_TIER, alignment);
122  headerItem()->setTextAlignment(COL_PEERS, alignment);
123  headerItem()->setTextAlignment(COL_SEEDS, alignment);
124  headerItem()->setTextAlignment(COL_LEECHES, alignment);
125  headerItem()->setTextAlignment(COL_DOWNLOADED, alignment);
126 
127  // Set hotkeys
128  const auto *editHotkey = new QShortcut(Qt::Key_F2, this, nullptr, nullptr, Qt::WidgetShortcut);
129  connect(editHotkey, &QShortcut::activated, this, &TrackerListWidget::editSelectedTracker);
130  const auto *deleteHotkey = new QShortcut(QKeySequence::Delete, this, nullptr, nullptr, Qt::WidgetShortcut);
131  connect(deleteHotkey, &QShortcut::activated, this, &TrackerListWidget::deleteSelectedTrackers);
132  const auto *copyHotkey = new QShortcut(QKeySequence::Copy, this, nullptr, nullptr, Qt::WidgetShortcut);
133  connect(copyHotkey, &QShortcut::activated, this, &TrackerListWidget::copyTrackerUrl);
134 
135  connect(this, &QAbstractItemView::doubleClicked, this, &TrackerListWidget::editSelectedTracker);
136 
137  // This hack fixes reordering of first column with Qt5.
138  // https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
139  QTableView unused;
140  unused.setVerticalHeader(header());
141  header()->setParent(this);
142  unused.setVerticalHeader(new QHeaderView(Qt::Horizontal));
143 }
144 
146 {
147  saveSettings();
148 }
149 
150 QVector<QTreeWidgetItem *> TrackerListWidget::getSelectedTrackerItems() const
151 {
152  const QList<QTreeWidgetItem *> selectedTrackerItems = selectedItems();
153  QVector<QTreeWidgetItem *> selectedTrackers;
154  selectedTrackers.reserve(selectedTrackerItems.size());
155 
156  for (QTreeWidgetItem *item : selectedTrackerItems)
157  {
158  if (indexOfTopLevelItem(item) >= NB_STICKY_ITEM) // Ignore STICKY ITEMS
159  selectedTrackers << item;
160  }
161 
162  return selectedTrackers;
163 }
164 
165 void TrackerListWidget::setRowColor(const int row, const QColor &color)
166 {
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);
171 }
172 
174 {
176  if (!torrent)
177  {
178  clear();
179  return;
180  }
181  const QVector<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
182  if (selectedTrackerItems.isEmpty()) return;
183 
184  bool change = false;
185  for (QTreeWidgetItem *item : selectedTrackerItems)
186  {
187  int index = indexOfTopLevelItem(item);
188  if (index > NB_STICKY_ITEM)
189  {
190  insertTopLevelItem(index - 1, takeTopLevelItem(index));
191  change = true;
192  }
193  }
194  if (!change) return;
195 
196  // Restore selection
197  QItemSelectionModel *selection = selectionModel();
198  for (QTreeWidgetItem *item : selectedTrackerItems)
199  selection->select(indexFromItem(item), (QItemSelectionModel::Rows | QItemSelectionModel::Select));
200 
201  setSelectionModel(selection);
202  // Update torrent trackers
203  QVector<BitTorrent::TrackerEntry> trackers;
204  trackers.reserve(topLevelItemCount());
205  for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i)
206  {
207  const QString trackerURL = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
208  trackers.append({trackerURL, (i - NB_STICKY_ITEM)});
209  }
210 
211  torrent->replaceTrackers(trackers);
212  // Reannounce
213  if (!torrent->isPaused())
214  torrent->forceReannounce();
215 }
216 
218 {
220  if (!torrent)
221  {
222  clear();
223  return;
224  }
225  const QVector<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
226  if (selectedTrackerItems.isEmpty()) return;
227 
228  bool change = false;
229  for (int i = selectedItems().size() - 1; i >= 0; --i)
230  {
231  int index = indexOfTopLevelItem(selectedTrackerItems.at(i));
232  if (index < (topLevelItemCount() - 1))
233  {
234  insertTopLevelItem(index + 1, takeTopLevelItem(index));
235  change = true;
236  }
237  }
238  if (!change) return;
239 
240  // Restore selection
241  QItemSelectionModel *selection = selectionModel();
242  for (QTreeWidgetItem *item : selectedTrackerItems)
243  selection->select(indexFromItem(item), (QItemSelectionModel::Rows | QItemSelectionModel::Select));
244 
245  setSelectionModel(selection);
246  // Update torrent trackers
247  QVector<BitTorrent::TrackerEntry> trackers;
248  trackers.reserve(topLevelItemCount());
249  for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i)
250  {
251  const QString trackerURL = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
252  trackers.append({trackerURL, (i - NB_STICKY_ITEM)});
253  }
254 
255  torrent->replaceTrackers(trackers);
256  // Reannounce
257  if (!torrent->isPaused())
258  torrent->forceReannounce();
259 }
260 
262 {
263  qDeleteAll(m_trackerItems);
264  m_trackerItems.clear();
265 
266  m_DHTItem->setText(COL_STATUS, "");
267  m_DHTItem->setText(COL_SEEDS, "");
268  m_DHTItem->setText(COL_LEECHES, "");
269  m_DHTItem->setText(COL_MSG, "");
270  m_PEXItem->setText(COL_STATUS, "");
271  m_PEXItem->setText(COL_SEEDS, "");
272  m_PEXItem->setText(COL_LEECHES, "");
273  m_PEXItem->setText(COL_MSG, "");
274  m_LSDItem->setText(COL_STATUS, "");
275  m_LSDItem->setText(COL_SEEDS, "");
276  m_LSDItem->setText(COL_LEECHES, "");
277  m_LSDItem->setText(COL_MSG, "");
278 }
279 
281 {
282  const QString working {tr("Working")};
283  const QString disabled {tr("Disabled")};
284  const QString torrentDisabled {tr("Disabled for this torrent")};
285  const auto *session = BitTorrent::Session::instance();
286 
287  // load DHT information
288  if (torrent->isPrivate() || torrent->isDHTDisabled())
289  m_DHTItem->setText(COL_STATUS, torrentDisabled);
290  else if (!session->isDHTEnabled())
291  m_DHTItem->setText(COL_STATUS, disabled);
292  else
293  m_DHTItem->setText(COL_STATUS, working);
294 
295  // Load PeX Information
296  if (torrent->isPrivate() || torrent->isPEXDisabled())
297  m_PEXItem->setText(COL_STATUS, torrentDisabled);
298  else if (!session->isPeXEnabled())
299  m_PEXItem->setText(COL_STATUS, disabled);
300  else
301  m_PEXItem->setText(COL_STATUS, working);
302 
303  // Load LSD Information
304  if (torrent->isPrivate() || torrent->isLSDDisabled())
305  m_LSDItem->setText(COL_STATUS, torrentDisabled);
306  else if (!session->isLSDEnabled())
307  m_LSDItem->setText(COL_STATUS, disabled);
308  else
309  m_LSDItem->setText(COL_STATUS, working);
310 
311  if (torrent->isPrivate())
312  {
313  QString privateMsg = tr("This torrent is private");
314  m_DHTItem->setText(COL_MSG, privateMsg);
315  m_PEXItem->setText(COL_MSG, privateMsg);
316  m_LSDItem->setText(COL_MSG, privateMsg);
317  }
318 
319  // XXX: libtorrent should provide this info...
320  // Count peers from DHT, PeX, LSD
321  uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0;
322  for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers()))
323  {
324  if (peer.isConnecting()) continue;
325 
326  if (peer.fromDHT())
327  {
328  if (peer.isSeed())
329  ++seedsDHT;
330  else
331  ++peersDHT;
332  }
333  if (peer.fromPeX())
334  {
335  if (peer.isSeed())
336  ++seedsPeX;
337  else
338  ++peersPeX;
339  }
340  if (peer.fromLSD())
341  {
342  if (peer.isSeed())
343  ++seedsLSD;
344  else
345  ++peersLSD;
346  }
347  }
348 
349  m_DHTItem->setText(COL_SEEDS, QString::number(seedsDHT));
350  m_DHTItem->setText(COL_LEECHES, QString::number(peersDHT));
351  m_PEXItem->setText(COL_SEEDS, QString::number(seedsPeX));
352  m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX));
353  m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
354  m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD));
355 }
356 
358 {
359  // Load trackers from torrent handle
361  if (!torrent) return;
362 
363  loadStickyItems(torrent);
364 
365  // Load actual trackers information
366  QStringList oldTrackerURLs = m_trackerItems.keys();
367 
368  for (const BitTorrent::TrackerEntry &entry : asConst(torrent->trackers()))
369  {
370  const QString trackerURL = entry.url;
371 
372  QTreeWidgetItem *item = m_trackerItems.value(trackerURL, nullptr);
373  if (!item)
374  {
375  item = new QTreeWidgetItem();
376  item->setText(COL_URL, trackerURL);
377  item->setToolTip(COL_URL, trackerURL);
378  addTopLevelItem(item);
379  m_trackerItems[trackerURL] = item;
380  }
381  else
382  {
383  oldTrackerURLs.removeOne(trackerURL);
384  }
385 
386  item->setText(COL_TIER, QString::number(entry.tier));
387 
388  switch (entry.status)
389  {
391  item->setText(COL_STATUS, tr("Working"));
392  break;
394  item->setText(COL_STATUS, tr("Updating..."));
395  break;
397  item->setText(COL_STATUS, tr("Not working"));
398  break;
400  item->setText(COL_STATUS, tr("Not contacted yet"));
401  break;
402  }
403 
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)
408  : tr("N/A")));
409  item->setText(COL_SEEDS, ((entry.numSeeds > -1)
410  ? QString::number(entry.numSeeds)
411  : tr("N/A")));
412  item->setText(COL_LEECHES, ((entry.numLeeches > -1)
413  ? QString::number(entry.numLeeches)
414  : tr("N/A")));
415  item->setText(COL_DOWNLOADED, ((entry.numDownloaded > -1)
416  ? QString::number(entry.numDownloaded)
417  : tr("N/A")));
418 
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);
423  item->setTextAlignment(COL_LEECHES, alignment);
424  item->setTextAlignment(COL_DOWNLOADED, alignment);
425  }
426 
427  // Remove old trackers
428  for (const QString &tracker : asConst(oldTrackerURLs))
429  delete m_trackerItems.take(tracker);
430 }
431 
432 // Ask the user for new trackers and add them to the torrent
434 {
436  if (!torrent) return;
437 
438  QVector<BitTorrent::TrackerEntry> trackers;
439  for (const QString &tracker : asConst(TrackersAdditionDialog::askForTrackers(this, torrent)))
440  trackers.append({tracker});
441 
442  torrent->addTrackers(trackers);
443 }
444 
446 {
447  const QVector<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
448  if (selectedTrackerItems.isEmpty()) return;
449 
450  QStringList urlsToCopy;
451  for (const QTreeWidgetItem *item : selectedTrackerItems)
452  {
453  QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
454  qDebug() << QString("Copy: ") + trackerURL;
455  urlsToCopy << trackerURL;
456  }
457  QApplication::clipboard()->setText(urlsToCopy.join('\n'));
458 }
459 
460 
462 {
464  if (!torrent)
465  {
466  clear();
467  return;
468  }
469 
470  const QVector<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
471  if (selectedTrackerItems.isEmpty()) return;
472 
473  QStringList urlsToRemove;
474  for (const QTreeWidgetItem *item : selectedTrackerItems)
475  {
476  QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
477  urlsToRemove << trackerURL;
478  m_trackerItems.remove(trackerURL);
479  delete item;
480  }
481 
482  // Iterate over the trackers and remove the selected ones
483  const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
484  QVector<BitTorrent::TrackerEntry> remainingTrackers;
485  remainingTrackers.reserve(trackers.size());
486 
487  for (const BitTorrent::TrackerEntry &entry : trackers)
488  {
489  if (!urlsToRemove.contains(entry.url))
490  remainingTrackers.push_back(entry);
491  }
492 
493  torrent->replaceTrackers(remainingTrackers);
494 
495  if (!torrent->isPaused())
496  torrent->forceReannounce();
497 }
498 
500 {
502  if (!torrent) return;
503 
504  const QVector<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
505  if (selectedTrackerItems.isEmpty()) return;
506 
507  // During multi-select only process item selected last
508  const QUrl trackerURL = selectedTrackerItems.last()->text(COL_URL);
509 
510  bool ok = false;
511  const QUrl newTrackerURL = AutoExpandableDialog::getText(this, tr("Tracker editing"), tr("Tracker URL:"),
512  QLineEdit::Normal, trackerURL.toString(), &ok).trimmed();
513  if (!ok) return;
514 
515  if (!newTrackerURL.isValid())
516  {
517  QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL entered is invalid."));
518  return;
519  }
520  if (newTrackerURL == trackerURL) return;
521 
522  QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
523  bool match = false;
524  for (BitTorrent::TrackerEntry &entry : trackers)
525  {
526  if (newTrackerURL == QUrl(entry.url))
527  {
528  QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists."));
529  return;
530  }
531 
532  if (!match && (trackerURL == QUrl(entry.url)))
533  {
534  match = true;
535  entry.url = newTrackerURL.toString();
536  }
537  }
538 
539  torrent->replaceTrackers(trackers);
540 
541  if (!torrent->isPaused())
542  torrent->forceReannounce();
543 }
544 
546 {
547  const QList<QTreeWidgetItem *> selItems = selectedItems();
548  if (selItems.isEmpty()) return;
549 
551  if (!torrent) return;
552 
553  const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
554 
555  for (const QTreeWidgetItem *item : selItems)
556  {
557  // DHT case
558  if (item == m_DHTItem)
559  {
560  torrent->forceDHTAnnounce();
561  continue;
562  }
563 
564  // Trackers case
565  for (int i = 0; i < trackers.size(); ++i)
566  {
567  if (item->text(COL_URL) == trackers[i].url)
568  {
569  torrent->forceReannounce(i);
570  break;
571  }
572  }
573  }
574 
575  loadTrackers();
576 }
577 
579 {
581  if (!torrent) return;
582 
583  QMenu *menu = new QMenu(this);
584  menu->setAttribute(Qt::WA_DeleteOnClose);
585 
586  // Add actions
587  menu->addAction(UIThemeManager::instance()->getIcon("list-add"), tr("Add a new tracker...")
589 
590  if (!getSelectedTrackerItems().isEmpty())
591  {
592  menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"),tr("Edit tracker URL...")
594  menu->addAction(UIThemeManager::instance()->getIcon("list-remove"), tr("Remove tracker")
596  menu->addAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy tracker URL")
598  }
599 
600  if (!torrent->isPaused())
601  {
602  menu->addAction(UIThemeManager::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers")
604  menu->addSeparator();
605  menu->addAction(UIThemeManager::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers")
606  , this, [this]()
607  {
609  h->forceReannounce();
610  h->forceDHTAnnounce();
611  });
612  }
613 
614  menu->popup(QCursor::pos());
615 }
616 
618 {
619  header()->restoreState(Preferences::instance()->getPropTrackerListState());
620 }
621 
623 {
624  Preferences::instance()->setPropTrackerListState(header()->saveState());
625 }
626 
628 {
629  return
630  {
631  tr("Tier")
632  , tr("URL")
633  , tr("Status")
634  , tr("Peers")
635  , tr("Seeds")
636  , tr("Leeches")
637  , tr("Downloaded")
638  , tr("Message")
639  };
640 }
641 
643 {
644  int visibleCols = 0;
645  for (int i = 0; i < COL_COUNT; ++i)
646  {
647  if (!isColumnHidden(i))
648  ++visibleCols;
649  }
650 
651  return visibleCols;
652 }
653 
655 {
656  QMenu *menu = new QMenu(this);
657  menu->setAttribute(Qt::WA_DeleteOnClose);
658  menu->setTitle(tr("Column visibility"));
659 
660  for (int i = 0; i < COL_COUNT; ++i)
661  {
662  QAction *myAct = menu->addAction(headerLabels().at(i));
663  myAct->setCheckable(true);
664  myAct->setChecked(!isColumnHidden(i));
665  myAct->setData(i);
666  }
667 
668  connect(menu, &QMenu::triggered, this, [this](const QAction *action)
669  {
670  const int col = action->data().toInt();
671  Q_ASSERT(visibleColumnsCount() > 0);
672 
673  if (!isColumnHidden(col) && (visibleColumnsCount() == 1))
674  return;
675 
676  setColumnHidden(col, !isColumnHidden(col));
677 
678  if (!isColumnHidden(col) && (columnWidth(col) <= 5))
679  resizeColumnToContents(col);
680 
681  saveSettings();
682  });
683 
684  menu->popup(QCursor::pos());
685 }
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()
Definition: session.cpp:997
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)
BitTorrent::Torrent * getCurrentTorrent() const
QHash< QString, QTreeWidgetItem * > m_trackerItems
QTreeWidgetItem * m_PEXItem
static QStringList headerLabels()
void loadStickyItems(const BitTorrent::Torrent *torrent)
void displayToggleColumnsMenu(const QPoint &)
PropertiesWidget * m_properties
void showTrackerListMenu(const QPoint &)
TrackerListWidget(PropertiesWidget *properties)
int visibleColumnsCount() const
void setRowColor(int row, const QColor &color)
QTreeWidgetItem * m_LSDItem
QTreeWidgetItem * m_DHTItem
QVector< QTreeWidgetItem * > getSelectedTrackerItems() const
static QStringList askForTrackers(QWidget *parent, BitTorrent::Torrent *const torrent)
static UIThemeManager * instance()
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
action
Definition: tstool.py:143
Definition: trackerentry.h:38
@ Working
Definition: trackerentry.h:42
@ NotWorking
Definition: trackerentry.h:44
@ Updating
Definition: trackerentry.h:43
@ NotContacted
Definition: trackerentry.h:41
#define NB_STICKY_ITEM