diff --git a/Changelog b/Changelog
index 26dd3bc21..95b7e4050 100644
--- a/Changelog
+++ b/Changelog
@@ -22,6 +22,7 @@
- FEATURE: Make sure torrent files are always sorted by name
- FEATURE: Seeds and Peers columns are now sortable
- FEATURE: Torrents can be rechecked from Web UI (Stephanos Antaris)
+ - FEATURE: New peers can manually be added to the torrents
- COSMETIC: Merged download / upload lists
- COSMETIC: Torrents can be filtered based on their status
- COSMETIC: Torrent properties are now displayed in main window
diff --git a/src/Icons/oxygen/add_peer.png b/src/Icons/oxygen/add_peer.png
new file mode 100644
index 000000000..a28a8a2e9
Binary files /dev/null and b/src/Icons/oxygen/add_peer.png differ
diff --git a/src/icons.qrc b/src/icons.qrc
index 546fe8c7f..7214c0166 100644
--- a/src/icons.qrc
+++ b/src/icons.qrc
@@ -142,5 +142,6 @@
Icons/oxygen/unsubscribe.png
Icons/oxygen/draw-rectangle.png
Icons/oxygen/subscribe16.png
+ Icons/oxygen/add_peer.png
\ No newline at end of file
diff --git a/src/peer.ui b/src/peer.ui
new file mode 100644
index 000000000..ad92877ed
--- /dev/null
+++ b/src/peer.ui
@@ -0,0 +1,109 @@
+
+
+ addPeerDialog
+
+
+
+ 0
+ 0
+ 400
+ 112
+
+
+
+
+ 0
+ 0
+
+
+
+ Peer addition
+
+
+ -
+
+
-
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ IP
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ Port
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 60
+ 0
+
+
+
+
+ 70
+ 16777215
+
+
+
+ 1000
+
+
+ 65535
+
+
+ 6881
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
diff --git a/src/peeraddition.h b/src/peeraddition.h
new file mode 100644
index 000000000..927585b5e
--- /dev/null
+++ b/src/peeraddition.h
@@ -0,0 +1,111 @@
+/*
+ * Bittorrent Client using Qt4 and libtorrent.
+ * Copyright (C) 2006 Christophe Dumez
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link this program with the OpenSSL project's "OpenSSL" library (or with
+ * modified versions of it that use the same license as the "OpenSSL" library),
+ * and distribute the linked executables. You must obey the GNU General Public
+ * License in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s), you may extend this exception to your version of the file(s),
+ * but you are not obligated to do so. If you do not wish to do so, delete this
+ * exception statement from your version.
+ *
+ * Contact : chris@qbittorrent.org
+ */
+
+#ifndef PEERADDITION_H
+#define PEERADDITION_H
+
+#include
+#include
+#include
+#include "ui_peer.h"
+#include
+
+class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog {
+ Q_OBJECT
+
+private:
+ bool valid;
+
+public:
+ PeerAdditionDlg(QWidget *parent=0): QDialog(parent), valid(false) {
+ setupUi(this);
+ connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+ connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput()));
+ }
+
+ ~PeerAdditionDlg(){}
+
+ QString getIP() const {
+ return lineIP->text();
+ }
+
+ unsigned short getPort() const {
+ return spinPort->value();
+ }
+
+ bool isValid() const {
+ return valid;
+ }
+
+ static boost::asio::ip::tcp::endpoint askForPeerEndpoint() {
+ boost::asio::ip::tcp::endpoint ep;
+ PeerAdditionDlg dlg;
+ dlg.exec();
+ if(dlg.isValid()) {
+ const QRegExp is_ipv6(QString::fromUtf8("[0-9a-f]{4}(:[0-9a-f]{4}){7}"), Qt::CaseInsensitive, QRegExp::RegExp);
+ const QRegExp is_ipv4(QString::fromUtf8("(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}"), Qt::CaseInsensitive, QRegExp::RegExp);
+ QString IP = dlg.getIP();
+ if(is_ipv4.exactMatch(IP)) {
+ // IPv4
+ ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string(IP.toLocal8Bit().data()), dlg.getPort());
+ } else {
+ // IPv6
+ ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v6::from_string(IP.toLocal8Bit().data()), dlg.getPort());
+ }
+ }
+ return ep;
+ }
+
+
+protected slots:
+ void validateInput() {
+ const QRegExp is_ipv6(QString::fromUtf8("[0-9a-f]{4}(:[0-9a-f]{4}){7}"), Qt::CaseInsensitive, QRegExp::RegExp);
+ const QRegExp is_ipv4(QString::fromUtf8("(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}"), Qt::CaseInsensitive, QRegExp::RegExp);
+ QString IP = getIP();
+ if(is_ipv4.exactMatch(IP)) {
+ qDebug("Detected IPv4 address: %s", IP.toLocal8Bit().data());
+ valid = true;
+ accept();
+ } else {
+ if(is_ipv6.exactMatch(IP)) {
+ qDebug("Detected IPv6 address: %s", IP.toLocal8Bit().data());
+ valid = true;
+ accept();
+ } else {
+ QMessageBox::warning(this, tr("Invalid IP"),
+ tr("The IP you provided is invalid."),
+ QMessageBox::Ok);
+
+ }
+ }
+ }
+};
+
+#endif // PEERADDITION_H
diff --git a/src/peerlistwidget.cpp b/src/peerlistwidget.cpp
index c8a0cb789..a3d9b5dfb 100644
--- a/src/peerlistwidget.cpp
+++ b/src/peerlistwidget.cpp
@@ -34,10 +34,12 @@
#include "preferences.h"
#include "propertieswidget.h"
#include "geoip.h"
+#include "peeraddition.h"
#include
#include
#include
#include
+#include
#include
PeerListWidget::PeerListWidget(PropertiesWidget *parent): properties(parent), display_flags(false) {
@@ -59,6 +61,9 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent): properties(parent), di
proxyModel->setDynamicSortFilter(true);
proxyModel->setSourceModel(listModel);
setModel(proxyModel);
+ // Context menu
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint)));
// List delegate
listDelegate = new PeerListDelegate(this);
setItemDelegate(listDelegate);
@@ -104,6 +109,27 @@ void PeerListWidget::updatePeerCountryResolutionState() {
}
}
+void PeerListWidget::showPeerListMenu(QPoint) {
+ QMenu menu;
+ QTorrentHandle h = properties->getCurrentTorrent();
+ if(!h.is_valid()) return;
+ QAction *addPeerAct = 0;
+ if(!h.is_queued() && !h.is_checking()) {
+ addPeerAct = menu.addAction(QIcon(":/Icons/oxygen/add_peer.png"), "Add a new peer");
+ }
+ QAction *act = menu.exec(QCursor::pos());
+ if(act == addPeerAct) {
+ boost::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint();
+ if(ep != boost::asio::ip::tcp::endpoint()) {
+ h.connect_peer(ep);
+ QMessageBox::information(0, tr("Peer addition"), tr("The peer was added to this torrent."));
+ } else {
+ qDebug("No peer was added");
+ }
+ return;
+ }
+}
+
void PeerListWidget::clear() {
qDebug("clearing peer list");
peerItems.clear();
diff --git a/src/peerlistwidget.h b/src/peerlistwidget.h
index 4f8f6e7ec..fa0b245ec 100644
--- a/src/peerlistwidget.h
+++ b/src/peerlistwidget.h
@@ -74,6 +74,7 @@ public slots:
protected slots:
void loadSettings();
void saveSettings() const;
+ void showPeerListMenu(QPoint);
};
#endif // PEERLISTWIDGET_H
diff --git a/src/qtorrenthandle.cpp b/src/qtorrenthandle.cpp
index 93fd5876a..253a251a8 100644
--- a/src/qtorrenthandle.cpp
+++ b/src/qtorrenthandle.cpp
@@ -290,6 +290,11 @@ void QTorrentHandle::file_progress(std::vector& fp) {
return h.file_progress(fp);
}
+bool QTorrentHandle::is_checking() const {
+ Q_ASSERT(h.is_valid());
+ return h.status().state == torrent_status::checking_files || h.status().state == torrent_status::checking_resume_data;
+}
+
size_type QTorrentHandle::all_time_download() {
Q_ASSERT(h.is_valid());
return h.status().all_time_download;
@@ -498,6 +503,11 @@ void QTorrentHandle::resolve_countries(bool r) {
h.resolve_countries(r);
}
+void QTorrentHandle::connect_peer(asio::ip::tcp::endpoint const& adr, int source) const {
+ Q_ASSERT(h.is_valid());
+ h.connect_peer(adr, source);
+}
+
//
// Operators
//
diff --git a/src/qtorrenthandle.h b/src/qtorrenthandle.h
index 71a21b04e..9a9bb0cb1 100644
--- a/src/qtorrenthandle.h
+++ b/src/qtorrenthandle.h
@@ -109,6 +109,7 @@ class QTorrentHandle {
QStringList files_path() const;
int num_uploads() const;
bool is_seed() const;
+ bool is_checking() const;
bool is_auto_managed() const;
qlonglong active_time() const;
qlonglong seeding_time() const;
@@ -145,6 +146,7 @@ class QTorrentHandle {
void move_storage(QString path) const;
void super_seeding(bool on) const;
void resolve_countries(bool r);
+ void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const;
//
// Operators
diff --git a/src/src.pro b/src/src.pro
index 57e79263a..4cc5210f0 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -191,7 +191,8 @@ HEADERS += GUI.h \
peerlistdelegate.h \
reverseresolution.h \
preferences.h \
- geoip.h
+ geoip.h \
+ peeraddition.h
FORMS += MainWindow.ui \
options.ui \
about.ui \
@@ -208,7 +209,8 @@ FORMS += MainWindow.ui \
trackersAdd.ui \
console.ui \
FeedDownloader.ui \
- propertiesWidget.ui
+ propertiesWidget.ui \
+ peer.ui
SOURCES += GUI.cpp \
main.cpp \
options_imp.cpp \