1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the examples of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:BSD$
9
** You may use this file under the terms of the BSD license as follows:
11
** "Redistribution and use in source and binary forms, with or without
12
** modification, are permitted provided that the following conditions are
14
** * Redistributions of source code must retain the above copyright
15
** notice, this list of conditions and the following disclaimer.
16
** * Redistributions in binary form must reproduce the above copyright
17
** notice, this list of conditions and the following disclaimer in
18
** the documentation and/or other materials provided with the
20
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21
** of its contributors may be used to endorse or promote products derived
22
** from this software without specific prior written permission.
25
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
39
****************************************************************************/
43
#include "addtorrentdialog.h"
44
#include "mainwindow.h"
45
#include "ratecontroller.h"
46
#include "torrentclient.h"
48
// TorrentView extends QTreeWidget to allow drag and drop.
49
class TorrentView : public QTreeWidget
53
TorrentView(QWidget *parent = 0);
55
#ifndef QT_NO_DRAGANDDROP
57
void fileDropped(const QString &fileName);
60
void dragMoveEvent(QDragMoveEvent *event);
61
void dropEvent(QDropEvent *event);
65
// TorrentViewDelegate is used to draw the progress bars.
66
class TorrentViewDelegate : public QItemDelegate
70
inline TorrentViewDelegate(MainWindow *mainWindow) : QItemDelegate(mainWindow) {}
72
inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
73
const QModelIndex &index ) const
75
if (index.column() != 2) {
76
QItemDelegate::paint(painter, option, index);
80
// Set up a QStyleOptionProgressBar to precisely mimic the
81
// environment of a progress bar.
82
QStyleOptionProgressBar progressBarOption;
83
progressBarOption.state = QStyle::State_Enabled;
84
progressBarOption.direction = QApplication::layoutDirection();
85
progressBarOption.rect = option.rect;
86
progressBarOption.fontMetrics = QApplication::fontMetrics();
87
progressBarOption.minimum = 0;
88
progressBarOption.maximum = 100;
89
progressBarOption.textAlignment = Qt::AlignCenter;
90
progressBarOption.textVisible = true;
92
// Set the progress and text values of the style option.
93
int progress = qobject_cast<MainWindow *>(parent())->clientForRow(index.row())->progress();
94
progressBarOption.progress = progress < 0 ? 0 : progress;
95
progressBarOption.text = QString().sprintf("%d%%", progressBarOption.progress);
97
// Draw the progress bar onto the view.
98
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
102
MainWindow::MainWindow(QWidget *parent)
103
: QMainWindow(parent), quitDialog(0), saveChanges(false)
105
// Initialize some static strings
107
headers << tr("Torrent") << tr("Peers/Seeds") << tr("Progress")
108
<< tr("Down rate") << tr("Up rate") << tr("Status");
111
torrentView = new TorrentView(this);
112
torrentView->setItemDelegate(new TorrentViewDelegate(this));
113
torrentView->setHeaderLabels(headers);
114
torrentView->setSelectionBehavior(QAbstractItemView::SelectRows);
115
torrentView->setAlternatingRowColors(true);
116
torrentView->setRootIsDecorated(false);
117
setCentralWidget(torrentView);
119
// Set header resize modes and initial section sizes
120
QFontMetrics fm = fontMetrics();
121
QHeaderView *header = torrentView->header();
122
header->resizeSection(0, fm.width("typical-name-for-a-torrent.torrent"));
123
header->resizeSection(1, fm.width(headers.at(1) + " "));
124
header->resizeSection(2, fm.width(headers.at(2) + " "));
125
header->resizeSection(3, qMax(fm.width(headers.at(3) + " "), fm.width(" 1234.0 KB/s ")));
126
header->resizeSection(4, qMax(fm.width(headers.at(4) + " "), fm.width(" 1234.0 KB/s ")));
127
header->resizeSection(5, qMax(fm.width(headers.at(5) + " "), fm.width(tr("Downloading") + " ")));
129
// Create common actions
130
QAction *newTorrentAction = new QAction(QIcon(":/icons/bottom.png"), tr("Add &new torrent"), this);
131
pauseTorrentAction = new QAction(QIcon(":/icons/player_pause.png"), tr("&Pause torrent"), this);
132
removeTorrentAction = new QAction(QIcon(":/icons/player_stop.png"), tr("&Remove torrent"), this);
135
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
136
fileMenu->addAction(newTorrentAction);
137
fileMenu->addAction(pauseTorrentAction);
138
fileMenu->addAction(removeTorrentAction);
139
fileMenu->addSeparator();
140
fileMenu->addAction(QIcon(":/icons/exit.png"), tr("E&xit"), this, SLOT(close()));
143
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
144
helpMenu->addAction(tr("&About"), this, SLOT(about()));
145
helpMenu->addAction(tr("About &Qt"), qApp, SLOT(aboutQt()));
148
QToolBar *topBar = new QToolBar(tr("Tools"));
149
addToolBar(Qt::TopToolBarArea, topBar);
150
topBar->setMovable(false);
151
topBar->addAction(newTorrentAction);
152
topBar->addAction(removeTorrentAction);
153
topBar->addAction(pauseTorrentAction);
154
topBar->addSeparator();
155
downActionTool = topBar->addAction(QIcon(tr(":/icons/1downarrow.png")), tr("Move down"));
156
upActionTool = topBar->addAction(QIcon(tr(":/icons/1uparrow.png")), tr("Move up"));
159
QToolBar *bottomBar = new QToolBar(tr("Rate control"));
160
addToolBar(Qt::BottomToolBarArea, bottomBar);
161
bottomBar->setMovable(false);
162
downloadLimitSlider = new QSlider(Qt::Horizontal);
163
downloadLimitSlider->setRange(0, 1000);
164
bottomBar->addWidget(new QLabel(tr("Max download:")));
165
bottomBar->addWidget(downloadLimitSlider);
166
bottomBar->addWidget((downloadLimitLabel = new QLabel(tr("0 KB/s"))));
167
downloadLimitLabel->setFixedSize(QSize(fm.width(tr("99999 KB/s")), fm.lineSpacing()));
168
bottomBar->addSeparator();
169
uploadLimitSlider = new QSlider(Qt::Horizontal);
170
uploadLimitSlider->setRange(0, 1000);
171
bottomBar->addWidget(new QLabel(tr("Max upload:")));
172
bottomBar->addWidget(uploadLimitSlider);
173
bottomBar->addWidget((uploadLimitLabel = new QLabel(tr("0 KB/s"))));
174
uploadLimitLabel->setFixedSize(QSize(fm.width(tr("99999 KB/s")), fm.lineSpacing()));
176
// Set up connections
177
connect(torrentView, SIGNAL(itemSelectionChanged()),
178
this, SLOT(setActionsEnabled()));
179
connect(torrentView, SIGNAL(fileDropped(QString)),
180
this, SLOT(acceptFileDrop(QString)));
181
connect(uploadLimitSlider, SIGNAL(valueChanged(int)),
182
this, SLOT(setUploadLimit(int)));
183
connect(downloadLimitSlider, SIGNAL(valueChanged(int)),
184
this, SLOT(setDownloadLimit(int)));
185
connect(newTorrentAction, SIGNAL(triggered()),
186
this, SLOT(addTorrent()));
187
connect(pauseTorrentAction, SIGNAL(triggered()),
188
this, SLOT(pauseTorrent()));
189
connect(removeTorrentAction, SIGNAL(triggered()),
190
this, SLOT(removeTorrent()));
191
connect(upActionTool, SIGNAL(triggered(bool)),
192
this, SLOT(moveTorrentUp()));
193
connect(downActionTool, SIGNAL(triggered(bool)),
194
this, SLOT(moveTorrentDown()));
196
// Load settings and start
197
setWindowTitle(tr("Torrent Client"));
199
QMetaObject::invokeMethod(this, "loadSettings", Qt::QueuedConnection);
202
QSize MainWindow::sizeHint() const
204
const QHeaderView *header = torrentView->header();
206
// Add up the sizes of all header sections. The last section is
207
// stretched, so its size is relative to the size of the width;
208
// instead of counting it, we count the size of its largest value.
209
int width = fontMetrics().width(tr("Downloading") + " ");
210
for (int i = 0; i < header->count() - 1; ++i)
211
width += header->sectionSize(i);
213
return QSize(width, QMainWindow::sizeHint().height())
214
.expandedTo(QApplication::globalStrut());
217
const TorrentClient *MainWindow::clientForRow(int row) const
219
// Return the client at the given row.
220
return jobs.at(row).client;
223
int MainWindow::rowOfClient(TorrentClient *client) const
225
// Return the row that displays this client's status, or -1 if the
226
// client is not known.
228
foreach (Job job, jobs) {
229
if (job.client == client)
236
void MainWindow::loadSettings()
238
// Load base settings (last working directory, upload/download limits).
239
QSettings settings("QtProject", "Torrent");
240
lastDirectory = settings.value("LastDirectory").toString();
241
if (lastDirectory.isEmpty())
242
lastDirectory = QDir::currentPath();
243
int up = settings.value("UploadLimit").toInt();
244
int down = settings.value("DownloadLimit").toInt();
245
uploadLimitSlider->setValue(up ? up : 170);
246
downloadLimitSlider->setValue(down ? down : 550);
248
// Resume all previous downloads.
249
int size = settings.beginReadArray("Torrents");
250
for (int i = 0; i < size; ++i) {
251
settings.setArrayIndex(i);
252
QByteArray resumeState = settings.value("resumeState").toByteArray();
253
QString fileName = settings.value("sourceFileName").toString();
254
QString dest = settings.value("destinationFolder").toString();
256
if (addTorrent(fileName, dest, resumeState)) {
257
TorrentClient *client = jobs.last().client;
258
client->setDownloadedBytes(settings.value("downloadedBytes").toLongLong());
259
client->setUploadedBytes(settings.value("uploadedBytes").toLongLong());
264
bool MainWindow::addTorrent()
266
// Show the file dialog, let the user select what torrent to start downloading.
267
QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a torrent file"),
269
tr("Torrents (*.torrent);;"
270
" All files (*.*)"));
271
if (fileName.isEmpty())
273
lastDirectory = QFileInfo(fileName).absolutePath();
275
// Show the "Add Torrent" dialog.
276
AddTorrentDialog *addTorrentDialog = new AddTorrentDialog(this);
277
addTorrentDialog->setTorrent(fileName);
278
addTorrentDialog->deleteLater();
279
if (!addTorrentDialog->exec())
282
// Add the torrent to our list of downloads
283
addTorrent(fileName, addTorrentDialog->destinationFolder());
286
QTimer::singleShot(1000, this, SLOT(saveSettings()));
291
void MainWindow::removeTorrent()
293
// Find the row of the current item, and find the torrent client
295
int row = torrentView->indexOfTopLevelItem(torrentView->currentItem());
296
TorrentClient *client = jobs.at(row).client;
299
client->disconnect();
300
connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped()));
303
// Remove the row from the view.
304
delete torrentView->takeTopLevelItem(row);
312
void MainWindow::torrentStopped()
314
// Schedule the client for deletion.
315
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
316
client->deleteLater();
318
// If the quit dialog is shown, update its progress.
320
if (++jobsStopped == jobsToStop)
325
void MainWindow::torrentError(TorrentClient::Error)
327
// Delete the client.
328
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
329
int row = rowOfClient(client);
330
QString fileName = jobs.at(row).torrentFileName;
333
// Display the warning.
334
QMessageBox::warning(this, tr("Error"),
335
tr("An error occurred while downloading %0: %1")
337
.arg(client->errorString()));
339
delete torrentView->takeTopLevelItem(row);
340
client->deleteLater();
343
bool MainWindow::addTorrent(const QString &fileName, const QString &destinationFolder,
344
const QByteArray &resumeState)
346
// Check if the torrent is already being downloaded.
347
foreach (Job job, jobs) {
348
if (job.torrentFileName == fileName && job.destinationDirectory == destinationFolder) {
349
QMessageBox::warning(this, tr("Already downloading"),
350
tr("The torrent file %1 is "
351
"already being downloaded.").arg(fileName));
356
// Create a new torrent client and attempt to parse the torrent data.
357
TorrentClient *client = new TorrentClient(this);
358
if (!client->setTorrent(fileName)) {
359
QMessageBox::warning(this, tr("Error"),
360
tr("The torrent file %1 cannot not be opened/resumed.").arg(fileName));
364
client->setDestinationFolder(destinationFolder);
365
client->setDumpedState(resumeState);
367
// Setup the client connections.
368
connect(client, SIGNAL(stateChanged(TorrentClient::State)), this, SLOT(updateState(TorrentClient::State)));
369
connect(client, SIGNAL(peerInfoUpdated()), this, SLOT(updatePeerInfo()));
370
connect(client, SIGNAL(progressUpdated(int)), this, SLOT(updateProgress(int)));
371
connect(client, SIGNAL(downloadRateUpdated(int)), this, SLOT(updateDownloadRate(int)));
372
connect(client, SIGNAL(uploadRateUpdated(int)), this, SLOT(updateUploadRate(int)));
373
connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped()));
374
connect(client, SIGNAL(error(TorrentClient::Error)), this, SLOT(torrentError(TorrentClient::Error)));
376
// Add the client to the list of downloading jobs.
379
job.torrentFileName = fileName;
380
job.destinationDirectory = destinationFolder;
383
// Create and add a row in the torrent view for this download.
384
QTreeWidgetItem *item = new QTreeWidgetItem(torrentView);
386
QString baseFileName = QFileInfo(fileName).fileName();
387
if (baseFileName.toLower().endsWith(".torrent"))
388
baseFileName.remove(baseFileName.size() - 8);
390
item->setText(0, baseFileName);
391
item->setToolTip(0, tr("Torrent: %1<br>Destination: %2")
392
.arg(baseFileName).arg(destinationFolder));
393
item->setText(1, tr("0/0"));
394
item->setText(2, "0");
395
item->setText(3, "0.0 KB/s");
396
item->setText(4, "0.0 KB/s");
397
item->setText(5, tr("Idle"));
398
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
399
item->setTextAlignment(1, Qt::AlignHCenter);
403
QTimer::singleShot(5000, this, SLOT(saveSettings()));
409
void MainWindow::saveSettings()
415
// Prepare and reset the settings
416
QSettings settings("QtProject", "Torrent");
419
settings.setValue("LastDirectory", lastDirectory);
420
settings.setValue("UploadLimit", uploadLimitSlider->value());
421
settings.setValue("DownloadLimit", downloadLimitSlider->value());
423
// Store data on all known torrents
424
settings.beginWriteArray("Torrents");
425
for (int i = 0; i < jobs.size(); ++i) {
426
settings.setArrayIndex(i);
427
settings.setValue("sourceFileName", jobs.at(i).torrentFileName);
428
settings.setValue("destinationFolder", jobs.at(i).destinationDirectory);
429
settings.setValue("uploadedBytes", jobs.at(i).client->uploadedBytes());
430
settings.setValue("downloadedBytes", jobs.at(i).client->downloadedBytes());
431
settings.setValue("resumeState", jobs.at(i).client->dumpedState());
437
void MainWindow::updateState(TorrentClient::State)
439
// Update the state string whenever the client's state changes.
440
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
441
int row = rowOfClient(client);
442
QTreeWidgetItem *item = torrentView->topLevelItem(row);
444
item->setToolTip(0, tr("Torrent: %1<br>Destination: %2<br>State: %3")
445
.arg(jobs.at(row).torrentFileName)
446
.arg(jobs.at(row).destinationDirectory)
447
.arg(client->stateString()));
449
item->setText(5, client->stateString());
454
void MainWindow::updatePeerInfo()
456
// Update the number of connected, visited, seed and leecher peers.
457
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
458
int row = rowOfClient(client);
460
QTreeWidgetItem *item = torrentView->topLevelItem(row);
461
item->setText(1, tr("%1/%2").arg(client->connectedPeerCount())
462
.arg(client->seedCount()));
465
void MainWindow::updateProgress(int percent)
467
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
468
int row = rowOfClient(client);
470
// Update the progressbar.
471
QTreeWidgetItem *item = torrentView->topLevelItem(row);
473
item->setText(2, QString::number(percent));
476
void MainWindow::setActionsEnabled()
478
// Find the view item and client for the current row, and update
479
// the states of the actions.
480
QTreeWidgetItem *item = 0;
481
if (!torrentView->selectedItems().isEmpty())
482
item = torrentView->selectedItems().first();
483
TorrentClient *client = item ? jobs.at(torrentView->indexOfTopLevelItem(item)).client : 0;
484
bool pauseEnabled = client && ((client->state() == TorrentClient::Paused)
485
|| (client->state() > TorrentClient::Preparing));
487
removeTorrentAction->setEnabled(item != 0);
488
pauseTorrentAction->setEnabled(item != 0 && pauseEnabled);
490
if (client && client->state() == TorrentClient::Paused) {
491
pauseTorrentAction->setIcon(QIcon(":/icons/player_play.png"));
492
pauseTorrentAction->setText(tr("Resume torrent"));
494
pauseTorrentAction->setIcon(QIcon(":/icons/player_pause.png"));
495
pauseTorrentAction->setText(tr("Pause torrent"));
498
int row = torrentView->indexOfTopLevelItem(item);
499
upActionTool->setEnabled(item && row != 0);
500
downActionTool->setEnabled(item && row != jobs.size() - 1);
503
void MainWindow::updateDownloadRate(int bytesPerSecond)
505
// Update the download rate.
506
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
507
int row = rowOfClient(client);
509
num.sprintf("%.1f KB/s", bytesPerSecond / 1024.0);
510
torrentView->topLevelItem(row)->setText(3, num);
514
QTimer::singleShot(5000, this, SLOT(saveSettings()));
518
void MainWindow::updateUploadRate(int bytesPerSecond)
520
// Update the upload rate.
521
TorrentClient *client = qobject_cast<TorrentClient *>(sender());
522
int row = rowOfClient(client);
524
num.sprintf("%.1f KB/s", bytesPerSecond / 1024.0);
525
torrentView->topLevelItem(row)->setText(4, num);
529
QTimer::singleShot(5000, this, SLOT(saveSettings()));
533
void MainWindow::pauseTorrent()
535
// Pause or unpause the current torrent.
536
int row = torrentView->indexOfTopLevelItem(torrentView->currentItem());
537
TorrentClient *client = jobs.at(row).client;
538
client->setPaused(client->state() != TorrentClient::Paused);
542
void MainWindow::moveTorrentUp()
544
QTreeWidgetItem *item = torrentView->currentItem();
545
int row = torrentView->indexOfTopLevelItem(item);
549
Job tmp = jobs.at(row - 1);
550
jobs[row - 1] = jobs[row];
553
QTreeWidgetItem *itemAbove = torrentView->takeTopLevelItem(row - 1);
554
torrentView->insertTopLevelItem(row, itemAbove);
558
void MainWindow::moveTorrentDown()
560
QTreeWidgetItem *item = torrentView->currentItem();
561
int row = torrentView->indexOfTopLevelItem(item);
562
if (row == jobs.size() - 1)
565
Job tmp = jobs.at(row + 1);
566
jobs[row + 1] = jobs[row];
569
QTreeWidgetItem *itemAbove = torrentView->takeTopLevelItem(row + 1);
570
torrentView->insertTopLevelItem(row, itemAbove);
574
static int rateFromValue(int value)
577
if (value >= 0 && value < 250) {
578
rate = 1 + int(value * 0.124);
579
} else if (value < 500) {
580
rate = 32 + int((value - 250) * 0.384);
581
} else if (value < 750) {
582
rate = 128 + int((value - 500) * 1.536);
584
rate = 512 + int((value - 750) * 6.1445);
589
void MainWindow::setUploadLimit(int value)
591
int rate = rateFromValue(value);
592
uploadLimitLabel->setText(tr("%1 KB/s").arg(QString().sprintf("%4d", rate)));
593
RateController::instance()->setUploadLimit(rate * 1024);
596
void MainWindow::setDownloadLimit(int value)
598
int rate = rateFromValue(value);
599
downloadLimitLabel->setText(tr("%1 KB/s").arg(QString().sprintf("%4d", rate)));
600
RateController::instance()->setDownloadLimit(rate * 1024);
603
void MainWindow::about()
605
QLabel *icon = new QLabel;
606
icon->setPixmap(QPixmap(":/icons/peertopeer.png"));
608
QLabel *text = new QLabel;
609
text->setWordWrap(true);
610
text->setText("<p>The <b>Torrent Client</b> example demonstrates how to"
611
" write a complete peer-to-peer file sharing"
612
" application using Qt's network and thread classes.</p>"
613
"<p>This feature complete client implementation of"
614
" the BitTorrent protocol can efficiently"
615
" maintain several hundred network connections"
616
" simultaneously.</p>");
618
QPushButton *quitButton = new QPushButton("OK");
620
QHBoxLayout *topLayout = new QHBoxLayout;
621
topLayout->setMargin(10);
622
topLayout->setSpacing(10);
623
topLayout->addWidget(icon);
624
topLayout->addWidget(text);
626
QHBoxLayout *bottomLayout = new QHBoxLayout;
627
bottomLayout->addStretch();
628
bottomLayout->addWidget(quitButton);
629
bottomLayout->addStretch();
631
QVBoxLayout *mainLayout = new QVBoxLayout;
632
mainLayout->addLayout(topLayout);
633
mainLayout->addLayout(bottomLayout);
636
about.setModal(true);
637
about.setWindowTitle(tr("About Torrent Client"));
638
about.setLayout(mainLayout);
640
connect(quitButton, SIGNAL(clicked()), &about, SLOT(close()));
645
void MainWindow::acceptFileDrop(const QString &fileName)
647
// Create and show the "Add Torrent" dialog.
648
AddTorrentDialog *addTorrentDialog = new AddTorrentDialog;
649
lastDirectory = QFileInfo(fileName).absolutePath();
650
addTorrentDialog->setTorrent(fileName);
651
addTorrentDialog->deleteLater();
652
if (!addTorrentDialog->exec())
655
// Add the torrent to our list of downloads.
656
addTorrent(fileName, addTorrentDialog->destinationFolder());
660
void MainWindow::closeEvent(QCloseEvent *)
665
// Save upload / download numbers.
669
quitDialog = new QProgressDialog(tr("Disconnecting from trackers"), tr("Abort"), 0, jobsToStop, this);
671
// Stop all clients, remove the rows from the view and wait for
672
// them to signal that they have stopped.
675
foreach (Job job, jobs) {
677
TorrentClient *client = job.client;
678
client->disconnect();
679
connect(client, SIGNAL(stopped()), this, SLOT(torrentStopped()));
681
delete torrentView->takeTopLevelItem(0);
684
if (jobsToStop > jobsStopped)
686
quitDialog->deleteLater();
690
TorrentView::TorrentView(QWidget *parent)
691
: QTreeWidget(parent)
693
#ifndef QT_NO_DRAGANDDROP
694
setAcceptDrops(true);
698
#ifndef QT_NO_DRAGANDDROP
699
void TorrentView::dragMoveEvent(QDragMoveEvent *event)
701
// Accept file actions with a '.torrent' extension.
702
QUrl url(event->mimeData()->text());
703
if (url.isValid() && url.scheme().toLower() == "file"
704
&& url.path().toLower().endsWith(".torrent"))
705
event->acceptProposedAction();
708
void TorrentView::dropEvent(QDropEvent *event)
710
// Accept drops if the file has a '.torrent' extension and it
712
QString fileName = QUrl(event->mimeData()->text()).path();
713
if (QFile::exists(fileName) && fileName.toLower().endsWith(".torrent"))
714
emit fileDropped(fileName);
718
#include "mainwindow.moc"