2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
30 #include "propertieswidget.h"
35 #include <QListWidgetItem>
37 #include <QMessageBox>
41 #include <QStackedWidget>
45 #include "base/bittorrent/infohash.h"
46 #include "base/bittorrent/session.h"
47 #include "base/bittorrent/torrent.h"
48 #include "base/path.h"
49 #include "base/preferences.h"
50 #include "base/types.h"
51 #include "base/unicodestrings.h"
52 #include "base/utils/misc.h"
53 #include "base/utils/string.h"
54 #include "gui/autoexpandabledialog.h"
55 #include "gui/lineedit.h"
56 #include "gui/uithememanager.h"
57 #include "gui/utils.h"
58 #include "downloadedpiecesbar.h"
59 #include "peerlistwidget.h"
60 #include "pieceavailabilitybar.h"
61 #include "proptabbar.h"
62 #include "speedwidget.h"
63 #include "trackerlistwidget.h"
64 #include "ui_propertieswidget.h"
66 PropertiesWidget::PropertiesWidget(QWidget
*parent
)
68 , m_ui
{new Ui::PropertiesWidget
}
72 setAutoFillBackground(true);
77 // Torrent content filtering
78 m_contentFilterLine
= new LineEdit(this);
79 m_contentFilterLine
->setPlaceholderText(tr("Filter files..."));
80 m_contentFilterLine
->setFixedWidth(300);
81 connect(m_contentFilterLine
, &LineEdit::textChanged
, m_ui
->filesList
, &TorrentContentWidget::setFilterPattern
);
82 m_ui
->contentFilterLayout
->insertWidget(3, m_contentFilterLine
);
84 m_ui
->filesList
->setDoubleClickAction(TorrentContentWidget::DoubleClickAction::Open
);
87 connect(m_ui
->selectAllButton
, &QPushButton::clicked
, m_ui
->filesList
, &TorrentContentWidget::checkAll
);
88 connect(m_ui
->selectNoneButton
, &QPushButton::clicked
, m_ui
->filesList
, &TorrentContentWidget::checkNone
);
89 connect(m_ui
->listWebSeeds
, &QWidget::customContextMenuRequested
, this, &PropertiesWidget::displayWebSeedListMenu
);
90 connect(m_ui
->stackedProperties
, &QStackedWidget::currentChanged
, this, &PropertiesWidget::loadDynamicData
);
91 connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentSavePathChanged
, this, &PropertiesWidget::updateSavePath
);
92 connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentMetadataReceived
, this, &PropertiesWidget::updateTorrentInfos
);
93 connect(m_ui
->filesList
, &TorrentContentWidget::stateChanged
, this, &PropertiesWidget::saveSettings
);
95 // set bar height relative to screen dpi
96 const int barHeight
= 18;
98 // Downloaded pieces progress bar
99 m_ui
->tempProgressBarArea
->setVisible(false);
100 m_downloadedPieces
= new DownloadedPiecesBar(this);
101 m_ui
->groupBarLayout
->addWidget(m_downloadedPieces
, 0, 1);
102 m_downloadedPieces
->setFixedHeight(barHeight
);
103 m_downloadedPieces
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
);
105 // Pieces availability bar
106 m_ui
->tempAvailabilityBarArea
->setVisible(false);
107 m_piecesAvailability
= new PieceAvailabilityBar(this);
108 m_ui
->groupBarLayout
->addWidget(m_piecesAvailability
, 1, 1);
109 m_piecesAvailability
->setFixedHeight(barHeight
);
110 m_piecesAvailability
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
);
113 m_trackerList
= new TrackerListWidget(this);
114 m_ui
->trackerUpButton
->setIcon(UIThemeManager::instance()->getIcon(u
"go-up"_qs
));
115 m_ui
->trackerUpButton
->setIconSize(Utils::Gui::smallIconSize());
116 m_ui
->trackerDownButton
->setIcon(UIThemeManager::instance()->getIcon(u
"go-down"_qs
));
117 m_ui
->trackerDownButton
->setIconSize(Utils::Gui::smallIconSize());
118 connect(m_ui
->trackerUpButton
, &QPushButton::clicked
, m_trackerList
, &TrackerListWidget::moveSelectionUp
);
119 connect(m_ui
->trackerDownButton
, &QPushButton::clicked
, m_trackerList
, &TrackerListWidget::moveSelectionDown
);
120 m_ui
->hBoxLayoutTrackers
->insertWidget(0, m_trackerList
);
122 m_peerList
= new PeerListWidget(this);
123 m_ui
->vBoxLayoutPeerPage
->addWidget(m_peerList
);
125 m_tabBar
= new PropTabBar(nullptr);
126 m_tabBar
->setContentsMargins(0, 5, 0, 5);
127 m_ui
->verticalLayout
->addLayout(m_tabBar
);
128 connect(m_tabBar
, &PropTabBar::tabChanged
, m_ui
->stackedProperties
, &QStackedWidget::setCurrentIndex
);
129 connect(m_tabBar
, &PropTabBar::tabChanged
, this, &PropertiesWidget::saveSettings
);
130 connect(m_tabBar
, &PropTabBar::visibilityToggled
, this, &PropertiesWidget::setVisibility
);
131 connect(m_tabBar
, &PropTabBar::visibilityToggled
, this, &PropertiesWidget::saveSettings
);
133 const auto *editWebSeedsHotkey
= new QShortcut(Qt::Key_F2
, m_ui
->listWebSeeds
, nullptr, nullptr, Qt::WidgetShortcut
);
134 connect(editWebSeedsHotkey
, &QShortcut::activated
, this, &PropertiesWidget::editWebSeed
);
135 const auto *deleteWebSeedsHotkey
= new QShortcut(QKeySequence::Delete
, m_ui
->listWebSeeds
, nullptr, nullptr, Qt::WidgetShortcut
);
136 connect(deleteWebSeedsHotkey
, &QShortcut::activated
, this, &PropertiesWidget::deleteSelectedUrlSeeds
);
137 connect(m_ui
->listWebSeeds
, &QListWidget::doubleClicked
, this, &PropertiesWidget::editWebSeed
);
140 connect(Preferences::instance(), &Preferences::changed
, this, &PropertiesWidget::configure
);
143 PropertiesWidget::~PropertiesWidget()
149 void PropertiesWidget::showPiecesAvailability(bool show
)
151 m_ui
->labelPiecesAvailability
->setVisible(show
);
152 m_piecesAvailability
->setVisible(show
);
153 m_ui
->labelAverageAvailabilityVal
->setVisible(show
);
154 if (show
|| !m_downloadedPieces
->isVisible())
155 m_ui
->lineBelowBars
->setVisible(show
);
158 void PropertiesWidget::showPiecesDownloaded(bool show
)
160 m_ui
->labelDownloadedPieces
->setVisible(show
);
161 m_downloadedPieces
->setVisible(show
);
162 m_ui
->labelProgressVal
->setVisible(show
);
163 if (show
|| !m_piecesAvailability
->isVisible())
164 m_ui
->lineBelowBars
->setVisible(show
);
167 void PropertiesWidget::setVisibility(const bool visible
)
169 if (!visible
&& (m_state
== VISIBLE
))
171 const int tabBarHeight
= m_tabBar
->geometry().height(); // take height before hiding
172 auto *hSplitter
= static_cast<QSplitter
*>(parentWidget());
173 m_ui
->stackedProperties
->setVisible(false);
174 m_slideSizes
= hSplitter
->sizes();
175 hSplitter
->handle(1)->setVisible(false);
176 hSplitter
->handle(1)->setDisabled(true);
177 m_handleWidth
= hSplitter
->handleWidth();
178 hSplitter
->setHandleWidth(0);
179 const QList
<int> sizes
{(hSplitter
->geometry().height() - tabBarHeight
), tabBarHeight
};
180 hSplitter
->setSizes(sizes
);
181 setMaximumSize(maximumSize().width(), tabBarHeight
);
186 if (visible
&& (m_state
== REDUCED
))
188 m_ui
->stackedProperties
->setVisible(true);
189 auto *hSplitter
= static_cast<QSplitter
*>(parentWidget());
190 if (m_handleWidth
!= -1)
191 hSplitter
->setHandleWidth(m_handleWidth
);
192 hSplitter
->handle(1)->setDisabled(false);
193 hSplitter
->handle(1)->setVisible(true);
194 hSplitter
->setSizes(m_slideSizes
);
196 setMaximumSize(maximumSize().width(), QWIDGETSIZE_MAX
);
202 void PropertiesWidget::clear()
204 qDebug("Clearing torrent properties");
205 m_ui
->labelSavePathVal
->clear();
206 m_ui
->labelCreatedOnVal
->clear();
207 m_ui
->labelTotalPiecesVal
->clear();
208 m_ui
->labelInfohash1Val
->clear();
209 m_ui
->labelInfohash2Val
->clear();
210 m_ui
->labelCommentVal
->clear();
211 m_ui
->labelProgressVal
->clear();
212 m_ui
->labelAverageAvailabilityVal
->clear();
213 m_ui
->labelWastedVal
->clear();
214 m_ui
->labelUpTotalVal
->clear();
215 m_ui
->labelDlTotalVal
->clear();
216 m_ui
->labelUpLimitVal
->clear();
217 m_ui
->labelDlLimitVal
->clear();
218 m_ui
->labelElapsedVal
->clear();
219 m_ui
->labelConnectionsVal
->clear();
220 m_ui
->labelReannounceInVal
->clear();
221 m_ui
->labelShareRatioVal
->clear();
222 m_ui
->listWebSeeds
->clear();
223 m_ui
->labelETAVal
->clear();
224 m_ui
->labelSeedsVal
->clear();
225 m_ui
->labelPeersVal
->clear();
226 m_ui
->labelDlSpeedVal
->clear();
227 m_ui
->labelUpSpeedVal
->clear();
228 m_ui
->labelTotalSizeVal
->clear();
229 m_ui
->labelCompletedOnVal
->clear();
230 m_ui
->labelLastSeenCompleteVal
->clear();
231 m_ui
->labelCreatedByVal
->clear();
232 m_ui
->labelAddedOnVal
->clear();
233 m_trackerList
->clear();
234 m_downloadedPieces
->clear();
235 m_piecesAvailability
->clear();
237 m_contentFilterLine
->clear();
240 BitTorrent::Torrent
*PropertiesWidget::getCurrentTorrent() const
245 TrackerListWidget
*PropertiesWidget::getTrackerList() const
247 return m_trackerList
;
250 PeerListWidget
*PropertiesWidget::getPeerList() const
255 QTreeView
*PropertiesWidget::getFilesList() const
257 return m_ui
->filesList
;
260 void PropertiesWidget::updateSavePath(BitTorrent::Torrent
*const torrent
)
262 if (torrent
== m_torrent
)
263 m_ui
->labelSavePathVal
->setText(m_torrent
->savePath().toString());
266 void PropertiesWidget::loadTrackers(BitTorrent::Torrent
*const torrent
)
268 if (torrent
== m_torrent
)
269 m_trackerList
->loadTrackers();
272 void PropertiesWidget::updateTorrentInfos(BitTorrent::Torrent
*const torrent
)
274 if (torrent
== m_torrent
)
275 loadTorrentInfos(m_torrent
);
278 void PropertiesWidget::loadTorrentInfos(BitTorrent::Torrent
*const torrent
)
282 m_downloadedPieces
->setTorrent(m_torrent
);
283 m_piecesAvailability
->setTorrent(m_torrent
);
284 m_ui
->filesList
->setContentHandler(m_torrent
);
289 updateSavePath(m_torrent
);
291 m_ui
->labelInfohash1Val
->setText(m_torrent
->infoHash().v1().isValid() ? m_torrent
->infoHash().v1().toString() : tr("N/A"));
292 m_ui
->labelInfohash2Val
->setText(m_torrent
->infoHash().v2().isValid() ? m_torrent
->infoHash().v2().toString() : tr("N/A"));
293 if (m_torrent
->hasMetadata())
296 m_ui
->labelCreatedOnVal
->setText(QLocale().toString(m_torrent
->creationDate(), QLocale::ShortFormat
));
298 m_ui
->labelTotalSizeVal
->setText(Utils::Misc::friendlyUnit(m_torrent
->totalSize()));
301 m_ui
->labelCommentVal
->setText(Utils::Misc::parseHtmlLinks(m_torrent
->comment().toHtmlEscaped()));
306 m_ui
->labelCreatedByVal
->setText(m_torrent
->creator());
312 void PropertiesWidget::readSettings()
314 const Preferences
*const pref
= Preferences::instance();
315 // Restore splitter sizes
316 QStringList sizesStr
= pref
->getPropSplitterSizes().split(u
',');
317 if (sizesStr
.size() == 2)
319 m_slideSizes
<< sizesStr
.first().toInt();
320 m_slideSizes
<< sizesStr
.last().toInt();
321 auto *hSplitter
= static_cast<QSplitter
*>(parentWidget());
322 hSplitter
->setSizes(m_slideSizes
);
324 const int currentTab
= pref
->getPropCurTab();
325 const bool visible
= pref
->getPropVisible();
326 m_ui
->filesList
->header()->restoreState(pref
->getPropFileListState());
327 m_tabBar
->setCurrentIndex(currentTab
);
329 setVisibility(false);
332 void PropertiesWidget::saveSettings()
334 Preferences
*const pref
= Preferences::instance();
335 pref
->setPropVisible(m_state
== VISIBLE
);
337 auto *hSplitter
= static_cast<QSplitter
*>(parentWidget());
339 if (m_state
== VISIBLE
)
340 sizes
= hSplitter
->sizes();
342 sizes
= m_slideSizes
;
344 if (sizes
.size() == 2)
345 pref
->setPropSplitterSizes(QString::number(sizes
.first()) + u
',' + QString::number(sizes
.last()));
346 pref
->setPropFileListState(m_ui
->filesList
->header()->saveState());
347 // Remember current tab
348 pref
->setPropCurTab(m_tabBar
->currentIndex());
351 void PropertiesWidget::reloadPreferences()
353 // Take program preferences into consideration
354 m_peerList
->updatePeerHostNameResolutionState();
355 m_peerList
->updatePeerCountryResolutionState();
358 void PropertiesWidget::loadDynamicData()
360 // Refresh only if the torrent handle is valid and visible
361 if (!m_torrent
|| (m_state
!= VISIBLE
)) return;
364 switch (m_ui
->stackedProperties
->currentIndex())
366 case PropTabBar::MainTab
:
368 m_ui
->labelWastedVal
->setText(Utils::Misc::friendlyUnit(m_torrent
->wastedSize()));
370 m_ui
->labelUpTotalVal
->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent
->totalUpload())
371 , Utils::Misc::friendlyUnit(m_torrent
->totalPayloadUpload())));
373 m_ui
->labelDlTotalVal
->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent
->totalDownload())
374 , Utils::Misc::friendlyUnit(m_torrent
->totalPayloadDownload())));
376 m_ui
->labelUpLimitVal
->setText(m_torrent
->uploadLimit() <= 0 ? C_INFINITY
: Utils::Misc::friendlyUnit(m_torrent
->uploadLimit(), true));
378 m_ui
->labelDlLimitVal
->setText(m_torrent
->downloadLimit() <= 0 ? C_INFINITY
: Utils::Misc::friendlyUnit(m_torrent
->downloadLimit(), true));
380 QString elapsedString
;
381 if (m_torrent
->isFinished())
383 elapsedString
= tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
384 .arg(Utils::Misc::userFriendlyDuration(m_torrent
->activeTime())
385 , Utils::Misc::userFriendlyDuration(m_torrent
->finishedTime()));
389 elapsedString
= Utils::Misc::userFriendlyDuration(m_torrent
->activeTime());
391 m_ui
->labelElapsedVal
->setText(elapsedString
);
393 m_ui
->labelConnectionsVal
->setText(tr("%1 (%2 max)", "%1 and %2 are numbers, e.g. 3 (10 max)")
394 .arg(m_torrent
->connectionsCount())
395 .arg(m_torrent
->connectionsLimit() < 0 ? C_INFINITY
: QString::number(m_torrent
->connectionsLimit())));
397 m_ui
->labelETAVal
->setText(Utils::Misc::userFriendlyDuration(m_torrent
->eta(), MAX_ETA
));
399 // Update next announce time
400 m_ui
->labelReannounceInVal
->setText(Utils::Misc::userFriendlyDuration(m_torrent
->nextAnnounce()));
403 const qreal ratio
= m_torrent
->realRatio();
404 m_ui
->labelShareRatioVal
->setText(ratio
> BitTorrent::Torrent::MAX_RATIO
? C_INFINITY
: Utils::String::fromDouble(ratio
, 2));
406 m_ui
->labelSeedsVal
->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
407 .arg(QString::number(m_torrent
->seedsCount())
408 , QString::number(m_torrent
->totalSeedsCount())));
410 m_ui
->labelPeersVal
->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
411 .arg(QString::number(m_torrent
->leechsCount())
412 , QString::number(m_torrent
->totalLeechersCount())));
414 const qlonglong dlDuration
= m_torrent
->activeTime() - m_torrent
->finishedTime();
415 const QString dlAvg
= Utils::Misc::friendlyUnit((m_torrent
->totalDownload() / ((dlDuration
== 0) ? -1 : dlDuration
)), true);
416 m_ui
->labelDlSpeedVal
->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
417 .arg(Utils::Misc::friendlyUnit(m_torrent
->downloadPayloadRate(), true), dlAvg
));
419 const qlonglong ulDuration
= m_torrent
->activeTime();
420 const QString ulAvg
= Utils::Misc::friendlyUnit((m_torrent
->totalUpload() / ((ulDuration
== 0) ? -1 : ulDuration
)), true);
421 m_ui
->labelUpSpeedVal
->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
422 .arg(Utils::Misc::friendlyUnit(m_torrent
->uploadPayloadRate(), true), ulAvg
));
424 m_ui
->labelLastSeenCompleteVal
->setText(m_torrent
->lastSeenComplete().isValid() ? QLocale().toString(m_torrent
->lastSeenComplete(), QLocale::ShortFormat
) : tr("Never"));
426 m_ui
->labelCompletedOnVal
->setText(m_torrent
->completedTime().isValid() ? QLocale().toString(m_torrent
->completedTime(), QLocale::ShortFormat
) : QString
{});
428 m_ui
->labelAddedOnVal
->setText(QLocale().toString(m_torrent
->addedTime(), QLocale::ShortFormat
));
430 if (m_torrent
->hasMetadata())
432 using TorrentPtr
= QPointer
<BitTorrent::Torrent
>;
434 m_ui
->labelTotalPiecesVal
->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent
->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent
->pieceLength())).arg(m_torrent
->piecesHave()));
436 if (!m_torrent
->isFinished() && !m_torrent
->isPaused() && !m_torrent
->isQueued() && !m_torrent
->isChecking())
438 // Pieces availability
439 showPiecesAvailability(true);
440 m_torrent
->fetchPieceAvailability([this, torrent
= TorrentPtr(m_torrent
)](const QVector
<int> &pieceAvailability
)
442 if (torrent
== m_torrent
)
443 m_piecesAvailability
->setAvailability(pieceAvailability
);
446 m_ui
->labelAverageAvailabilityVal
->setText(Utils::String::fromDouble(m_torrent
->distributedCopies(), 3));
450 showPiecesAvailability(false);
454 qreal progress
= m_torrent
->progress() * 100.;
455 m_ui
->labelProgressVal
->setText(Utils::String::fromDouble(progress
, 1) + u
'%');
457 m_torrent
->fetchDownloadingPieces([this, torrent
= TorrentPtr(m_torrent
)](const QBitArray
&downloadingPieces
)
459 if (torrent
== m_torrent
)
460 m_downloadedPieces
->setProgress(m_torrent
->pieces(), downloadingPieces
);
465 showPiecesAvailability(false);
469 case PropTabBar::TrackersTab
:
471 m_trackerList
->loadTrackers();
473 case PropTabBar::PeersTab
:
475 m_peerList
->loadPeers(m_torrent
);
477 case PropTabBar::FilesTab
:
478 m_ui
->filesList
->refresh();
484 void PropertiesWidget::loadUrlSeeds()
489 using TorrentPtr
= QPointer
<BitTorrent::Torrent
>;
490 m_torrent
->fetchURLSeeds([this, torrent
= TorrentPtr(m_torrent
)](const QVector
<QUrl
> &urlSeeds
)
492 if (torrent
!= m_torrent
)
495 m_ui
->listWebSeeds
->clear();
496 qDebug("Loading URL seeds");
498 for (const QUrl
&urlSeed
: urlSeeds
)
500 qDebug("Loading URL seed: %s", qUtf8Printable(urlSeed
.toString()));
501 new QListWidgetItem(urlSeed
.toString(), m_ui
->listWebSeeds
);
506 void PropertiesWidget::displayWebSeedListMenu()
508 if (!m_torrent
) return;
510 const QModelIndexList rows
= m_ui
->listWebSeeds
->selectionModel()->selectedRows();
512 QMenu
*menu
= new QMenu(this);
513 menu
->setAttribute(Qt::WA_DeleteOnClose
);
515 menu
->addAction(UIThemeManager::instance()->getIcon(u
"list-add"_qs
), tr("New Web seed"), this, &PropertiesWidget::askWebSeed
);
519 menu
->addAction(UIThemeManager::instance()->getIcon(u
"edit-clear"_qs
, u
"list-remove"_qs
), tr("Remove Web seed")
520 , this, &PropertiesWidget::deleteSelectedUrlSeeds
);
521 menu
->addSeparator();
522 menu
->addAction(UIThemeManager::instance()->getIcon(u
"edit-copy"_qs
), tr("Copy Web seed URL")
523 , this, &PropertiesWidget::copySelectedWebSeedsToClipboard
);
524 menu
->addAction(UIThemeManager::instance()->getIcon(u
"edit-rename"_qs
), tr("Edit Web seed URL")
525 , this, &PropertiesWidget::editWebSeed
);
528 menu
->popup(QCursor::pos());
531 void PropertiesWidget::configure()
534 if (Preferences::instance()->isSpeedWidgetEnabled())
536 if (!qobject_cast
<SpeedWidget
*>(m_speedWidget
))
540 m_ui
->speedLayout
->removeWidget(m_speedWidget
);
541 delete m_speedWidget
;
544 m_speedWidget
= new SpeedWidget(this);
545 m_ui
->speedLayout
->addWidget(m_speedWidget
);
550 if (!qobject_cast
<QLabel
*>(m_speedWidget
))
554 m_ui
->speedLayout
->removeWidget(m_speedWidget
);
555 delete m_speedWidget
;
558 const auto displayText
= u
"<center><b>%1</b><p>%2</p></center>"_qs
559 .arg(tr("Speed graphs are disabled"), tr("You can enable it in Advanced Options"));
560 auto *label
= new QLabel(displayText
, this);
561 label
->setAlignment(Qt::AlignHCenter
| Qt::AlignVCenter
);
562 m_speedWidget
= label
;
563 m_ui
->speedLayout
->addWidget(m_speedWidget
);
568 void PropertiesWidget::askWebSeed()
571 // Ask user for a new url seed
572 const QString urlSeed
= AutoExpandableDialog::getText(this, tr("New URL seed", "New HTTP source"),
573 tr("New URL seed:"), QLineEdit::Normal
,
574 u
"http://www."_qs
, &ok
);
576 qDebug("Adding %s web seed", qUtf8Printable(urlSeed
));
577 if (!m_ui
->listWebSeeds
->findItems(urlSeed
, Qt::MatchFixedString
).empty())
579 QMessageBox::warning(this, u
"qBittorrent"_qs
, tr("This URL seed is already in the list."), QMessageBox::Ok
);
583 m_torrent
->addUrlSeeds({urlSeed
});
584 // Refresh the seeds list
588 void PropertiesWidget::deleteSelectedUrlSeeds()
590 const QList
<QListWidgetItem
*> selectedItems
= m_ui
->listWebSeeds
->selectedItems();
591 if (selectedItems
.isEmpty()) return;
593 QVector
<QUrl
> urlSeeds
;
594 urlSeeds
.reserve(selectedItems
.size());
596 for (const QListWidgetItem
*item
: selectedItems
)
597 urlSeeds
<< item
->text();
599 m_torrent
->removeUrlSeeds(urlSeeds
);
604 void PropertiesWidget::copySelectedWebSeedsToClipboard() const
606 const QList
<QListWidgetItem
*> selectedItems
= m_ui
->listWebSeeds
->selectedItems();
607 if (selectedItems
.isEmpty()) return;
609 QStringList urlsToCopy
;
610 for (const QListWidgetItem
*item
: selectedItems
)
611 urlsToCopy
<< item
->text();
613 QApplication::clipboard()->setText(urlsToCopy
.join(u
'\n'));
616 void PropertiesWidget::editWebSeed()
618 const QList
<QListWidgetItem
*> selectedItems
= m_ui
->listWebSeeds
->selectedItems();
619 if (selectedItems
.size() != 1) return;
621 const QListWidgetItem
*selectedItem
= selectedItems
.last();
622 const QString oldSeed
= selectedItem
->text();
624 const QString newSeed
= AutoExpandableDialog::getText(this, tr("Web seed editing"),
625 tr("Web seed URL:"), QLineEdit::Normal
,
629 if (!m_ui
->listWebSeeds
->findItems(newSeed
, Qt::MatchFixedString
).empty())
631 QMessageBox::warning(this, u
"qBittorrent"_qs
,
632 tr("This URL seed is already in the list."),
637 m_torrent
->removeUrlSeeds({oldSeed
});
638 m_torrent
->addUrlSeeds({newSeed
});