- Tagged rc3 release
2
AUTHORS
|
@ -1,5 +1,5 @@
|
|||
Author:
|
||||
* Christophe Dumez <chris@qbittorrent.org>
|
||||
|
||||
Other developers:
|
||||
Contributors:
|
||||
* Arnaud Demaizière <arnaud@qbittorrent.org>
|
||||
|
|
15
Changelog
|
@ -5,6 +5,7 @@
|
|||
- FEATURE: Bittorrent FAST extension support
|
||||
- FEATURE: Added RSS support
|
||||
- FEATURE: Support files prioritizing in a torrent
|
||||
- FEATURE: Brand new search engine plugins system
|
||||
- FEATURE: Finished torrents are now moved to another tab for seeding
|
||||
- FEATURE: Display more infos about the torrent in its properties
|
||||
- FEATURE: Allow the user to edit torrents' trackers
|
||||
|
@ -30,6 +31,15 @@
|
|||
- FEATURE: User is now warned when fast resume data was rejected
|
||||
- FEATURE: Url seeds are now displayed in torrent properties and are editable
|
||||
- FEATURE: Allow to drag 'n drop urls on the main window
|
||||
- FEATURE: Improved search engine (multipage support in all plugins)
|
||||
- FEATURE: Added BTJunkie search engine plugin
|
||||
- FEATURE: Added an option to force full disk allocation for all torrents
|
||||
- FEATURE: Added an option to add torrents in paused state
|
||||
- FEATURE: Added an option to set the max number of connections per torrent
|
||||
- FEATURE: Added an option to set the max number of uploads per torrent
|
||||
- FEATURE: Added an option to automatically delete torrents when they reach a given ratio (>= 1.0)
|
||||
- FEATURE: Added an option to display current transfer speeds in title bar
|
||||
- FEATURE: Torrent content is now displayed as a tree
|
||||
- I18N: Added Hungarian translation
|
||||
- I18N: Added Brazilian translation
|
||||
- BUGFIX: Progress of paused torrents is now correct on restart
|
||||
|
@ -52,11 +62,14 @@
|
|||
- BUGFIX: Made torrent deletion from hard-drive safer
|
||||
- BUGFIX: Prevent downloadFromUrl flooding
|
||||
- BUGFIX: ETA was wrong for torrents with filtered files
|
||||
- BUGFIX: Fixed drag'n drop on non-KDE systems
|
||||
- COSMETIC: Redesigned torrent properties a little
|
||||
- COSMETIC: Redesigned options a little
|
||||
- COSMETIC: Totally redesigned program preferences
|
||||
- COSMETIC: Display more logs messages concerning features
|
||||
- COSMETIC: Improved lists renderers
|
||||
- COSMETIC: Use a different icon for torrents being checked and for connecting ones
|
||||
- COSMETIC: Improved some icons
|
||||
- COSMETIC: Improved systray tooltip style
|
||||
|
||||
* Mon May 07 2007 - Christophe Dumez <chris@qbittorrent.org> - v0.9.3
|
||||
- BUGFIX: Fixed pause toggle on double-click in download list
|
||||
|
|
17
INSTALL
|
@ -16,9 +16,10 @@ will install and execute qBittorrent hopefully without any problems.
|
|||
Dependencies:
|
||||
- Qt >= 4.2 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
||||
|
||||
- libtorrent by Arvid Norberg (>= v0.13 REQUIRED)
|
||||
- rblibtorrent by Arvid Norberg (>= v0.13 REQUIRED)
|
||||
-> http://www.qbittorrent.org/download.php (advised)
|
||||
-> http://www.libtorrent.net
|
||||
Be carefull: another library (the one used by rtorrent) use the same name.
|
||||
Be careful: another library (the one used by rTorrent) use the same name.
|
||||
These are TWO different libraries and qBittorrent will only work with the one provided
|
||||
on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other.
|
||||
|
||||
|
@ -26,13 +27,17 @@ Dependencies:
|
|||
|
||||
- libcommoncpp2
|
||||
|
||||
- python >= 2.3 (previous might work - not tested): needed by search engine.
|
||||
- python >= 2.3 (needed by search engine)
|
||||
|
||||
- libmagick++ (advised, not required)
|
||||
* Needed for favicons support (RSS / Search plugins)
|
||||
|
||||
NOTE FOR GNOME USERS:
|
||||
- qt4-qtconfig package is advised when using Plastique style (default)
|
||||
or just change qBittorrent style to Cleanlooks (GNOME like)
|
||||
- libzzip (advised, not required)
|
||||
* Needed for zip support (Search plugins)
|
||||
|
||||
NOTE FOR NON-KDE USERS:
|
||||
- qt4-qtconfig package is advised when using other systems than KDE.
|
||||
You can also change qBittorrent style to Cleanlooks (GNOME like)
|
||||
|
||||
DOCUMENTATION:
|
||||
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
||||
|
|
14
README
|
@ -3,30 +3,30 @@ qBittorrent - A BitTorrent client in Qt4
|
|||
|
||||
Description:
|
||||
********************************
|
||||
qBittorrent is a bittorrent client programmed in C++ / Qt4 that use
|
||||
libtorrent (sometimes called rb_libtorrent) by Arvid Norberg.
|
||||
qBittorrent is a bittorrent client programmed in C++ / Qt4 that uses
|
||||
libtorrent (sometimes called rblibtorrent) by Arvid Norberg.
|
||||
|
||||
It aims to be a good alternative to all other bittorrent clients
|
||||
out there. qBittorrent is fast, stable and provides unicode
|
||||
support.
|
||||
support as well as many features.
|
||||
|
||||
Installation:
|
||||
********************************
|
||||
For installation follow the instructions from INSTALL file, but simple
|
||||
For installation, follow the instructions from INSTALL file, but simple:
|
||||
|
||||
./configure
|
||||
make && make install
|
||||
qbittorrent
|
||||
|
||||
will install and execute qBittorrent hopefully without any problems.
|
||||
will install and execute qBittorrent hopefully without any problem.
|
||||
|
||||
For more information please visit:
|
||||
http://www.qbittorrent.org
|
||||
|
||||
Please report any bug (or feature requests) to:
|
||||
Please report any bug (or feature request) to:
|
||||
http://bugs.qbittorrent.org
|
||||
|
||||
You can also meet me on IRC:
|
||||
You can also meet me (chris-qBT) on IRC:
|
||||
#qbittorrent on irc.freenode.net
|
||||
|
||||
------------------------------------------
|
||||
|
|
102
TODO
|
@ -1,98 +1,62 @@
|
|||
// Easy
|
||||
- Translations into as many languages as possible
|
||||
- Improve man page
|
||||
- Use Launchpad/Rosetta for translations once it supports TS files
|
||||
|
||||
// Intermediate
|
||||
- Port on MacOS, Windows (and create an installer for Windows) - Progressing slowly
|
||||
- Add some transparency (menus,...), improve look
|
||||
- Port on MacOS, Windows (and create an installer for Windows) - Slow progress
|
||||
- Add some transparency (menus,...), improve look / usabilty
|
||||
- Skins support? (contact Mateusz)
|
||||
|
||||
// Harder
|
||||
- Display a progress bar that really displays the pieces we have (like in eMule)
|
||||
- Display a progress bar that really represents the pieces we have (like in eMule)
|
||||
- Torrent scheduler ala µtorrent/Bitcomet
|
||||
|
||||
// Waiting for libtorrent
|
||||
- File selection in a torrent in compact mode
|
||||
- Allow to prioritize torrents
|
||||
- Allow to prioritize torrents (may code this in qBittorrent?)
|
||||
|
||||
// Unsure
|
||||
- Display the peers we are connected to for each torrent with infos (like flag, dl/up speeds, ...)
|
||||
- Azureus spoofing to prevent ban from trackers?
|
||||
- Option to shutdown computer when downloads are finished
|
||||
- Add a torrent scheduler
|
||||
- NAT checker/Tester
|
||||
- Display hard drive space left?
|
||||
- Make use of dbus on Linux for the single instance instead of socket communication?
|
||||
(http://techbase.kde.org/Development/Tutorials/D-Bus/Accessing_Interfaces)
|
||||
- search engines customizing
|
||||
- When favicon can't be downloaded, try to parse the webpage for:
|
||||
<link rel="icon" href="http://example.com/favicon.ico" type="image/vnd.microsoft.icon">
|
||||
* Be carefull, the link can be relative
|
||||
|
||||
// in v1.2.0
|
||||
- Allow user to organize the downloads into categories/folders
|
||||
- Allow user to organize the downloads into categories/folders?
|
||||
|
||||
// in v1.1.0
|
||||
- Tabs support in search
|
||||
- Allow to hide columns?
|
||||
- Allow to scan multiple directories? (useful?)
|
||||
- Web interface (turbogears? php?)
|
||||
- Web interface (turbogears? php? python?)
|
||||
* Webserver? Try to write a webserver as a plugin for qBittorrent in Python
|
||||
* http://fragments.turtlemeat.com/pythonwebserver.php
|
||||
- improve and test tracker authentication code (remember login/pass) (need a tracker to test this)
|
||||
- support zipped torrents? (useful?)
|
||||
- Add option for RSS customization (refresh interval, max news per RSS...)
|
||||
- Allow to disable UPnP/NAT-PMP/LSD in options?
|
||||
- Allow to automatically delete torrents when they reach a given ratio (in options) : easy
|
||||
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
|
||||
- Add "Mark all as read" feature for RSS
|
||||
- Allow to customize lists refreshing interval (in options)
|
||||
- Use search engines as plugins (split them, load them dynamically) to allow the user to add some
|
||||
- Improve search plugin install (choose in a list taken from plugins.qbittorrent.org)
|
||||
- Display the number of DHT node if possible
|
||||
- When adding a duplicate torrent, check if the trackers are different from the existing one and ask the user if he wants to add them
|
||||
- Display in torrent addition dialog:
|
||||
* free disk space on selected drive
|
||||
* free disk space after torrent download (and/or torrent size)
|
||||
- Allow to change action on double-click
|
||||
-> in download list
|
||||
-> in seeding list
|
||||
|
||||
// in v1.0.0 (partial) - WIP
|
||||
- Check storage st creation + hasher in torrent creation
|
||||
// in v1.0.0 - FEATURE FREEZE
|
||||
- Fix all (or almost all) opened bugs in bug tracker
|
||||
- update sorting when a new torrent is added?
|
||||
- Keep documention up to date
|
||||
- Windows port (Chris - Peerkoel)
|
||||
- write patches libtorrent for file_priority(int index), actual_size() ?
|
||||
- valgrind --tool=memcheck --leak-check=full src/qbittorrent (Looks ok)
|
||||
- 128m 29m 16m S 4.8 2.9 0:02.28 qbittorrent
|
||||
* beta 6
|
||||
- Translations update (IN PROGRESS)
|
||||
- Wait for some bug fixes in libtorrent :
|
||||
- Number of seeds non null for finished torrent (Ticket #122)
|
||||
- debug new torrent content selection
|
||||
- Recheck doc
|
||||
- Translations update (IN PROGRESS)
|
||||
- Make use of total_wanted
|
||||
|
||||
LANGUAGES UPDATED:
|
||||
- French *BETA3*
|
||||
- English *BETA3*
|
||||
- Japanese *BETA3*
|
||||
- Swedish *BETA3*
|
||||
- Slovak *BETA3*
|
||||
- Ukrainian *BETA3*
|
||||
- Chinese (simplified) *BETA4*
|
||||
- Hungarian *BETA4*
|
||||
- Italian *BETA5*
|
||||
- Polish *BETA5*
|
||||
- Portuguese *BETA5*
|
||||
- Brazilian *BETA5*
|
||||
- Spanish *BETA5*
|
||||
- German *BETA5*
|
||||
- Russian *BETA5*
|
||||
- Korean *BETA5*
|
||||
- Greek *BETA6*
|
||||
- Dutch *BETA6*
|
||||
- Romanian *BETA6*
|
||||
|
||||
beta5->beta6 changelog:
|
||||
- FEATURE: Split download tab from GUI class and cleaned up code
|
||||
- FEATURE: A lot of code optimization (CPU & memory usage)
|
||||
- FEATURE: Added support for .ico format (useful for RSS favicons)
|
||||
- FEATURE: Replaced Meganova search engine by TorrentReactor
|
||||
- I18N: Updated Greek, Dutch and Romanian translation
|
||||
- I18N: Removed no longer maintained Traditional chinese translation
|
||||
- BUGFIX: Made torrent deletion from hard-drive safer
|
||||
- BUGFIX: Fixed a bug when switching from finished to downloading list
|
||||
- BUGFIX: Showing checking progress for paused torrents too
|
||||
- BUGFIX: Fixed progress column sorting on startup
|
||||
- BUGFIX: Prevent downloadFromUrl flooding
|
||||
- BUGFIX: Fixed pause state toggle for paused and checking torrents
|
||||
- BUGFIX: Made finished list context menu more similar to the download list one
|
||||
- BUGFIX: Fixed Pause/Start action in lists context menus
|
||||
- BUGFIX: Improved ETA calculation
|
||||
- BUGFIX: ETA was wrong for torrents with filtered files
|
||||
- BUGFIX: Display the torrent that are being checked as 'checking' in seeding list
|
||||
- BUGFIX: Fixed file preview and improved previewable files detection
|
||||
- BUGFIX: Do not store and calculate ETA values for finished/paused torrents
|
||||
- BUGFIX: Fixed memory leak in GUI
|
||||
rc2->rc3 changelog:
|
||||
- BUGFIX: Fixed a proxy problem causing connections to be rejected by trackers
|
||||
- BUGFIX: Fixed compilation problem on FreeBSD
|
114
configure
vendored
|
@ -27,6 +27,9 @@ Dependency options:
|
|||
--disable-libmagick Disable use of libmagick
|
||||
--with-libmagick-inc=[path] Path to libmagick++ include files
|
||||
--with-libmagick-lib=[path] Path to libmagick++ library files
|
||||
--disable-libzzip Disable use of libzzip
|
||||
--with-libzzip-inc=[path] Path to libzzip++ include files
|
||||
--with-libzzip-lib=[path] Path to libzzip++ library files
|
||||
|
||||
EOT
|
||||
}
|
||||
|
@ -188,6 +191,21 @@ while [ $# -gt 0 ]; do
|
|||
shift
|
||||
;;
|
||||
|
||||
--disable-libzzip)
|
||||
QC_DISABLE_libzzip="Y"
|
||||
shift
|
||||
;;
|
||||
|
||||
--with-libzzip-inc=*)
|
||||
QC_WITH_LIBZZIP_INC=$optarg
|
||||
shift
|
||||
;;
|
||||
|
||||
--with-libzzip-lib=*)
|
||||
QC_WITH_LIBZZIP_LIB=$optarg
|
||||
shift
|
||||
;;
|
||||
|
||||
--verbose)
|
||||
QC_VERBOSE="Y"
|
||||
shift
|
||||
|
@ -218,6 +236,9 @@ echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_LIB
|
|||
echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick
|
||||
echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC
|
||||
echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB
|
||||
echo QC_DISABLE_libzzip=$QC_DISABLE_libzzip
|
||||
echo QC_WITH_LIBZZIP_INC=$QC_WITH_LIBZZIP_INC
|
||||
echo QC_WITH_LIBZZIP_LIB=$QC_WITH_LIBZZIP_LIB
|
||||
echo
|
||||
fi
|
||||
|
||||
|
@ -319,21 +340,21 @@ fi
|
|||
|
||||
gen_files() {
|
||||
cat >$1/modules.cpp <<EOT
|
||||
#line 1 "qt42.qcm"
|
||||
#line 1 "qt4.qcm"
|
||||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: Qt >= 4.2
|
||||
name: Qt >= 4.3
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
class qc_qt42 : public ConfObj
|
||||
class qc_qt4 : public ConfObj
|
||||
{
|
||||
public:
|
||||
qc_qt42(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.2"; }
|
||||
QString shortname() const { return "qt42"; }
|
||||
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.3"; }
|
||||
QString shortname() const { return "Qt 4.3"; }
|
||||
bool exec()
|
||||
{
|
||||
return(QT_VERSION >= 0x040200);
|
||||
return(QT_VERSION >= 0x040300);
|
||||
}
|
||||
};
|
||||
#line 1 "libtorrent.qcm"
|
||||
|
@ -618,11 +639,82 @@ public:
|
|||
magickConfig.waitForStarted();
|
||||
magickConfig.waitForFinished();
|
||||
QByteArray result = magickConfig.readAll();
|
||||
result = result.replace("\n", "");
|
||||
conf->addLib(result.data());
|
||||
conf->addDefine("HAVE_MAGICK");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#line 1 "libzzip.qcm"
|
||||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: libzzip
|
||||
arg: with-libzzip-inc=[path], Path to libzzip++ include files
|
||||
arg: with-libzzip-lib=[path], Path to libzzip++ library files
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
#include <QProcess>
|
||||
class qc_libzzip : public ConfObj
|
||||
{
|
||||
public:
|
||||
qc_libzzip(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Zzip library (libzzip)"; }
|
||||
QString shortname() const { return "libzzip"; }
|
||||
QString checkString() const {
|
||||
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||
return "";
|
||||
return ConfObj::checkString();
|
||||
}
|
||||
bool exec(){
|
||||
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||
return false;
|
||||
QString s;
|
||||
s = conf->getenv("QC_WITH_LIBZZIP_INC");
|
||||
if(!s.isEmpty()) {
|
||||
if(!conf->checkHeader(s, "zzip/zzip.h")) {
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
QStringList sl;
|
||||
sl << "/usr/include";
|
||||
sl << "/usr/local/include";
|
||||
bool found = false;
|
||||
foreach(s, sl){
|
||||
if(conf->checkHeader(s, "zzip/zzip.h")){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
return false;
|
||||
}
|
||||
conf->addIncludePath(s);
|
||||
|
||||
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
|
||||
if(!s.isEmpty()) {
|
||||
if(!QFile::exists(s+QString("libzzip.so"))){
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
QStringList sl;
|
||||
sl << "/usr/lib/";
|
||||
sl << "/usr/local/lib/";
|
||||
bool found = false;
|
||||
foreach(s, sl){
|
||||
if(QFile::exists(s+QString("libzzip.so"))){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
return false;
|
||||
}
|
||||
conf->addLib(QString("-L") + s);
|
||||
conf->addLib("-lzzip");
|
||||
conf->addDefine("HAVE_ZZIP");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#line 1 "python.qcm"
|
||||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
|
@ -646,7 +738,7 @@ public:
|
|||
|
||||
EOT
|
||||
cat >$1/modules_new.cpp <<EOT
|
||||
o = new qc_qt42(conf);
|
||||
o = new qc_qt4(conf);
|
||||
o->required = true;
|
||||
o->disabled = false;
|
||||
o = new qc_libtorrent(conf);
|
||||
|
@ -661,6 +753,9 @@ cat >$1/modules_new.cpp <<EOT
|
|||
o = new qc_libmagick(conf);
|
||||
o->required = false;
|
||||
o->disabled = false;
|
||||
o = new qc_libzzip(conf);
|
||||
o->required = false;
|
||||
o->disabled = false;
|
||||
o = new qc_python(conf);
|
||||
o->required = true;
|
||||
o->disabled = false;
|
||||
|
@ -1618,6 +1713,9 @@ export QC_WITH_LIBCOMMONCPP2_LIB
|
|||
export QC_DISABLE_libmagick
|
||||
export QC_WITH_LIBMAGICK_INC
|
||||
export QC_WITH_LIBMAGICK_LIB
|
||||
export QC_DISABLE_libzzip
|
||||
export QC_WITH_LIBZZIP_INC
|
||||
export QC_WITH_LIBZZIP_LIB
|
||||
export QC_VERBOSE
|
||||
rm -rf .qconftemp
|
||||
(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<profile>qbittorrent.pro</profile>
|
||||
<moddir>qcm</moddir>
|
||||
<datadir/>
|
||||
<dep type='qt42'>
|
||||
<dep type='qt4'>
|
||||
<required/>
|
||||
</dep>
|
||||
<dep type='libtorrent'>
|
||||
|
@ -15,7 +15,8 @@
|
|||
<dep type='libcommoncpp2'>
|
||||
<required/>
|
||||
</dep>
|
||||
<dep type='libmagick'/>
|
||||
<dep type='libmagick'/>
|
||||
<dep type='libzzip'/>
|
||||
<dep type='python'>
|
||||
<required/>
|
||||
</dep>
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
magickConfig.waitForStarted();
|
||||
magickConfig.waitForFinished();
|
||||
QByteArray result = magickConfig.readAll();
|
||||
result = result.replace("\n", "");
|
||||
conf->addLib(result.data());
|
||||
conf->addDefine("HAVE_MAGICK");
|
||||
return true;
|
||||
|
|
69
qcm/libzzip.qcm
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: libzzip
|
||||
arg: with-libzzip-inc=[path], Path to libzzip++ include files
|
||||
arg: with-libzzip-lib=[path], Path to libzzip++ library files
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
#include <QProcess>
|
||||
class qc_libzzip : public ConfObj
|
||||
{
|
||||
public:
|
||||
qc_libzzip(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Zzip library (libzzip)"; }
|
||||
QString shortname() const { return "libzzip"; }
|
||||
QString checkString() const {
|
||||
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||
return "";
|
||||
return ConfObj::checkString();
|
||||
}
|
||||
bool exec(){
|
||||
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||
return false;
|
||||
QString s;
|
||||
s = conf->getenv("QC_WITH_LIBZZIP_INC");
|
||||
if(!s.isEmpty()) {
|
||||
if(!conf->checkHeader(s, "zzip/zzip.h")) {
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
QStringList sl;
|
||||
sl << "/usr/include";
|
||||
sl << "/usr/local/include";
|
||||
bool found = false;
|
||||
foreach(s, sl){
|
||||
if(conf->checkHeader(s, "zzip/zzip.h")){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
return false;
|
||||
}
|
||||
conf->addIncludePath(s);
|
||||
|
||||
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
|
||||
if(!s.isEmpty()) {
|
||||
if(!QFile::exists(s+QString("libzzip.so"))){
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
QStringList sl;
|
||||
sl << "/usr/lib/";
|
||||
sl << "/usr/local/lib/";
|
||||
bool found = false;
|
||||
foreach(s, sl){
|
||||
if(QFile::exists(s+QString("libzzip.so"))){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
return false;
|
||||
}
|
||||
conf->addLib(QString("-L") + s);
|
||||
conf->addLib("-lzzip");
|
||||
conf->addDefine("HAVE_ZZIP");
|
||||
return true;
|
||||
}
|
||||
};
|
16
qcm/qt4.qcm
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: Qt >= 4.3
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
class qc_qt4 : public ConfObj
|
||||
{
|
||||
public:
|
||||
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.3"; }
|
||||
QString shortname() const { return "Qt 4.3"; }
|
||||
bool exec()
|
||||
{
|
||||
return(QT_VERSION >= 0x040300);
|
||||
}
|
||||
};
|
16
qcm/qt42.qcm
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
-----BEGIN QCMOD-----
|
||||
name: Qt >= 4.2
|
||||
-----END QCMOD-----
|
||||
*/
|
||||
class qc_qt42 : public ConfObj
|
||||
{
|
||||
public:
|
||||
qc_qt42(Conf *c) : ConfObj(c) {}
|
||||
QString name() const { return "Qt >= 4.2"; }
|
||||
QString shortname() const { return "qt42"; }
|
||||
bool exec()
|
||||
{
|
||||
return(QT_VERSION >= 0x040200);
|
||||
}
|
||||
};
|
|
@ -86,7 +86,8 @@ class DLListDelegate: public QItemDelegate {
|
|||
newopt.textVisible = false;
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
|
||||
painter);
|
||||
painter->setPen(QColor("Black"));
|
||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
||||
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
|
||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,8 @@ class FinishedListDelegate: public QItemDelegate {
|
|||
newopt.textVisible = false;
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
|
||||
painter);
|
||||
painter->setPen(QColor("Black"));
|
||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
||||
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
|
||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -122,7 +122,8 @@ void FinishedTorrents::torrentAdded(QString, QTorrentHandle& h, bool) {
|
|||
|
||||
// Set the color of a row in data model
|
||||
void FinishedTorrents::setRowColor(int row, QString color){
|
||||
for(int i=0; i<finishedListModel->columnCount(); ++i){
|
||||
unsigned int nbColumns = finishedListModel->columnCount()-1;
|
||||
for(unsigned int i=0; i<nbColumns; ++i){
|
||||
finishedListModel->setData(finishedListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +210,10 @@ void FinishedTorrents::updateFinishedList(){
|
|||
}
|
||||
Q_ASSERT(row != -1);
|
||||
if(h.is_paused()) continue;
|
||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
|
||||
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
|
||||
continue;
|
||||
}
|
||||
if(h.state() == torrent_status::downloading || (h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking && h.progress() < 1.)) {
|
||||
// What are you doing here? go back to download tab!
|
||||
qDebug("Info: a torrent was moved from finished to download tab");
|
||||
|
@ -218,10 +223,8 @@ void FinishedTorrents::updateFinishedList(){
|
|||
continue;
|
||||
}
|
||||
if(h.state() == torrent_status::checking_files){
|
||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
|
||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||
setRowColor(row, QString::fromUtf8("grey"));
|
||||
}
|
||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||
setRowColor(row, QString::fromUtf8("grey"));
|
||||
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
|
||||
continue;
|
||||
}
|
||||
|
@ -311,7 +314,7 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
|
|||
// Enable/disable pause/start action given the DL state
|
||||
QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes();
|
||||
QSettings settings("qBittorrent", "qBittorrent");
|
||||
QString previewProgram = settings.value("Options/Misc/PreviewProgram", QString()).toString();
|
||||
QString previewProgram = settings.value("Preferences/general/MediaPlayer", QString()).toString();
|
||||
bool has_pause = false, has_start = false, has_preview = false;
|
||||
foreach(index, selectedIndexes) {
|
||||
if(index.column() == F_NAME) {
|
||||
|
|
396
src/GUI.cpp
|
@ -56,11 +56,11 @@ namespace fs = boost::filesystem;
|
|||
*****************************************************/
|
||||
|
||||
// Constructor
|
||||
GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), force_exit(false) {
|
||||
GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), displaySpeedInTitle(false), force_exit(false), refreshInterval(1500) {
|
||||
setupUi(this);
|
||||
setWindowTitle(tr("qBittorrent %1", "e.g: qBittorrent v0.x").arg(QString::fromUtf8(VERSION)));
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
systrayIntegration = settings.value(QString::fromUtf8("Options/Misc/Behaviour/SystrayIntegration"), true).toBool();
|
||||
systrayIntegration = settings.value(QString::fromUtf8("Preferences/General/SystrayEnabled"), true).toBool();
|
||||
// Create tray icon
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
if(systrayIntegration) {
|
||||
|
@ -112,6 +112,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
|
|||
connect(BTSession, SIGNAL(scanDirFoundTorrents(const QStringList&)), this, SLOT(processScannedFiles(const QStringList&)));
|
||||
connect(BTSession, SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString)));
|
||||
connect(BTSession, SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString)));
|
||||
connect(BTSession, SIGNAL(torrent_deleted(QString, QString, bool)), this, SLOT(deleteTorrent(QString, QString, bool)));
|
||||
qDebug("create tabWidget");
|
||||
tabs = new QTabWidget();
|
||||
// Download torrents tab
|
||||
|
@ -130,6 +131,10 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
|
|||
// Smooth torrent switching between tabs Downloading <--> Finished
|
||||
connect(downloadingTorrentTab, SIGNAL(torrentFinished(QString)), finishedTorrentTab, SLOT(addTorrent(QString)));
|
||||
connect(finishedTorrentTab, SIGNAL(torrentMovedFromFinishedList(QString)), downloadingTorrentTab, SLOT(addTorrent(QString)));
|
||||
// Start download list refresher
|
||||
refresher = new QTimer(this);
|
||||
connect(refresher, SIGNAL(timeout()), this, SLOT(updateLists()));
|
||||
refresher->start(1500);
|
||||
// Configure BT session according to options
|
||||
configureSession(true);
|
||||
// Resume unfinished torrents
|
||||
|
@ -141,7 +146,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
|
|||
// RSS tab
|
||||
rssWidget = new RSSImp();
|
||||
tabs->addTab(rssWidget, tr("RSS"));
|
||||
tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss.png")));
|
||||
tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss32.png")));
|
||||
readSettings();
|
||||
// Add torrent given on command line
|
||||
processParams(torrentCmdLine);
|
||||
|
@ -155,10 +160,6 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
|
|||
checkConnect = new QTimer(this);
|
||||
connect(checkConnect, SIGNAL(timeout()), this, SLOT(checkConnectionStatus()));
|
||||
checkConnect->start(5000);
|
||||
// Start download list refresher
|
||||
refresher = new QTimer(this);
|
||||
connect(refresher, SIGNAL(timeout()), this, SLOT(updateLists()));
|
||||
refresher->start(1500);
|
||||
previewProcess = new QProcess(this);
|
||||
connect(previewProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanTempPreviewFile(int, QProcess::ExitStatus)));
|
||||
// Accept drag 'n drops
|
||||
|
@ -243,7 +244,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
|
|||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool show_msg = true;
|
||||
QString fileName = h.name();
|
||||
int useOSD = settings.value(QString::fromUtf8("Options/OSDEnabled"), 1).toInt();
|
||||
bool useNotificationBalloons = settings.value(QString::fromUtf8("Preferences/General/NotificationBaloons"), true).toBool();
|
||||
// Add it to finished tab
|
||||
QString hash = h.hash();
|
||||
if(QFile::exists(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".finished"))) {
|
||||
|
@ -254,7 +255,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
|
|||
downloadingTorrentTab->setInfoBar(tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName));
|
||||
downloadingTorrentTab->deleteTorrent(hash);
|
||||
finishedTorrentTab->addTorrent(hash);
|
||||
if(show_msg && systrayIntegration && (useOSD == 1 || (useOSD == 2 && (isMinimized() || isHidden())))) {
|
||||
if(show_msg && systrayIntegration && useNotificationBalloons) {
|
||||
myTrayIcon->showMessage(tr("Download finished"), tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName), QSystemTrayIcon::Information, TIME_TRAY_BALLOON);
|
||||
}
|
||||
}
|
||||
|
@ -262,8 +263,8 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
|
|||
// Notification when disk is full
|
||||
void GUI::fullDiskError(QTorrentHandle& h) const {
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
int useOSD = settings.value(QString::fromUtf8("Options/OSDEnabled"), 1).toInt();
|
||||
if(systrayIntegration && (useOSD == 1 || (useOSD == 2 && (isMinimized() || isHidden())))) {
|
||||
bool useNotificationBalloons = settings.value(QString::fromUtf8("Preferences/General/NotificationBaloons"), true).toBool();
|
||||
if(systrayIntegration && useNotificationBalloons) {
|
||||
myTrayIcon->showMessage(tr("I/O Error", "i.e: Input/Output Error"), tr("An error occured when trying to read or write %1. The disk is probably full, download has been paused", "e.g: An error occured when trying to read or write xxx.avi. The disk is probably full, download has been paused").arg(h.name()), QSystemTrayIcon::Critical, TIME_TRAY_BALLOON);
|
||||
}
|
||||
// Download will be paused by libtorrent. Updating GUI information accordingly
|
||||
|
@ -412,7 +413,7 @@ void GUI::previewFile(QString filePath) {
|
|||
QStringList params;
|
||||
params << tmpPath;
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString();
|
||||
QString previewProgram = settings.value(QString::fromUtf8("Preferences/General/MediaPlayer"), QString()).toString();
|
||||
previewProcess->start(previewProgram, params, QIODevice::ReadOnly);
|
||||
}else{
|
||||
QMessageBox::critical(0, tr("Preview process already running"), tr("There is already another preview process running.\nPlease close the other one first."));
|
||||
|
@ -475,13 +476,13 @@ void GUI::on_actionAbout_triggered() {
|
|||
void GUI::closeEvent(QCloseEvent *e) {
|
||||
qDebug("Mainwindow received closeEvent");
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool goToSystrayOnExit = settings.value(QString::fromUtf8("Options/Misc/Behaviour/GoToSystrayOnExit"), false).toBool();
|
||||
bool goToSystrayOnExit = settings.value(QString::fromUtf8("Preferences/General/CloseToTray"), false).toBool();
|
||||
if(!force_exit && systrayIntegration && goToSystrayOnExit && !this->isHidden()) {
|
||||
hide();
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
if(settings.value(QString::fromUtf8("Options/Misc/Behaviour/ConfirmOnExit"), true).toBool() && downloadingTorrentTab->getNbTorrentsInList()) {
|
||||
if(settings.value(QString::fromUtf8("Preferences/General/ExitConfirm"), true).toBool() && downloadingTorrentTab->getNbTorrentsInList()) {
|
||||
show();
|
||||
if(!isMaximized())
|
||||
showNormal();
|
||||
|
@ -511,13 +512,14 @@ void GUI::closeEvent(QCloseEvent *e) {
|
|||
|
||||
// Display window to create a torrent
|
||||
void GUI::on_actionCreate_torrent_triggered() {
|
||||
new createtorrent(this);
|
||||
createtorrent *ct = new createtorrent(this);
|
||||
connect(ct, SIGNAL(torrent_to_seed(QString)), this, SLOT(addTorrent(QString path)));
|
||||
}
|
||||
|
||||
// Called when we minimize the program
|
||||
void GUI::hideEvent(QHideEvent *e) {
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
if(systrayIntegration && settings.value(QString::fromUtf8("Options/Misc/Behaviour/GoToSystray"), true).toBool() && !e->spontaneous()) {
|
||||
if(systrayIntegration && settings.value(QString::fromUtf8("Preferences/General/MinimizeToTray"), false).toBool() && !e->spontaneous()) {
|
||||
// Hide window
|
||||
hide();
|
||||
}
|
||||
|
@ -528,13 +530,25 @@ void GUI::hideEvent(QHideEvent *e) {
|
|||
// Action executed when a file is dropped
|
||||
void GUI::dropEvent(QDropEvent *event) {
|
||||
event->acceptProposedAction();
|
||||
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
|
||||
QStringList files;
|
||||
if(event->mimeData()->hasUrls()) {
|
||||
QList<QUrl> urls = event->mimeData()->urls();
|
||||
QUrl url;
|
||||
foreach(url, urls) {
|
||||
QString tmp = url.toString();
|
||||
tmp.trimmed();
|
||||
if(!tmp.isEmpty())
|
||||
files << url.toString();
|
||||
}
|
||||
} else {
|
||||
files = event->mimeData()->text().split(QString::fromUtf8("\n"));
|
||||
}
|
||||
// Add file to download list
|
||||
QString file;
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
|
||||
foreach(file, files) {
|
||||
file = file.trimmed().replace(QString::fromUtf8("file://"), QString::fromUtf8(""));
|
||||
file = file.trimmed().replace(QString::fromUtf8("file://"), QString::fromUtf8(""), Qt::CaseInsensitive);
|
||||
qDebug("Dropped file %s on download list", file.toUtf8().data());
|
||||
if(file.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
|
||||
BTSession->downloadFromUrl(file);
|
||||
|
@ -553,7 +567,11 @@ void GUI::dropEvent(QDropEvent *event) {
|
|||
|
||||
// Decode if we accept drag 'n drop or not
|
||||
void GUI::dragEnterEvent(QDragEnterEvent *event) {
|
||||
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain"))) {
|
||||
QString mime;
|
||||
foreach(mime, event->mimeData()->formats()){
|
||||
qDebug("mimeData: %s", mime.toUtf8().data());
|
||||
}
|
||||
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
@ -567,16 +585,14 @@ void GUI::dragEnterEvent(QDragEnterEvent *event) {
|
|||
// Display a dialog to allow user to add
|
||||
// torrents to download list
|
||||
void GUI::on_actionOpen_triggered() {
|
||||
QStringList pathsList;
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
// Open File Open Dialog
|
||||
// Note: it is possible to select more than one file
|
||||
pathsList = QFileDialog::getOpenFileNames(0,
|
||||
QStringList pathsList = QFileDialog::getOpenFileNames(0,
|
||||
tr("Open Torrent Files"), settings.value(QString::fromUtf8("MainWindowLastDir"), QDir::homePath()).toString(),
|
||||
tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
||||
if(!pathsList.empty()) {
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
|
||||
unsigned int listSize = pathsList.size();
|
||||
for(unsigned int i=0; i<listSize; ++i) {
|
||||
if(useTorrentAdditionDialog) {
|
||||
|
@ -646,6 +662,16 @@ void GUI::on_actionDelete_Permanently_triggered() {
|
|||
}
|
||||
}
|
||||
|
||||
void GUI::deleteTorrent(QString hash, QString fileName, bool finished) {
|
||||
if(finished) {
|
||||
finishedTorrentTab->deleteTorrent(hash);
|
||||
} else {
|
||||
downloadingTorrentTab->deleteTorrent(hash);
|
||||
}
|
||||
// Update info bar
|
||||
downloadingTorrentTab->setInfoBar(tr("'%1' was removed because its ratio reached the maximum value you set.", "%1 is a file name").arg(fileName));
|
||||
}
|
||||
|
||||
// delete selected items in the list
|
||||
void GUI::on_actionDelete_triggered() {
|
||||
QStringList hashes;
|
||||
|
@ -708,7 +734,7 @@ void GUI::on_actionDelete_triggered() {
|
|||
void GUI::processParams(const QStringList& params) {
|
||||
QString param;
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
|
||||
foreach(param, params) {
|
||||
param = param.trimmed();
|
||||
if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
|
||||
|
@ -717,7 +743,7 @@ void GUI::processParams(const QStringList& params) {
|
|||
if(useTorrentAdditionDialog) {
|
||||
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
|
||||
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
|
||||
dialog->showLoad(param);
|
||||
}else{
|
||||
BTSession->addTorrent(param);
|
||||
|
@ -726,15 +752,19 @@ void GUI::processParams(const QStringList& params) {
|
|||
}
|
||||
}
|
||||
|
||||
void GUI::addTorrent(QString path) {
|
||||
BTSession->addTorrent(path);
|
||||
}
|
||||
|
||||
void GUI::processScannedFiles(const QStringList& params) {
|
||||
QString param;
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
|
||||
foreach(param, params) {
|
||||
if(useTorrentAdditionDialog) {
|
||||
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
|
||||
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
|
||||
dialog->showLoad(param, true);
|
||||
}else{
|
||||
BTSession->addTorrent(param, true);
|
||||
|
@ -744,11 +774,11 @@ void GUI::processScannedFiles(const QStringList& params) {
|
|||
|
||||
void GUI::processDownloadedFiles(QString path, QString url) {
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
|
||||
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
|
||||
if(useTorrentAdditionDialog) {
|
||||
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
|
||||
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString)));
|
||||
connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
|
||||
dialog->showLoad(path, false, url);
|
||||
}else{
|
||||
BTSession->addTorrent(path, false, url);
|
||||
|
@ -758,92 +788,67 @@ void GUI::processDownloadedFiles(QString path, QString url) {
|
|||
// Set BT session configuration
|
||||
void GUI::configureSession(bool deleteOptions) {
|
||||
qDebug("Configuring session");
|
||||
QPair<int, int> limits;
|
||||
unsigned short old_listenPort, new_listenPort;
|
||||
proxy_settings proxySettings;
|
||||
session_settings sessionSettings;
|
||||
pe_settings encryptionSettings;
|
||||
// Configure session regarding options
|
||||
// General
|
||||
displaySpeedInTitle = options->speedInTitleBar();
|
||||
unsigned int new_refreshInterval = options->getRefreshInterval();
|
||||
if(refreshInterval != new_refreshInterval) {
|
||||
refreshInterval = new_refreshInterval;
|
||||
refresher->start(refreshInterval);
|
||||
}
|
||||
// Downloads
|
||||
// * Save path
|
||||
BTSession->setDefaultSavePath(options->getSavePath());
|
||||
old_listenPort = BTSession->getListenPort();
|
||||
BTSession->preAllocateAllFiles(options->preAllocateAllFiles());
|
||||
BTSession->startTorrentsInPause(options->addTorrentsInPause());
|
||||
// * Scan dir
|
||||
if(options->getScanDir().isNull()) {
|
||||
BTSession->disableDirectoryScanning();
|
||||
}else{
|
||||
BTSession->enableDirectoryScanning(options->getScanDir());
|
||||
}
|
||||
// Connection
|
||||
// * Ports binding
|
||||
unsigned short old_listenPort = BTSession->getListenPort();
|
||||
BTSession->setListeningPortsRange(options->getPorts());
|
||||
new_listenPort = BTSession->getListenPort();
|
||||
unsigned short new_listenPort = BTSession->getListenPort();
|
||||
if(new_listenPort != old_listenPort) {
|
||||
downloadingTorrentTab->setInfoBar(tr("qBittorrent is bind to port: %1", "e.g: qBittorrent is bind to port: 1666").arg( misc::toQString(new_listenPort)));
|
||||
}
|
||||
// Apply max connec limit (-1 if disabled)
|
||||
BTSession->setMaxConnections(options->getMaxConnec());
|
||||
limits = options->getLimits();
|
||||
switch(limits.first) {
|
||||
case -1: // Download limit disabled
|
||||
case 0:
|
||||
BTSession->setDownloadRateLimit(-1);
|
||||
break;
|
||||
default:
|
||||
BTSession->setDownloadRateLimit(limits.first*1024);
|
||||
// * Global download limit
|
||||
QPair<int, int> limits = options->getGlobalBandwidthLimits();
|
||||
if(limits.first <= 0) {
|
||||
// Download limit disabled
|
||||
BTSession->setDownloadRateLimit(-1);
|
||||
} else {
|
||||
// Enabled
|
||||
BTSession->setDownloadRateLimit(limits.first*1024);
|
||||
}
|
||||
switch(limits.second) {
|
||||
case -1: // Upload limit disabled
|
||||
case 0:
|
||||
BTSession->setUploadRateLimit(-1);
|
||||
break;
|
||||
default:
|
||||
BTSession->setUploadRateLimit(limits.second*1024);
|
||||
// * Global Upload limit
|
||||
if(limits.second <= 0) {
|
||||
// Upload limit disabled
|
||||
BTSession->setUploadRateLimit(-1);
|
||||
} else {
|
||||
// Enabled
|
||||
BTSession->setUploadRateLimit(limits.second*1024);
|
||||
}
|
||||
// Apply ratio (0 if disabled)
|
||||
BTSession->setGlobalRatio(options->getRatio());
|
||||
// DHT (Trackerless)
|
||||
if(options->isDHTEnabled()) {
|
||||
downloadingTorrentTab->setInfoBar(tr("DHT support [ON], port: %1").arg(options->getDHTPort()), QString::fromUtf8("blue"));
|
||||
BTSession->enableDHT();
|
||||
// Set DHT Port
|
||||
BTSession->setDHTPort(options->getDHTPort());
|
||||
}else{
|
||||
downloadingTorrentTab->setInfoBar(tr("DHT support [OFF]"), QString::fromUtf8("blue"));
|
||||
BTSession->disableDHT();
|
||||
// * UPnP
|
||||
if(options->isUPnPEnabled()) {
|
||||
BTSession->enableUPnP(true);
|
||||
downloadingTorrentTab->setInfoBar(tr("UPnP support [ON]"), QString::fromUtf8("blue"));
|
||||
} else {
|
||||
BTSession->enableUPnP(false);
|
||||
downloadingTorrentTab->setInfoBar(tr("UPnP support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
// UPnP can't be disabled
|
||||
downloadingTorrentTab->setInfoBar(tr("UPnP support [ON]"), QString::fromUtf8("blue"));
|
||||
// Encryption settings
|
||||
int encryptionState = options->getEncryptionSetting();
|
||||
// The most secure, rc4 only so that all streams and encrypted
|
||||
encryptionSettings.allowed_enc_level = pe_settings::rc4;
|
||||
encryptionSettings.prefer_rc4 = true;
|
||||
switch(encryptionState) {
|
||||
case 0: //Enabled
|
||||
encryptionSettings.out_enc_policy = pe_settings::enabled;
|
||||
encryptionSettings.in_enc_policy = pe_settings::enabled;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [ON]"), QString::fromUtf8("blue"));
|
||||
break;
|
||||
case 1: // Forced
|
||||
encryptionSettings.out_enc_policy = pe_settings::forced;
|
||||
encryptionSettings.in_enc_policy = pe_settings::forced;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [FORCED]"), QString::fromUtf8("blue"));
|
||||
break;
|
||||
default: // Disabled
|
||||
encryptionSettings.out_enc_policy = pe_settings::disabled;
|
||||
encryptionSettings.in_enc_policy = pe_settings::disabled;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [OFF]"), QString::fromUtf8("blue"));
|
||||
// * NAT-PMP
|
||||
if(options->isNATPMPEnabled()) {
|
||||
BTSession->enableNATPMP(true);
|
||||
downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [ON]"), QString::fromUtf8("blue"));
|
||||
} else {
|
||||
BTSession->enableNATPMP(false);
|
||||
downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
BTSession->applyEncryptionSettings(encryptionSettings);
|
||||
// PeX
|
||||
if(!options->isPeXDisabled()) {
|
||||
qDebug("Enabling Peer eXchange (PeX)");
|
||||
downloadingTorrentTab->setInfoBar(tr("PeX support [ON]"), QString::fromUtf8("blue"));
|
||||
BTSession->enablePeerExchange();
|
||||
}else{
|
||||
downloadingTorrentTab->setInfoBar(tr("PeX support [OFF]"), QString::fromUtf8("blue"));
|
||||
qDebug("Peer eXchange (PeX) disabled");
|
||||
}
|
||||
// Apply filtering settings
|
||||
if(options->isFilteringEnabled()) {
|
||||
BTSession->enableIPFilter(options->getFilter());
|
||||
downloadingTorrentTab->setBottomTabEnabled(1, true);
|
||||
}else{
|
||||
BTSession->disableIPFilter();
|
||||
downloadingTorrentTab->setBottomTabEnabled(1, false);
|
||||
}
|
||||
// Apply Proxy settings
|
||||
// * Proxy settings
|
||||
proxy_settings proxySettings;
|
||||
if(options->isProxyEnabled()) {
|
||||
switch(options->getProxyType()) {
|
||||
case HTTP:
|
||||
|
@ -867,14 +872,79 @@ void GUI::configureSession(bool deleteOptions) {
|
|||
}
|
||||
}
|
||||
BTSession->setProxySettings(proxySettings, options->useProxyForTrackers(), options->useProxyForPeers(), options->useProxyForWebseeds(), options->useProxyForDHT());
|
||||
// * Session settings
|
||||
session_settings sessionSettings;
|
||||
sessionSettings.user_agent = "qBittorrent "VERSION;
|
||||
BTSession->setSessionSettings(sessionSettings);
|
||||
// Scan dir stuff
|
||||
if(options->getScanDir().isNull()) {
|
||||
BTSession->disableDirectoryScanning();
|
||||
// Bittorrent
|
||||
// * Max connections limit
|
||||
BTSession->setMaxConnections(options->getMaxConnecs());
|
||||
// * Max connections per torrent limit
|
||||
BTSession->setMaxConnectionsPerTorrent(options->getMaxConnecsPerTorrent());
|
||||
// * Max uploads per torrent limit
|
||||
BTSession->setMaxUploadsPerTorrent(options->getMaxUploadsPerTorrent());
|
||||
// * DHT
|
||||
if(options->isDHTEnabled()) {
|
||||
BTSession->enableDHT(true);
|
||||
downloadingTorrentTab->setInfoBar(tr("DHT support [ON], port: %1").arg(new_listenPort), QString::fromUtf8("blue"));
|
||||
// Set DHT Port
|
||||
BTSession->setDHTPort(new_listenPort);
|
||||
}else{
|
||||
BTSession->enableDirectoryScanning(options->getScanDir());
|
||||
BTSession->enableDHT(false);
|
||||
downloadingTorrentTab->setInfoBar(tr("DHT support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
// * PeX
|
||||
if(options->isPeXEnabled()) {
|
||||
downloadingTorrentTab->setInfoBar(tr("PeX support [ON]"), QString::fromUtf8("blue"));
|
||||
BTSession->enablePeerExchange();
|
||||
}else{
|
||||
// TODO: How can we remove the extension?
|
||||
downloadingTorrentTab->setInfoBar(tr("PeX support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
// * LSD
|
||||
if(options->isLSDEnabled()) {
|
||||
BTSession->enableLSD(true);
|
||||
downloadingTorrentTab->setInfoBar(tr("Local Peer Discovery [ON]"), QString::fromUtf8("blue"));
|
||||
} else {
|
||||
BTSession->enableLSD(false);
|
||||
downloadingTorrentTab->setInfoBar(tr("Local Peer Discovery support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
// * Encryption
|
||||
int encryptionState = options->getEncryptionSetting();
|
||||
// The most secure, rc4 only so that all streams and encrypted
|
||||
pe_settings encryptionSettings;
|
||||
encryptionSettings.allowed_enc_level = pe_settings::rc4;
|
||||
encryptionSettings.prefer_rc4 = true;
|
||||
switch(encryptionState) {
|
||||
case 0: //Enabled
|
||||
encryptionSettings.out_enc_policy = pe_settings::enabled;
|
||||
encryptionSettings.in_enc_policy = pe_settings::enabled;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [ON]"), QString::fromUtf8("blue"));
|
||||
break;
|
||||
case 1: // Forced
|
||||
encryptionSettings.out_enc_policy = pe_settings::forced;
|
||||
encryptionSettings.in_enc_policy = pe_settings::forced;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [FORCED]"), QString::fromUtf8("blue"));
|
||||
break;
|
||||
default: // Disabled
|
||||
encryptionSettings.out_enc_policy = pe_settings::disabled;
|
||||
encryptionSettings.in_enc_policy = pe_settings::disabled;
|
||||
downloadingTorrentTab->setInfoBar(tr("Encryption support [OFF]"), QString::fromUtf8("blue"));
|
||||
}
|
||||
BTSession->applyEncryptionSettings(encryptionSettings);
|
||||
// * Desired ratio
|
||||
BTSession->setGlobalRatio(options->getDesiredRatio());
|
||||
// * Maximum ratio
|
||||
BTSession->setDeleteRatio(options->getDeleteRatio());
|
||||
// Ip Filter
|
||||
if(options->isFilteringEnabled()) {
|
||||
BTSession->enableIPFilter(options->getFilter());
|
||||
downloadingTorrentTab->setBottomTabEnabled(1, true);
|
||||
}else{
|
||||
BTSession->disableIPFilter();
|
||||
downloadingTorrentTab->setBottomTabEnabled(1, false);
|
||||
}
|
||||
// Clean up
|
||||
if(deleteOptions) {
|
||||
delete options;
|
||||
}
|
||||
|
@ -919,23 +989,40 @@ void GUI::togglePausedState(QString hash) {
|
|||
void GUI::on_actionPause_All_triggered() {
|
||||
bool change = false;
|
||||
bool inDownloadList = true;
|
||||
if(tabs->currentIndex() > 1) return;
|
||||
if(tabs->currentIndex() == 1)
|
||||
inDownloadList = false;
|
||||
QStringList hashes;
|
||||
if(inDownloadList) {
|
||||
hashes = BTSession->getUnfinishedTorrents();
|
||||
} else {
|
||||
hashes = BTSession->getFinishedTorrents();
|
||||
bool hidden = false;
|
||||
switch(getCurrentTabIndex()) {
|
||||
case -1:
|
||||
hidden = true;
|
||||
inDownloadList = false;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
inDownloadList = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList DL_hashes;
|
||||
QStringList F_hashes;
|
||||
if(hidden || inDownloadList) {
|
||||
DL_hashes = BTSession->getUnfinishedTorrents();
|
||||
}
|
||||
if(hidden || !inDownloadList) {
|
||||
F_hashes = BTSession->getFinishedTorrents();
|
||||
}
|
||||
QString hash;
|
||||
foreach(hash, hashes) {
|
||||
foreach(hash, DL_hashes) {
|
||||
if(BTSession->pauseTorrent(hash)){
|
||||
change = true;
|
||||
if(inDownloadList)
|
||||
downloadingTorrentTab->pauseTorrent(hash);
|
||||
else
|
||||
finishedTorrentTab->pauseTorrent(hash);
|
||||
downloadingTorrentTab->pauseTorrent(hash);
|
||||
}
|
||||
}
|
||||
foreach(hash, F_hashes) {
|
||||
if(BTSession->pauseTorrent(hash)){
|
||||
change = true;
|
||||
finishedTorrentTab->pauseTorrent(hash);
|
||||
}
|
||||
}
|
||||
if(change)
|
||||
|
@ -971,23 +1058,40 @@ void GUI::on_actionPause_triggered() {
|
|||
void GUI::on_actionStart_All_triggered() {
|
||||
bool change = false;
|
||||
bool inDownloadList = true;
|
||||
if(tabs->currentIndex() > 1) return;
|
||||
if(tabs->currentIndex() == 1)
|
||||
inDownloadList = false;
|
||||
QStringList hashes;
|
||||
if(inDownloadList) {
|
||||
hashes = BTSession->getUnfinishedTorrents();
|
||||
} else {
|
||||
hashes = BTSession->getFinishedTorrents();
|
||||
bool hidden = false;
|
||||
switch(getCurrentTabIndex()) {
|
||||
case -1:
|
||||
hidden = true;
|
||||
inDownloadList = false;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
inDownloadList = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList DL_hashes;
|
||||
QStringList F_hashes;
|
||||
if(hidden || inDownloadList) {
|
||||
DL_hashes = BTSession->getUnfinishedTorrents();
|
||||
}
|
||||
if(hidden || !inDownloadList) {
|
||||
F_hashes = BTSession->getFinishedTorrents();
|
||||
}
|
||||
QString hash;
|
||||
foreach(hash, hashes) {
|
||||
foreach(hash, DL_hashes) {
|
||||
if(BTSession->resumeTorrent(hash)){
|
||||
change = true;
|
||||
if(inDownloadList)
|
||||
downloadingTorrentTab->resumeTorrent(hash);
|
||||
else
|
||||
finishedTorrentTab->resumeTorrent(hash);
|
||||
downloadingTorrentTab->resumeTorrent(hash);
|
||||
}
|
||||
}
|
||||
foreach(hash, F_hashes) {
|
||||
if(BTSession->resumeTorrent(hash)){
|
||||
change = true;
|
||||
finishedTorrentTab->resumeTorrent(hash);
|
||||
}
|
||||
}
|
||||
if(change)
|
||||
|
@ -1049,6 +1153,11 @@ void GUI::updateLists() {
|
|||
default:
|
||||
return;
|
||||
}
|
||||
if(displaySpeedInTitle) {
|
||||
QString dl_rate = QByteArray::number(BTSession->getSessionStatus().payload_download_rate/1024, 'f', 1);
|
||||
QString up_rate = QByteArray::number(BTSession->getSessionStatus().payload_upload_rate/1024, 'f', 1);
|
||||
setWindowTitle(tr("qBittorrent %1 (DL: %2KiB/s, UP: %3KiB/s)", "%1 is qBittorrent version").arg(QString::fromUtf8(VERSION)).arg(dl_rate).arg(up_rate));
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a tracker requires authentication
|
||||
|
@ -1067,7 +1176,16 @@ void GUI::checkConnectionStatus() {
|
|||
downloadingTorrentTab->updateRatio();
|
||||
// update global informations
|
||||
if(systrayIntegration) {
|
||||
myTrayIcon->setToolTip(QString::fromUtf8("<b>")+tr("qBittorrent")+QString::fromUtf8("</b><br>")+tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1)))+QString::fromUtf8("<br>")+tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1)))); // tray icon
|
||||
QString html = "<div style='background-color: #678db2; color: #fff;height: 18px; font-weight: bold; margin-bottom: 5px;'>";
|
||||
html += tr("qBittorrent");
|
||||
html += "</div>";
|
||||
html += "<div style='vertical-align: baseline; height: 18px;'>";
|
||||
html += "<img src=':/Icons/skin/downloading.png'/> "+tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1)));
|
||||
html += "</div>";
|
||||
html += "<div style='vertical-align: baseline; height: 18px;'>";
|
||||
html += "<img src=':/Icons/skin/seeding.png'/> "+tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1)));
|
||||
html += "</div>";
|
||||
myTrayIcon->setToolTip(html); // tray icon
|
||||
}
|
||||
session_status sessionStatus = BTSession->getSessionStatus();
|
||||
if(sessionStatus.has_incoming_connections) {
|
||||
|
@ -1134,7 +1252,7 @@ void GUI::on_actionOptions_triggered() {
|
|||
|
||||
// Is executed each time options are saved
|
||||
void GUI::OptionsSaved(QString info, bool deleteOptions) {
|
||||
bool newSystrayIntegration = options->useSystrayIntegration();
|
||||
bool newSystrayIntegration = options->systrayIntegration();
|
||||
if(newSystrayIntegration && !systrayIntegration) {
|
||||
// create the trayicon
|
||||
createTrayIcon();
|
||||
|
|
|
@ -64,7 +64,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||
FinishedTorrents *finishedTorrentTab;
|
||||
QLabel *connecStatusLblIcon;
|
||||
bool systrayIntegration;
|
||||
bool displaySpeedInTitle;
|
||||
bool force_exit;
|
||||
unsigned int refreshInterval;
|
||||
QTimer *refresher;
|
||||
// Keyboard shortcuts
|
||||
QShortcut *switchSearchShortcut;
|
||||
|
@ -127,10 +129,12 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||
void checkConnectionStatus();
|
||||
void configureSession(bool deleteOptions);
|
||||
void processParams(const QStringList& params);
|
||||
void addTorrent(QString path);
|
||||
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
|
||||
void processScannedFiles(const QStringList& params);
|
||||
void processDownloadedFiles(QString path, QString url);
|
||||
void downloadFromURLList(const QStringList& urls);
|
||||
void deleteTorrent(QString hash, QString fileName, bool finished);
|
||||
void finishedTorrent(QTorrentHandle& h) const;
|
||||
void torrentChecked(QString hash) const;
|
||||
void updateLists();
|
||||
|
|
BIN
src/Icons/bt_settings.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/Icons/configure.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 820 B |
BIN
src/Icons/download.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.4 KiB |
BIN
src/Icons/file.png
Normal file
After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 998 B |
Before Width: | Height: | Size: 558 B |
BIN
src/Icons/folder.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
src/Icons/gear.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 948 B |
Before Width: | Height: | Size: 2.2 KiB |
BIN
src/Icons/rss16.png
Normal file
After Width: | Height: | Size: 607 B |
BIN
src/Icons/rss32.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
src/Icons/star.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/Icons/subscribe.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/Icons/subscribe16.png
Normal file
After Width: | Height: | Size: 739 B |
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 856 B |
BIN
src/Icons/unsubscribe.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/Icons/unsubscribe16.png
Normal file
After Width: | Height: | Size: 765 B |
|
@ -63,7 +63,8 @@ class PreviewListDelegate: public QItemDelegate {
|
|||
newopt.state |= QStyle::State_Enabled;
|
||||
newopt.textVisible = false;
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||
painter->setPen(QColor("Black"));
|
||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
||||
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
|
||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#define SIZE 1
|
||||
#define PROGRESS 2
|
||||
#define PRIORITY 3
|
||||
#define INDEX 4
|
||||
|
||||
#define IGNORED 0
|
||||
#define NORMAL 1
|
||||
|
@ -76,7 +77,8 @@ class PropListDelegate: public QItemDelegate {
|
|||
newopt.state |= QStyle::State_Enabled;
|
||||
newopt.textVisible = false;
|
||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||
painter->setPen(QColor("Black"));
|
||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
||||
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
|
||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
||||
break;
|
||||
}
|
||||
|
@ -115,7 +117,6 @@ class PropListDelegate: public QItemDelegate {
|
|||
|
||||
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const {
|
||||
if(index.column() != PRIORITY) return 0;
|
||||
if(onlyOneItem(index)) return 0;
|
||||
QComboBox* editor = new QComboBox(parent);
|
||||
editor->setFocusPolicy(Qt::StrongFocus);
|
||||
editor->addItem(tr("Ignored"));
|
||||
|
@ -128,6 +129,7 @@ class PropListDelegate: public QItemDelegate {
|
|||
void setEditorData(QWidget *editor, const QModelIndex &index) const {
|
||||
unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt();
|
||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||
qDebug("Set Editor data: Prio is %d", val);
|
||||
switch(val){
|
||||
case IGNORED:
|
||||
combobox->setCurrentIndex(0);
|
||||
|
@ -156,28 +158,11 @@ class PropListDelegate: public QItemDelegate {
|
|||
return textRect.size();
|
||||
}
|
||||
|
||||
bool onlyOneItem(const QModelIndex& index) const {
|
||||
const QAbstractItemModel *model = index.model();
|
||||
unsigned int nbRows = model->rowCount();
|
||||
if(nbRows == 1) return true;
|
||||
for(unsigned int i=0; i<nbRows; ++i){
|
||||
if((unsigned int)index.row() == i) continue;
|
||||
if(model->data(model->index(i, PRIORITY)).toInt()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||
int value = combobox->currentIndex();
|
||||
qDebug("Setting combobox value in index: %d", value);
|
||||
QString color;
|
||||
if(value) {
|
||||
color = QString::fromUtf8("green");
|
||||
} else {
|
||||
color = QString::fromUtf8("red");
|
||||
}
|
||||
unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt();
|
||||
switch(value){
|
||||
case 0:
|
||||
|
@ -185,6 +170,10 @@ class PropListDelegate: public QItemDelegate {
|
|||
model->setData(index, QVariant(IGNORED));
|
||||
if(filteredFilesChanged != 0)
|
||||
*filteredFilesChanged = true;
|
||||
} else {
|
||||
// XXX: hack to force the model to send the itemChanged() signal
|
||||
model->setData(index, QVariant(NORMAL));
|
||||
model->setData(index, QVariant(IGNORED));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
|
@ -192,6 +181,9 @@ class PropListDelegate: public QItemDelegate {
|
|||
model->setData(index, QVariant(NORMAL));
|
||||
if(filteredFilesChanged != 0)
|
||||
*filteredFilesChanged = true;
|
||||
} else {
|
||||
model->setData(index, QVariant(HIGH));
|
||||
model->setData(index, QVariant(NORMAL));
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
|
@ -199,6 +191,9 @@ class PropListDelegate: public QItemDelegate {
|
|||
model->setData(index, QVariant(HIGH));
|
||||
if(filteredFilesChanged != 0)
|
||||
*filteredFilesChanged = true;
|
||||
} else {
|
||||
model->setData(index, QVariant(NORMAL));
|
||||
model->setData(index, QVariant(HIGH));
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
|
@ -206,6 +201,9 @@ class PropListDelegate: public QItemDelegate {
|
|||
model->setData(index, QVariant(MAXIMUM));
|
||||
if(filteredFilesChanged != 0)
|
||||
*filteredFilesChanged = true;
|
||||
} else {
|
||||
model->setData(index, QVariant(HIGH));
|
||||
model->setData(index, QVariant(MAXIMUM));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -213,16 +211,17 @@ class PropListDelegate: public QItemDelegate {
|
|||
model->setData(index, QVariant(NORMAL));
|
||||
if(filteredFilesChanged != 0)
|
||||
*filteredFilesChanged = true;
|
||||
} else {
|
||||
model->setData(index, QVariant(HIGH));
|
||||
model->setData(index, QVariant(NORMAL));
|
||||
}
|
||||
}
|
||||
for(int i=0; i<model->columnCount(); ++i){
|
||||
model->setData(model->index(index.row(), i), QVariant(QColor(color)), Qt::ForegroundRole);
|
||||
}
|
||||
}
|
||||
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,9 +44,9 @@ class about : public QDialog, private Ui::AboutDlg{
|
|||
// Thanks
|
||||
te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
|
||||
te_thanks->append(QString::fromUtf8("<ul><li>I would like to thank sourceforge.net for hosting qBittorrent project.</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière joined the project as a programmer. His help is greatly appreciated</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, webdevelopper and RPM packager, for his help.</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I am gratefull to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière is contributing to the project as a developer. His help is greatly appreciated</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, RPM packager, for his help and support.</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>"));
|
||||
te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
|
||||
te_thanks->scrollToAnchor(QString::fromUtf8("top"));
|
||||
// Translation
|
||||
|
|
|
@ -5,20 +5,29 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>440</width>
|
||||
<height>389</height>
|
||||
<width>511</width>
|
||||
<height>441</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Torrent addition dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="fileNameLbl" >
|
||||
<property name="text" >
|
||||
|
@ -48,12 +57,21 @@
|
|||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="savePathTxt" />
|
||||
</item>
|
||||
|
@ -108,12 +126,21 @@
|
|||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
|
|
|
@ -157,13 +157,13 @@ class BandwidthAllocationDialog : public QDialog, private Ui_bandwidth_dlg {
|
|||
s->set_upload_rate_limit(-1);
|
||||
else
|
||||
s->set_upload_rate_limit(val*1024);
|
||||
settings.setValue(QString::fromUtf8("Options/Main/UPLimit"), val);
|
||||
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalUPLimit"), val);
|
||||
}else{
|
||||
if(!val)
|
||||
s->set_download_rate_limit(-1);
|
||||
else
|
||||
s->set_download_rate_limit(val*1024);
|
||||
settings.setValue(QString::fromUtf8("Options/Main/DLLimit"), val);
|
||||
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalDLLimit"), val);
|
||||
}
|
||||
}
|
||||
close();
|
||||
|
|
256
src/arborescence.h
Normal file
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ARBORESCENCE_H
|
||||
#define ARBORESCENCE_H
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QStringList>
|
||||
#include <QDir>
|
||||
#include "misc.h"
|
||||
|
||||
class file {
|
||||
private:
|
||||
file *parent;
|
||||
bool is_dir;
|
||||
QString rel_path;
|
||||
QList<file*> children;
|
||||
size_type size;
|
||||
float progress;
|
||||
int priority;
|
||||
int index; // Index in torrent_info
|
||||
|
||||
public:
|
||||
file(file *parent, QString path, bool dir, size_type size=0, int index=-1, float progress=0., int priority=1): parent(parent), is_dir(dir), size(size), progress(progress), priority(priority), index(index){
|
||||
qDebug("created a file with index %d", index);
|
||||
rel_path = QDir::cleanPath(path);
|
||||
if(parent) {
|
||||
parent->updateProgress();
|
||||
parent->updatePriority(priority);
|
||||
}
|
||||
}
|
||||
|
||||
~file() {
|
||||
qDeleteAll(children);
|
||||
}
|
||||
|
||||
QString path() const {
|
||||
return rel_path;
|
||||
}
|
||||
|
||||
QString name() const {
|
||||
return rel_path.split(QDir::separator()).last();
|
||||
}
|
||||
|
||||
void updateProgress() {
|
||||
Q_ASSERT(is_dir);
|
||||
float sum = 0;
|
||||
file *child;
|
||||
foreach(child, children) {
|
||||
sum += child->getProgress();
|
||||
}
|
||||
progress = sum / (float)children.size();
|
||||
}
|
||||
|
||||
void updatePriority(int prio) {
|
||||
Q_ASSERT(is_dir);
|
||||
file *child;
|
||||
foreach(child, children) {
|
||||
if(child->getPriority() != prio) return;
|
||||
}
|
||||
priority = prio;
|
||||
}
|
||||
|
||||
int getPriority() const {
|
||||
return priority;
|
||||
}
|
||||
|
||||
size_type getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
float getProgress() const {
|
||||
return progress;
|
||||
}
|
||||
|
||||
int getIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
bool isDir() const {
|
||||
return is_dir;
|
||||
}
|
||||
|
||||
bool hasChildren() const {
|
||||
return (!children.isEmpty());
|
||||
}
|
||||
|
||||
QList<file*> getChildren() const {
|
||||
return children;
|
||||
}
|
||||
|
||||
file* getChild(QString fileName) const {
|
||||
Q_ASSERT(is_dir);
|
||||
file* f;
|
||||
foreach(f, children) {
|
||||
if(f->name() == fileName) return f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void addBytes(size_type b) {
|
||||
size += b;
|
||||
if(parent)
|
||||
parent->addBytes(b);
|
||||
}
|
||||
|
||||
file* addChild(QString fileName, bool dir, size_type size=0, int index = -1, float progress=0., int priority=1) {
|
||||
Q_ASSERT(is_dir);
|
||||
qDebug("Adding a new child of size: %ld", (long)size);
|
||||
file *f = new file(this, QDir::cleanPath(rel_path+QDir::separator()+fileName), dir, size, index, progress, priority);
|
||||
children << f;
|
||||
if(size) {
|
||||
addBytes(size);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
bool removeFromFS(QString saveDir) {
|
||||
QString full_path = saveDir + QDir::separator() + rel_path;
|
||||
if(!QFile::exists(full_path)) {
|
||||
qDebug("%s does not exist, no need to remove it", full_path.toUtf8().data());
|
||||
return true;
|
||||
}
|
||||
bool success = true;
|
||||
file *f;
|
||||
qDebug("We have %d children", children.size());
|
||||
foreach(f, children) {
|
||||
bool s = f->removeFromFS(saveDir);
|
||||
success = s && success;
|
||||
}
|
||||
if(is_dir) {
|
||||
qDebug("trying to remove directory: %s", full_path.toUtf8().data());
|
||||
QDir dir(full_path);
|
||||
dir.rmdir(full_path);
|
||||
} else {
|
||||
qDebug("trying to remove file: %s", full_path.toUtf8().data());
|
||||
bool s = QFile::remove(full_path);
|
||||
success = s && success;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
class arborescence {
|
||||
private:
|
||||
file *root;
|
||||
|
||||
public:
|
||||
arborescence(torrent_info t) {
|
||||
torrent_info::file_iterator fi = t.begin_files();
|
||||
if(t.num_files() > 1) {
|
||||
root = new file(0, misc::toQString(t.name()), true);
|
||||
} else {
|
||||
// XXX: Will crash if there is no file in torrent
|
||||
root = new file(0, misc::toQString(t.name()), false, fi->size, 0);
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
while(fi != t.end_files()) {
|
||||
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
|
||||
addFile(path, fi->size, i);
|
||||
fi++;
|
||||
++i;
|
||||
}
|
||||
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
|
||||
Q_ASSERT(root->getSize() == t.total_size());
|
||||
}
|
||||
|
||||
arborescence(torrent_info t, std::vector<float> fp, int *prioritiesTab) {
|
||||
torrent_info::file_iterator fi = t.begin_files();
|
||||
if(t.num_files() > 1) {
|
||||
qDebug("More than one file in the torrent, setting a folder as root");
|
||||
root = new file(0, misc::toQString(t.name()), true);
|
||||
} else {
|
||||
// XXX: Will crash if there is no file in torrent
|
||||
qDebug("one file in the torrent, setting it as root with index 0");
|
||||
root = new file(0, misc::toQString(t.name()), false, fi->size, 0, fp[0], prioritiesTab[0]);
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
while(fi != t.end_files()) {
|
||||
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
|
||||
addFile(path, fi->size, i, fp[i], prioritiesTab[i]);
|
||||
fi++;
|
||||
++i;
|
||||
}
|
||||
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
|
||||
Q_ASSERT(root->getSize() == t.total_size());
|
||||
}
|
||||
|
||||
~arborescence() {
|
||||
delete root;
|
||||
}
|
||||
|
||||
file* getRoot() const {
|
||||
return root;
|
||||
}
|
||||
|
||||
bool removeFromFS(QString saveDir) {
|
||||
if(!QFile::exists(saveDir+QDir::separator()+root->path())) return true;
|
||||
bool success = root->removeFromFS(saveDir);
|
||||
QDir root_dir(root->path());
|
||||
root_dir.rmdir(saveDir+QDir::separator()+root->path());
|
||||
return success;
|
||||
}
|
||||
|
||||
protected:
|
||||
void addFile(QString path, size_type file_size, int index, float progress=0., int priority=1) {
|
||||
Q_ASSERT(root->isDir());
|
||||
path = QDir::cleanPath(path);
|
||||
Q_ASSERT(path.startsWith(root->path()));
|
||||
QString relative_path = path.remove(0, root->path().size());
|
||||
if(relative_path.at(0) ==QDir::separator())
|
||||
relative_path.remove(0, 1);
|
||||
QStringList fileNames = relative_path.split(QDir::separator());
|
||||
QString fileName;
|
||||
file *dad = root;
|
||||
unsigned int nb_i = 0;
|
||||
unsigned int size = fileNames.size();
|
||||
foreach(fileName, fileNames) {
|
||||
++nb_i;
|
||||
if(fileName == ".") continue;
|
||||
file* child = dad->getChild(fileName);
|
||||
if(!child) {
|
||||
if(nb_i != size) {
|
||||
// Folder
|
||||
child = dad->addChild(fileName, true);
|
||||
} else {
|
||||
// File
|
||||
child = dad->addChild(fileName, false, file_size, index, progress, priority);
|
||||
}
|
||||
}
|
||||
dad = child;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -24,6 +24,11 @@
|
|||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include "bittorrent.h"
|
||||
#include "misc.h"
|
||||
#include "downloadThread.h"
|
||||
#include "deleteThread.h"
|
||||
|
||||
#include <libtorrent/extensions/metadata_transfer.hpp>
|
||||
#include <libtorrent/extensions/ut_pex.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
@ -34,27 +39,18 @@
|
|||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <boost/filesystem/exception.hpp>
|
||||
|
||||
#include "bittorrent.h"
|
||||
#include "misc.h"
|
||||
#include "downloadThread.h"
|
||||
#include "deleteThread.h"
|
||||
|
||||
#define ETAS_MAX_VALUES 3
|
||||
#define ETA_REFRESH_INTERVAL 10000
|
||||
#define MAX_TRACKER_ERRORS 2
|
||||
|
||||
// Main constructor
|
||||
bittorrent::bittorrent() : timerScan(0), DHTEnabled(false){
|
||||
bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false), addInPause(false), maxConnecsPerTorrent(500), maxUploadsPerTorrent(4), max_ratio(-1) {
|
||||
// To avoid some exceptions
|
||||
fs::path::default_name_check(fs::no_check);
|
||||
// Creating bittorrent session
|
||||
s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0));
|
||||
// Set severity level of libtorrent session
|
||||
s->set_severity_level(alert::info);
|
||||
// Enable LSD/UPnP/NAT-PMP
|
||||
s->start_lsd();
|
||||
s->start_natpmp();
|
||||
s->start_upnp();
|
||||
// Enabling metadata plugin
|
||||
s->add_extension(&create_metadata_plugin);
|
||||
timerAlerts = new QTimer();
|
||||
|
@ -85,6 +81,43 @@ bittorrent::~bittorrent() {
|
|||
delete s;
|
||||
}
|
||||
|
||||
void bittorrent::preAllocateAllFiles(bool b) {
|
||||
preAllocateAll = b;
|
||||
if(b) {
|
||||
// Reload All Torrents
|
||||
std::vector<torrent_handle> handles = s->get_torrents();
|
||||
unsigned int nbHandles = handles.size();
|
||||
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||
QTorrentHandle h = handles[i];
|
||||
if(!h.is_valid()) {
|
||||
qDebug("/!\\ Error: Invalid handle");
|
||||
continue;
|
||||
}
|
||||
QString hash = h.hash();
|
||||
if(has_filtered_files(hash)) continue;
|
||||
reloadTorrent(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bittorrent::deleteBigRatios() {
|
||||
if(max_ratio == -1) return;
|
||||
QString hash;
|
||||
foreach(hash, finishedTorrents) {
|
||||
QTorrentHandle h = getTorrentHandle(hash);
|
||||
if(!h.is_valid()) {
|
||||
qDebug("/!\\ Error: Invalid handle");
|
||||
continue;
|
||||
}
|
||||
QString hash = h.hash();
|
||||
if(getRealRatio(hash) > max_ratio) {
|
||||
QString fileName = h.name();
|
||||
deleteTorrent(hash);
|
||||
emit torrent_deleted(hash, fileName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bittorrent::setDownloadLimit(QString hash, long val) {
|
||||
QTorrentHandle h = getTorrentHandle(hash);
|
||||
if(h.is_valid())
|
||||
|
@ -104,6 +137,10 @@ void bittorrent::handleDownloadFailure(QString url, QString reason) {
|
|||
emit downloadFromUrlFailure(url, reason);
|
||||
}
|
||||
|
||||
void bittorrent::startTorrentsInPause(bool b) {
|
||||
addInPause = b;
|
||||
}
|
||||
|
||||
void bittorrent::updateETAs() {
|
||||
QString hash;
|
||||
foreach(hash, unfinishedTorrents) {
|
||||
|
@ -118,18 +155,25 @@ void bittorrent::updateETAs() {
|
|||
if(h.download_payload_rate()) {
|
||||
listEtas << (qlonglong)((h.actual_size()-h.total_done())/(double)h.download_payload_rate());
|
||||
ETAstats[hash] = listEtas;
|
||||
long moy = 0;
|
||||
long val;
|
||||
qlonglong moy = 0;
|
||||
qlonglong val;
|
||||
unsigned int nbETAs = listEtas.size();
|
||||
Q_ASSERT(nbETAs);
|
||||
foreach(val, listEtas) {
|
||||
moy += (qlonglong)((double)val/(double)nbETAs);
|
||||
Q_ASSERT(moy >= 0);
|
||||
if(moy < 0) break;
|
||||
}
|
||||
if(moy < 0) {
|
||||
ETAs.remove(hash);
|
||||
} else {
|
||||
ETAs[hash] = moy;
|
||||
}
|
||||
ETAs[hash] = moy;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete big ratios
|
||||
if(max_ratio != -1)
|
||||
deleteBigRatios();
|
||||
}
|
||||
|
||||
long bittorrent::getETA(QString hash) const{
|
||||
|
@ -165,9 +209,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
|||
}
|
||||
QString savePath = h.save_path();
|
||||
QString fileName = h.name();
|
||||
QStringList files_path;
|
||||
arborescence *files_arb = 0;
|
||||
if(permanent){
|
||||
files_path = h.files_path();
|
||||
files_arb = new arborescence(h.get_torrent_info());
|
||||
}
|
||||
// Remove it from session
|
||||
s->remove_torrent(h.get_torrent_handle());
|
||||
|
@ -198,11 +242,11 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
|||
std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n";
|
||||
}
|
||||
}
|
||||
if(permanent) {
|
||||
if(permanent && files_arb != 0) {
|
||||
// Remove from Hard drive
|
||||
qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName));
|
||||
// Deleting in a thread to avoid GUI freeze
|
||||
deleter->deleteTorrent(savePath, files_path);
|
||||
deleter->deleteTorrent(savePath, files_arb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,11 +390,11 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||
}
|
||||
}
|
||||
// Processing torrents
|
||||
file = path.trimmed().replace("file://", "");
|
||||
file = path.trimmed().replace("file://", "", Qt::CaseInsensitive);
|
||||
if(file.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(!file.startsWith("http://") && !file.startsWith("https://") && !file.startsWith("ftp://"));
|
||||
Q_ASSERT(!file.startsWith("http://", Qt::CaseInsensitive) && !file.startsWith("https://", Qt::CaseInsensitive) && !file.startsWith("ftp://", Qt::CaseInsensitive));
|
||||
qDebug("Adding %s to download list", file.toUtf8().data());
|
||||
std::ifstream in(file.toUtf8().data(), std::ios_base::binary);
|
||||
in.unsetf(std::ios_base::skipws);
|
||||
|
@ -358,27 +402,18 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||
// Decode torrent file
|
||||
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
|
||||
// Getting torrent file informations
|
||||
torrent_info t(e);
|
||||
qDebug(" -> Hash: %s", misc::toString(t.info_hash()).c_str());
|
||||
qDebug(" -> Name: %s", t.name().c_str());
|
||||
QString hash = misc::toQString(t.info_hash());
|
||||
boost::intrusive_ptr<torrent_info> t(new torrent_info(e));
|
||||
qDebug(" -> Hash: %s", misc::toString(t->info_hash()).c_str());
|
||||
qDebug(" -> Name: %s", t->name().c_str());
|
||||
QString hash = misc::toQString(t->info_hash());
|
||||
if(file.startsWith(torrentBackup.path())) {
|
||||
QFileInfo fi(file);
|
||||
QString old_hash = fi.baseName();
|
||||
if(old_hash != hash){
|
||||
qDebug("* ERROR: Strange, hash changed from %s to %s", old_hash.toUtf8().data(), hash.toUtf8().data());
|
||||
// QStringList filters;
|
||||
// filters << old_hash+".*";
|
||||
// QStringList files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||
// QString my_f;
|
||||
// foreach(my_f, files) {
|
||||
// qDebug("* deleting %s", my_f.toUtf8().data());
|
||||
// torrentBackup.remove(my_f);
|
||||
// }
|
||||
// return;
|
||||
}
|
||||
}
|
||||
if(s->find_torrent(t.info_hash()).is_valid()) {
|
||||
if(s->find_torrent(t->info_hash()).is_valid()) {
|
||||
qDebug("/!\\ Torrent is already in download list");
|
||||
// Update info Bar
|
||||
if(!fromScanDir) {
|
||||
|
@ -409,7 +444,7 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||
}
|
||||
QString savePath = getSavePath(hash);
|
||||
// Adding files to bittorrent session
|
||||
if(has_filtered_files(hash)) {
|
||||
if(has_filtered_files(hash) || preAllocateAll) {
|
||||
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, false, true);
|
||||
qDebug(" -> Full allocation mode");
|
||||
}else{
|
||||
|
@ -423,9 +458,10 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||
if(!from_url.isNull()) QFile::remove(file);
|
||||
return;
|
||||
}
|
||||
// Is this really useful and appropriate ?
|
||||
//h.set_max_connections(60);
|
||||
h.set_max_uploads(-1);
|
||||
// Connections limit per torrent
|
||||
h.set_max_connections(maxConnecsPerTorrent);
|
||||
// Uploads limit per torrent
|
||||
h.set_max_uploads(maxUploadsPerTorrent);
|
||||
// Load filtered files
|
||||
loadFilesPriorities(h);
|
||||
// Load custom url seeds
|
||||
|
@ -450,13 +486,13 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||
QFile::copy(file, newFile);
|
||||
}
|
||||
// Pause torrent if it was paused last time
|
||||
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
|
||||
if(addInPause || QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
|
||||
torrentsToPauseAfterChecking << hash;
|
||||
qDebug("Adding a torrent to the torrentsToPauseAfterChecking list");
|
||||
}
|
||||
// Incremental download
|
||||
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".incremental")) {
|
||||
qDebug("Incremental download enabled for %s", t.name().c_str());
|
||||
qDebug("Incremental download enabled for %s", t->name().c_str());
|
||||
h.set_sequenced_download_threshold(1);
|
||||
}
|
||||
// Start torrent because it was added in paused state
|
||||
|
@ -538,35 +574,88 @@ void bittorrent::setMaxConnections(int maxConnec) {
|
|||
s->set_max_connections(maxConnec);
|
||||
}
|
||||
|
||||
void bittorrent::setMaxConnectionsPerTorrent(int max) {
|
||||
maxConnecsPerTorrent = max;
|
||||
// Apply this to all session torrents
|
||||
std::vector<torrent_handle> handles = s->get_torrents();
|
||||
unsigned int nbHandles = handles.size();
|
||||
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||
QTorrentHandle h = handles[i];
|
||||
if(!h.is_valid()) {
|
||||
qDebug("/!\\ Error: Invalid handle");
|
||||
continue;
|
||||
}
|
||||
h.set_max_connections(max);
|
||||
}
|
||||
}
|
||||
|
||||
void bittorrent::setMaxUploadsPerTorrent(int max) {
|
||||
maxUploadsPerTorrent = max;
|
||||
// Apply this to all session torrents
|
||||
std::vector<torrent_handle> handles = s->get_torrents();
|
||||
unsigned int nbHandles = handles.size();
|
||||
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||
QTorrentHandle h = handles[i];
|
||||
if(!h.is_valid()) {
|
||||
qDebug("/!\\ Error: Invalid handle");
|
||||
continue;
|
||||
}
|
||||
h.set_max_uploads(max);
|
||||
}
|
||||
}
|
||||
|
||||
// Return DHT state
|
||||
bool bittorrent::isDHTEnabled() const{
|
||||
return DHTEnabled;
|
||||
}
|
||||
|
||||
// Enable DHT
|
||||
void bittorrent::enableDHT() {
|
||||
if(!DHTEnabled) {
|
||||
boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary);
|
||||
dht_state_file.unsetf(std::ios_base::skipws);
|
||||
entry dht_state;
|
||||
try{
|
||||
dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>());
|
||||
}catch (std::exception&) {}
|
||||
s->start_dht(dht_state);
|
||||
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
|
||||
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
|
||||
s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881));
|
||||
DHTEnabled = true;
|
||||
qDebug("DHT enabled");
|
||||
void bittorrent::enableUPnP(bool b) {
|
||||
if(b) {
|
||||
s->start_upnp();
|
||||
} else {
|
||||
s->stop_upnp();
|
||||
}
|
||||
}
|
||||
|
||||
// Disable DHT
|
||||
void bittorrent::disableDHT() {
|
||||
if(DHTEnabled) {
|
||||
DHTEnabled = false;
|
||||
s->stop_dht();
|
||||
qDebug("DHT disabled");
|
||||
void bittorrent::enableNATPMP(bool b) {
|
||||
if(b) {
|
||||
s->start_natpmp();
|
||||
} else {
|
||||
s->stop_natpmp();
|
||||
}
|
||||
}
|
||||
|
||||
void bittorrent::enableLSD(bool b) {
|
||||
if(b) {
|
||||
s->start_lsd();
|
||||
} else {
|
||||
s->stop_lsd();
|
||||
}
|
||||
}
|
||||
|
||||
// Enable DHT
|
||||
void bittorrent::enableDHT(bool b) {
|
||||
if(b) {
|
||||
if(!DHTEnabled) {
|
||||
boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary);
|
||||
dht_state_file.unsetf(std::ios_base::skipws);
|
||||
entry dht_state;
|
||||
try{
|
||||
dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>());
|
||||
}catch (std::exception&) {}
|
||||
s->start_dht(dht_state);
|
||||
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
|
||||
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
|
||||
s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881));
|
||||
DHTEnabled = true;
|
||||
qDebug("DHT enabled");
|
||||
}
|
||||
} else {
|
||||
if(DHTEnabled) {
|
||||
DHTEnabled = false;
|
||||
s->stop_dht();
|
||||
qDebug("DHT disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -660,7 +749,11 @@ void bittorrent::loadDownloadUploadForTorrent(QString hash) {
|
|||
QPair<size_type,size_type> downUp;
|
||||
downUp.first = (size_type)data_list.at(0).toLongLong();
|
||||
downUp.second = (size_type)data_list.at(1).toLongLong();
|
||||
Q_ASSERT(downUp.first >= 0 && downUp.second >= 0);
|
||||
if(downUp.first < 0 || downUp.second < 0) {
|
||||
qDebug("** Overflow in ratio!!! fixing...");
|
||||
downUp.first = 0;
|
||||
downUp.second = 0;
|
||||
}
|
||||
ratioData[hash] = downUp;
|
||||
}
|
||||
|
||||
|
@ -865,6 +958,11 @@ void bittorrent::setUploadRateLimit(long rate) {
|
|||
// libtorrent allow to adjust ratio for each torrent
|
||||
// This function will apply to same ratio to all torrents
|
||||
void bittorrent::setGlobalRatio(float ratio) {
|
||||
if(ratio != -1 && ratio < 1.) ratio = 1.;
|
||||
if(ratio == -1) {
|
||||
// 0 means unlimited for libtorrent
|
||||
ratio = 0;
|
||||
}
|
||||
std::vector<torrent_handle> handles = s->get_torrents();
|
||||
unsigned int nbHandles = handles.size();
|
||||
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||
|
@ -877,6 +975,15 @@ void bittorrent::setGlobalRatio(float ratio) {
|
|||
}
|
||||
}
|
||||
|
||||
// Torrents will a ratio superior to the given value will
|
||||
// be automatically deleted
|
||||
void bittorrent::setDeleteRatio(float ratio) {
|
||||
if(ratio != -1 && ratio < 1.) ratio = 1.;
|
||||
max_ratio = ratio;
|
||||
qDebug("* Set deleteRatio to %.1f", max_ratio);
|
||||
deleteBigRatios();
|
||||
}
|
||||
|
||||
bool bittorrent::loadTrackerFile(QString hash) {
|
||||
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
||||
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
|
||||
|
@ -980,6 +1087,7 @@ void bittorrent::readAlerts() {
|
|||
}
|
||||
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) {
|
||||
QTorrentHandle h(p->handle);
|
||||
qDebug("File Error: %s", p->msg().c_str());
|
||||
if(h.is_valid())
|
||||
emit fullDiskError(h);
|
||||
}
|
||||
|
@ -1086,7 +1194,7 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
|
|||
fs::path saveDir = h.save_path_boost();
|
||||
QString fileName = h.name();
|
||||
QString hash = h.hash();
|
||||
torrent_info t = h.get_torrent_info();
|
||||
boost::intrusive_ptr<torrent_info> t(new torrent_info(h.get_torrent_info()));
|
||||
qDebug("Reloading torrent: %s", fileName.toUtf8().data());
|
||||
entry resumeData;
|
||||
// Checking if torrentBackup Dir exists
|
||||
|
@ -1109,8 +1217,10 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
|
|||
}
|
||||
QTorrentHandle new_h = s->add_torrent(t, saveDir, resumeData, false);
|
||||
qDebug("Using full allocation mode");
|
||||
|
||||
new_h.set_max_uploads(-1);
|
||||
// Connections limit per torrent
|
||||
new_h.set_max_connections(maxConnecsPerTorrent);
|
||||
// Uploads limit per torrent
|
||||
new_h.set_max_uploads(maxUploadsPerTorrent);
|
||||
// Load filtered Files
|
||||
loadFilesPriorities(new_h);
|
||||
// Load speed limit from hard drive
|
||||
|
|
|
@ -57,6 +57,11 @@ class bittorrent : public QObject{
|
|||
QStringList waitingForPause;
|
||||
QStringList finishedTorrents;
|
||||
QStringList unfinishedTorrents;
|
||||
bool preAllocateAll;
|
||||
bool addInPause;
|
||||
int maxConnecsPerTorrent;
|
||||
int maxUploadsPerTorrent;
|
||||
float max_ratio;
|
||||
|
||||
protected:
|
||||
QString getSavePath(QString hash);
|
||||
|
@ -90,9 +95,8 @@ class bittorrent : public QObject{
|
|||
void deleteTorrent(QString hash, bool permanent = false);
|
||||
bool pauseTorrent(QString hash);
|
||||
bool resumeTorrent(QString hash);
|
||||
void enableDHT();
|
||||
void disableDHT();
|
||||
void saveDHTEntry();
|
||||
void preAllocateAllFiles(bool b);
|
||||
void saveFastResumeAndRatioData();
|
||||
void enableDirectoryScanning(QString scan_dir);
|
||||
void disableDirectoryScanning();
|
||||
|
@ -111,12 +115,16 @@ class bittorrent : public QObject{
|
|||
// Session configuration - Setters
|
||||
void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports);
|
||||
void setMaxConnections(int maxConnec);
|
||||
void setMaxConnectionsPerTorrent(int max);
|
||||
void setMaxUploadsPerTorrent(int max);
|
||||
void setDownloadRateLimit(long rate);
|
||||
void setUploadRateLimit(long rate);
|
||||
void setGlobalRatio(float ratio);
|
||||
void setDeleteRatio(float ratio);
|
||||
void setDHTPort(int dht_port);
|
||||
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
|
||||
void setSessionSettings(session_settings sessionSettings);
|
||||
void startTorrentsInPause(bool b);
|
||||
void setDefaultSavePath(QString savepath);
|
||||
void applyEncryptionSettings(pe_settings se);
|
||||
void loadFilesPriorities(QTorrentHandle& h);
|
||||
|
@ -124,6 +132,10 @@ class bittorrent : public QObject{
|
|||
void setUploadLimit(QString hash, long val);
|
||||
void setUnfinishedTorrent(QString hash);
|
||||
void setFinishedTorrent(QString hash);
|
||||
void enableUPnP(bool b);
|
||||
void enableNATPMP(bool b);
|
||||
void enableLSD(bool b);
|
||||
void enableDHT(bool b);
|
||||
|
||||
protected slots:
|
||||
void scanDirectory();
|
||||
|
@ -132,6 +144,7 @@ class bittorrent : public QObject{
|
|||
bool loadTrackerFile(QString hash);
|
||||
void saveTrackerFile(QString hash);
|
||||
void reloadTorrent(const QTorrentHandle &h); // This is protected now, call pauseAndReloadTorrent() instead
|
||||
void deleteBigRatios();
|
||||
|
||||
signals:
|
||||
void invalidTorrent(QString path);
|
||||
|
@ -151,7 +164,7 @@ class bittorrent : public QObject{
|
|||
void fastResumeDataRejected(QString name);
|
||||
void urlSeedProblem(QString url, QString msg);
|
||||
void torrentFinishedChecking(QString hash);
|
||||
|
||||
void torrent_deleted(QString hash, QString fileName, bool finished);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
1102
src/createtorrent.ui
|
@ -42,51 +42,23 @@ using namespace boost::filesystem;
|
|||
|
||||
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
|
||||
setupUi(this);
|
||||
addTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
|
||||
removeTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
||||
addURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
|
||||
removeURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
||||
removeFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
||||
addFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_folder.png")));
|
||||
addFile_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_file.png")));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
show();
|
||||
}
|
||||
|
||||
void createtorrent::on_browse_destination_clicked(){
|
||||
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
||||
if(!destination.isEmpty()){
|
||||
if(!destination.endsWith(QString::fromUtf8(".torrent")))
|
||||
destination += QString::fromUtf8(".torrent");
|
||||
txt_destination->setText(destination);
|
||||
}
|
||||
}
|
||||
|
||||
void createtorrent::on_addFolder_button_clicked(){
|
||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
|
||||
if(!dir.isEmpty() && input_list->findItems(dir, Qt::MatchCaseSensitive).size() == 0)
|
||||
input_list->addItem(dir);
|
||||
if(!dir.isEmpty())
|
||||
textInputPath->setText(dir);
|
||||
}
|
||||
|
||||
void createtorrent::on_addFile_button_clicked(){
|
||||
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
|
||||
QString file;
|
||||
foreach(file, files){
|
||||
if(input_list->findItems(file, Qt::MatchCaseSensitive).size() == 0){
|
||||
input_list->addItem(file);
|
||||
}
|
||||
}
|
||||
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
|
||||
if(!file.isEmpty())
|
||||
textInputPath->setText(file);
|
||||
}
|
||||
|
||||
void createtorrent::on_removeFolder_button_clicked(){
|
||||
QModelIndexList selectedIndexes = input_list->selectionModel()->selectedIndexes();
|
||||
for(int i=selectedIndexes.size()-1; i>=0; --i){
|
||||
QListWidgetItem *item = input_list->takeItem(selectedIndexes.at(i).row());
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void createtorrent::on_removeTracker_button_clicked(){
|
||||
void createtorrent::on_removeTracker_button_clicked() {
|
||||
QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes();
|
||||
for(int i=selectedIndexes.size()-1; i>=0; --i){
|
||||
QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row());
|
||||
|
@ -94,7 +66,28 @@ void createtorrent::on_removeTracker_button_clicked(){
|
|||
}
|
||||
}
|
||||
|
||||
void createtorrent::on_addTracker_button_clicked(){
|
||||
int createtorrent::getPieceSize() const {
|
||||
switch(comboPieceSize->currentIndex()) {
|
||||
case 0:
|
||||
return 32*1024;
|
||||
case 1:
|
||||
return 64*1024;
|
||||
case 2:
|
||||
return 128*1024;
|
||||
case 3:
|
||||
return 256*1024;
|
||||
case 4:
|
||||
return 512*1024;
|
||||
case 5:
|
||||
return 1024*1024;
|
||||
case 6:
|
||||
return 2048*1024;
|
||||
default:
|
||||
return 256*1024;
|
||||
}
|
||||
}
|
||||
|
||||
void createtorrent::on_addTracker_button_clicked() {
|
||||
bool ok;
|
||||
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
|
||||
tr("Announce URL:", "Tracker URL"), QLineEdit::Normal,
|
||||
|
@ -147,60 +140,67 @@ QStringList createtorrent::allItems(QListWidget *list){
|
|||
|
||||
// Main function that create a .torrent file
|
||||
void createtorrent::on_createButton_clicked(){
|
||||
QString destination = txt_destination->text();
|
||||
if(destination.isEmpty()){
|
||||
QMessageBox::critical(0, tr("No destination path set"), tr("Please type a destination path first"));
|
||||
QString input = textInputPath->text().trimmed();
|
||||
if(input.isEmpty() == 0){
|
||||
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
|
||||
return;
|
||||
}
|
||||
QStringList input = allItems(input_list);
|
||||
if(input.size() == 0){
|
||||
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
|
||||
QStringList trackers = allItems(trackers_list);
|
||||
if(!trackers.size()){
|
||||
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
|
||||
return;
|
||||
}
|
||||
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
||||
if(!destination.isEmpty()) {
|
||||
if(!destination.endsWith(QString::fromUtf8(".torrent")))
|
||||
destination += QString::fromUtf8(".torrent");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
char const* creator_str = "qBittorrent "VERSION;
|
||||
try {
|
||||
torrent_info t;
|
||||
QString input_path;
|
||||
path full_path;
|
||||
boost::intrusive_ptr<torrent_info> t(new torrent_info);
|
||||
ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary);
|
||||
foreach(input_path, input){
|
||||
full_path = complete(path((const char*)input_path.toUtf8()));
|
||||
add_files(t, full_path.branch_path(), full_path.leaf());
|
||||
}
|
||||
int piece_size = 256 * 1024;
|
||||
t.set_piece_size(piece_size);
|
||||
path full_path;
|
||||
// Adding files to the torrent
|
||||
full_path = complete(path(input.toUtf8().data()));
|
||||
add_files(*t, full_path.branch_path(), full_path.leaf());
|
||||
// Set piece size
|
||||
int piece_size = getPieceSize();
|
||||
t->set_piece_size(piece_size);
|
||||
// Add url seeds
|
||||
QStringList urlSeeds = allItems(URLSeeds_list);
|
||||
QString seed;
|
||||
foreach(seed, urlSeeds){
|
||||
t.add_url_seed((const char*)seed.toUtf8());
|
||||
t->add_url_seed(seed.toUtf8().data());
|
||||
}
|
||||
QStringList trackers = allItems(trackers_list);
|
||||
for(int i=0; i<trackers.size(); ++i){
|
||||
t.add_tracker((const char*)trackers.at(i).toUtf8());
|
||||
t->add_tracker(trackers.at(i).toUtf8().data());
|
||||
}
|
||||
|
||||
// calculate the hash for all pieces
|
||||
file_pool fp;
|
||||
boost::scoped_ptr<storage_interface> st(default_storage_constructor(t, full_path.branch_path(), fp));
|
||||
int num = t.num_pieces();
|
||||
int num = t->num_pieces();
|
||||
std::vector<char> buf(piece_size);
|
||||
for (int i = 0; i < num; ++i) {
|
||||
st->read(&buf[0], i, 0, t.piece_size(i));
|
||||
hasher h(&buf[0], t.piece_size(i));
|
||||
t.set_hash(i, h.final());
|
||||
st->read(&buf[0], i, 0, t->piece_size(i));
|
||||
hasher h(&buf[0], t->piece_size(i));
|
||||
t->set_hash(i, h.final());
|
||||
}
|
||||
// Set qBittorrent as creator and add user comment to
|
||||
// torrent_info structure
|
||||
t.set_creator(creator_str);
|
||||
t.set_comment((const char*)txt_comment->toPlainText().toUtf8());
|
||||
t->set_creator(creator_str);
|
||||
t->set_comment((const char*)txt_comment->toPlainText().toUtf8());
|
||||
// Is private ?
|
||||
if(check_private->isChecked()){
|
||||
t.set_priv(true);
|
||||
t->set_priv(true);
|
||||
}
|
||||
// create the torrent and print it to out
|
||||
entry e = t.create_torrent();
|
||||
entry e = t->create_torrent();
|
||||
libtorrent::bencode(std::ostream_iterator<char>(out), e);
|
||||
if(checkStartSeeding->isChecked())
|
||||
emit torrent_to_seed(destination);
|
||||
}
|
||||
catch (std::exception& e){
|
||||
std::cerr << e.what() << "\n";
|
||||
|
|
|
@ -30,13 +30,15 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
|
|||
public:
|
||||
createtorrent(QWidget *parent = 0);
|
||||
QStringList allItems(QListWidget *list);
|
||||
int getPieceSize() const;
|
||||
|
||||
signals:
|
||||
void torrent_to_seed(QString path);
|
||||
|
||||
protected slots:
|
||||
void on_browse_destination_clicked();
|
||||
void on_createButton_clicked();
|
||||
void on_addFile_button_clicked();
|
||||
void on_addFolder_button_clicked();
|
||||
void on_removeFolder_button_clicked();
|
||||
void on_addTracker_button_clicked();
|
||||
void on_removeTracker_button_clicked();
|
||||
void on_addURLSeed_button_clicked();
|
||||
|
|
|
@ -28,17 +28,17 @@
|
|||
#include <QMutexLocker>
|
||||
#include <QPair>
|
||||
|
||||
#include "misc.h"
|
||||
#include "arborescence.h"
|
||||
|
||||
class subDeleteThread : public QThread {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString save_path;
|
||||
QStringList files_path;
|
||||
arborescence *arb;
|
||||
bool abort;
|
||||
|
||||
public:
|
||||
subDeleteThread(QObject *parent, QString save_path, QStringList files_path) : QThread(parent), save_path(save_path), files_path(files_path), abort(false){}
|
||||
subDeleteThread(QObject *parent, QString saveDir, arborescence *arb) : QThread(parent), save_path(saveDir), arb(arb), abort(false){}
|
||||
|
||||
~subDeleteThread(){
|
||||
abort = true;
|
||||
|
@ -52,10 +52,11 @@ class subDeleteThread : public QThread {
|
|||
|
||||
protected:
|
||||
void run(){
|
||||
if(misc::removeTorrentSavePath(save_path, files_path))
|
||||
if(arb->removeFromFS(save_path))
|
||||
emit deletionSuccessST(this);
|
||||
else
|
||||
emit deletionFailureST(this);
|
||||
delete arb;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -63,7 +64,7 @@ class deleteThread : public QThread {
|
|||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QList<QPair<QString, QStringList> > torrents_list;
|
||||
QList<QPair<QString, arborescence*> > torrents_list;
|
||||
QMutex mutex;
|
||||
QWaitCondition condition;
|
||||
bool abort;
|
||||
|
@ -81,9 +82,10 @@ class deleteThread : public QThread {
|
|||
wait();
|
||||
}
|
||||
|
||||
void deleteTorrent(QString save_path, QStringList files_path){
|
||||
void deleteTorrent(QString saveDir, arborescence *arb){
|
||||
qDebug("deleteThread called");
|
||||
QMutexLocker locker(&mutex);
|
||||
torrents_list << QPair<QString, QStringList>(save_path, files_path);
|
||||
torrents_list << QPair<QString, arborescence*>(saveDir, arb);
|
||||
if(!isRunning()){
|
||||
start();
|
||||
}else{
|
||||
|
@ -98,7 +100,7 @@ class deleteThread : public QThread {
|
|||
return;
|
||||
mutex.lock();
|
||||
if(torrents_list.size() != 0){
|
||||
QPair<QString, QStringList> torrent = torrents_list.takeFirst();
|
||||
QPair<QString, arborescence *> torrent = torrents_list.takeFirst();
|
||||
mutex.unlock();
|
||||
subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second);
|
||||
subThreads << st;
|
||||
|
|
174
src/downloadThread.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "downloadThread.h"
|
||||
#include <iostream>
|
||||
#include <cc++/common.h>
|
||||
|
||||
QString subDownloadThread::errorCodeToString(int status) {
|
||||
switch(status){
|
||||
case 1://ost::URLStream::errUnreachable:
|
||||
return tr("Host is unreachable");
|
||||
case 2://ost::URLStream::errMissing:
|
||||
return tr("File was not found (404)");
|
||||
case 3://ost::URLStream::errDenied:
|
||||
return tr("Connection was denied");
|
||||
case 4://ost::URLStream::errInvalid:
|
||||
return tr("Url is invalid");
|
||||
case 5://ost::URLStream::errForbidden:
|
||||
return tr("Connection forbidden (403)");
|
||||
case 6://ost::URLStream::errUnauthorized:
|
||||
return tr("Connection was not authorized (401)");
|
||||
case 7://ost::URLStream::errRelocated:
|
||||
return tr("Content has moved (301)");
|
||||
case 8://ost::URLStream::errFailure:
|
||||
return tr("Connection failure");
|
||||
case 9://ost::URLStream::errTimeout:
|
||||
return tr("Connection was timed out");
|
||||
case 10://ost::URLStream::errInterface:
|
||||
return tr("Incorrect network interface");
|
||||
default:
|
||||
return tr("Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){
|
||||
url_stream = new ost::URLStream();
|
||||
}
|
||||
|
||||
subDownloadThread::~subDownloadThread(){
|
||||
abort = true;
|
||||
wait();
|
||||
delete url_stream;
|
||||
}
|
||||
|
||||
void subDownloadThread::run(){
|
||||
// XXX: Trick to get a unique filename
|
||||
QString filePath;
|
||||
QTemporaryFile *tmpfile = new QTemporaryFile();
|
||||
if (tmpfile->open()) {
|
||||
filePath = tmpfile->fileName();
|
||||
}
|
||||
delete tmpfile;
|
||||
QFile dest_file(filePath);
|
||||
if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){
|
||||
std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n';
|
||||
return;
|
||||
}
|
||||
ost::URLStream::Error status = url_stream->get((const char*)url.toUtf8());
|
||||
if(status){
|
||||
// Failure
|
||||
QString error_msg = errorCodeToString((int)status);
|
||||
qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8());
|
||||
url_stream->close();
|
||||
emit downloadFailureST(this, url, error_msg);
|
||||
return;
|
||||
}
|
||||
qDebug("Downloading %s...", (const char*)url.toUtf8());
|
||||
char cbuf[1024];
|
||||
int len;
|
||||
while(!url_stream->eof()) {
|
||||
url_stream->read(cbuf, sizeof(cbuf));
|
||||
len = url_stream->gcount();
|
||||
if(len > 0)
|
||||
dest_file.write(cbuf, len);
|
||||
if(abort){
|
||||
dest_file.close();
|
||||
url_stream->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest_file.close();
|
||||
url_stream->close();
|
||||
emit downloadFinishedST(this, url, filePath);
|
||||
qDebug("download completed here: %s", (const char*)filePath.toUtf8());
|
||||
}
|
||||
|
||||
/** Download Thread **/
|
||||
|
||||
downloadThread::downloadThread(QObject* parent) : QThread(parent), abort(false){}
|
||||
|
||||
downloadThread::~downloadThread(){
|
||||
mutex.lock();
|
||||
abort = true;
|
||||
condition.wakeOne();
|
||||
mutex.unlock();
|
||||
qDeleteAll(subThreads);
|
||||
wait();
|
||||
}
|
||||
|
||||
void downloadThread::downloadUrl(QString url){
|
||||
QMutexLocker locker(&mutex);
|
||||
if(downloading_list.contains(url)) return;
|
||||
url_list << url;
|
||||
downloading_list << url;
|
||||
if(!isRunning()){
|
||||
start();
|
||||
}else{
|
||||
condition.wakeOne();
|
||||
}
|
||||
}
|
||||
|
||||
void downloadThread::run(){
|
||||
forever{
|
||||
if(abort)
|
||||
return;
|
||||
mutex.lock();
|
||||
if(url_list.size() != 0){
|
||||
QString url = url_list.takeFirst();
|
||||
mutex.unlock();
|
||||
subDownloadThread *st = new subDownloadThread(0, url);
|
||||
subThreads << st;
|
||||
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
|
||||
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
|
||||
st->start();
|
||||
}else{
|
||||
condition.wait(&mutex);
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void downloadThread::propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
|
||||
int index = subThreads.indexOf(st);
|
||||
Q_ASSERT(index != -1);
|
||||
subThreads.removeAt(index);
|
||||
delete st;
|
||||
emit downloadFinished(url, path);
|
||||
mutex.lock();
|
||||
index = downloading_list.indexOf(url);
|
||||
Q_ASSERT(index != -1);
|
||||
downloading_list.removeAt(index);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void downloadThread::propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){
|
||||
int index = subThreads.indexOf(st);
|
||||
Q_ASSERT(index != -1);
|
||||
subThreads.removeAt(index);
|
||||
delete st;
|
||||
emit downloadFailure(url, reason);
|
||||
mutex.lock();
|
||||
index = downloading_list.indexOf(url);
|
||||
Q_ASSERT(index != -1);
|
||||
downloading_list.removeAt(index);
|
||||
mutex.unlock();
|
||||
}
|
|
@ -29,55 +29,22 @@
|
|||
#include <QMutexLocker>
|
||||
#include <QWaitCondition>
|
||||
#include <QStringList>
|
||||
#include <iostream>
|
||||
#include <cc++/common.h>
|
||||
|
||||
#ifdef CCXX_NAMESPACES
|
||||
using namespace std;
|
||||
using namespace ost;
|
||||
#endif
|
||||
namespace ost {
|
||||
class URLStream;
|
||||
}
|
||||
|
||||
class subDownloadThread : public QThread {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString url;
|
||||
URLStream url_stream;
|
||||
ost::URLStream *url_stream;
|
||||
bool abort;
|
||||
|
||||
public:
|
||||
subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){}
|
||||
|
||||
~subDownloadThread(){
|
||||
abort = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
static QString errorCodeToString(URLStream::Error status){
|
||||
switch(status){
|
||||
case URLStream::errUnreachable:
|
||||
return tr("Host is unreachable");
|
||||
case URLStream::errMissing:
|
||||
return tr("File was not found (404)");
|
||||
case URLStream::errDenied:
|
||||
return tr("Connection was denied");
|
||||
case URLStream::errInvalid:
|
||||
return tr("Url is invalid");
|
||||
case URLStream::errForbidden:
|
||||
return tr("Connection forbidden (403)");
|
||||
case URLStream::errUnauthorized:
|
||||
return tr("Connection was not authorized (401)");
|
||||
case URLStream::errRelocated:
|
||||
return tr("Content has moved (301)");
|
||||
case URLStream::errFailure:
|
||||
return tr("Connection failure");
|
||||
case URLStream::errTimeout:
|
||||
return tr("Connection was timed out");
|
||||
case URLStream::errInterface:
|
||||
return tr("Incorrect network interface");
|
||||
default:
|
||||
return tr("Unknown error");
|
||||
}
|
||||
}
|
||||
subDownloadThread(QObject *parent, QString url);
|
||||
~subDownloadThread();
|
||||
QString errorCodeToString(int status);
|
||||
|
||||
signals:
|
||||
// For subthreads
|
||||
|
@ -85,47 +52,7 @@ class subDownloadThread : public QThread {
|
|||
void downloadFailureST(subDownloadThread* st, QString url, QString reason);
|
||||
|
||||
protected:
|
||||
void run(){
|
||||
// XXX: Trick to get a unique filename
|
||||
QString filePath;
|
||||
QTemporaryFile *tmpfile = new QTemporaryFile();
|
||||
if (tmpfile->open()) {
|
||||
filePath = tmpfile->fileName();
|
||||
}
|
||||
delete tmpfile;
|
||||
QFile dest_file(filePath);
|
||||
if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){
|
||||
std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n';
|
||||
return;
|
||||
}
|
||||
URLStream::Error status = url_stream.get((const char*)url.toUtf8());
|
||||
if(status){
|
||||
// Failure
|
||||
QString error_msg = errorCodeToString(status);
|
||||
qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8());
|
||||
url_stream.close();
|
||||
emit downloadFailureST(this, url, error_msg);
|
||||
return;
|
||||
}
|
||||
qDebug("Downloading %s...", (const char*)url.toUtf8());
|
||||
char cbuf[1024];
|
||||
int len;
|
||||
while(!url_stream.eof()) {
|
||||
url_stream.read(cbuf, sizeof(cbuf));
|
||||
len = url_stream.gcount();
|
||||
if(len > 0)
|
||||
dest_file.write(cbuf, len);
|
||||
if(abort){
|
||||
dest_file.close();
|
||||
url_stream.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest_file.close();
|
||||
url_stream.close();
|
||||
emit downloadFinishedST(this, url, filePath);
|
||||
qDebug("download completed here: %s", (const char*)filePath.toUtf8());
|
||||
}
|
||||
void run();
|
||||
};
|
||||
|
||||
class downloadThread : public QThread {
|
||||
|
@ -144,75 +71,19 @@ class downloadThread : public QThread {
|
|||
void downloadFailure(QString url, QString reason);
|
||||
|
||||
public:
|
||||
downloadThread(QObject* parent) : QThread(parent), abort(false){}
|
||||
downloadThread(QObject* parent);
|
||||
|
||||
~downloadThread(){
|
||||
mutex.lock();
|
||||
abort = true;
|
||||
condition.wakeOne();
|
||||
mutex.unlock();
|
||||
qDeleteAll(subThreads);
|
||||
wait();
|
||||
}
|
||||
~downloadThread();
|
||||
|
||||
void downloadUrl(QString url){
|
||||
QMutexLocker locker(&mutex);
|
||||
if(downloading_list.contains(url)) return;
|
||||
url_list << url;
|
||||
downloading_list << url;
|
||||
if(!isRunning()){
|
||||
start();
|
||||
}else{
|
||||
condition.wakeOne();
|
||||
}
|
||||
}
|
||||
void downloadUrl(QString url);
|
||||
|
||||
protected:
|
||||
void run(){
|
||||
forever{
|
||||
if(abort)
|
||||
return;
|
||||
mutex.lock();
|
||||
if(url_list.size() != 0){
|
||||
QString url = url_list.takeFirst();
|
||||
mutex.unlock();
|
||||
subDownloadThread *st = new subDownloadThread(0, url);
|
||||
subThreads << st;
|
||||
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
|
||||
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
|
||||
st->start();
|
||||
}else{
|
||||
condition.wait(&mutex);
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
protected slots:
|
||||
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
|
||||
int index = subThreads.indexOf(st);
|
||||
Q_ASSERT(index != -1);
|
||||
subThreads.removeAt(index);
|
||||
delete st;
|
||||
emit downloadFinished(url, path);
|
||||
mutex.lock();
|
||||
index = downloading_list.indexOf(url);
|
||||
Q_ASSERT(index != -1);
|
||||
downloading_list.removeAt(index);
|
||||
mutex.unlock();
|
||||
}
|
||||
void run();
|
||||
|
||||
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){
|
||||
int index = subThreads.indexOf(st);
|
||||
Q_ASSERT(index != -1);
|
||||
subThreads.removeAt(index);
|
||||
delete st;
|
||||
emit downloadFailure(url, reason);
|
||||
mutex.lock();
|
||||
index = downloading_list.indexOf(url);
|
||||
Q_ASSERT(index != -1);
|
||||
downloading_list.removeAt(index);
|
||||
mutex.unlock();
|
||||
}
|
||||
protected slots:
|
||||
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path);
|
||||
|
||||
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -182,7 +182,7 @@ void DownloadingTorrents::deleteTorrent(QString hash) {
|
|||
}
|
||||
|
||||
// Update Info Bar information
|
||||
void DownloadingTorrents::setInfoBar(QString info, QString color) {
|
||||
void DownloadingTorrents::setInfoBar(QString info, QColor color) {
|
||||
static unsigned int nbLines = 0;
|
||||
++nbLines;
|
||||
// Check log size, clear it if too big
|
||||
|
@ -190,7 +190,8 @@ void DownloadingTorrents::setInfoBar(QString info, QString color) {
|
|||
infoBar->clear();
|
||||
nbLines = 1;
|
||||
}
|
||||
infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>"));
|
||||
qDebug("Color is %s", color.name().toUtf8().data());
|
||||
infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color.name() +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>"));
|
||||
}
|
||||
|
||||
void DownloadingTorrents::addFastResumeRejectedAlert(QString name) {
|
||||
|
@ -246,7 +247,7 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
|
|||
// Enable/disable pause/start action given the DL state
|
||||
QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes();
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString();
|
||||
QString previewProgram = settings.value(QString::fromUtf8("Preferences/general/MediaPlayer"), QString()).toString();
|
||||
bool has_pause = false, has_start = false, has_preview = false;
|
||||
foreach(index, selectedIndexes) {
|
||||
if(index.column() == NAME) {
|
||||
|
@ -336,7 +337,7 @@ void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
|
|||
QMenu myLogMenu(this);
|
||||
myLogMenu.addAction(actionClearLog);
|
||||
// XXX: Why mapToGlobal() is not enough?
|
||||
myLogMenu.exec(mapToGlobal(pos)+QPoint(22,383));
|
||||
myLogMenu.exec(mapToGlobal(pos)+QPoint(44,305));
|
||||
}
|
||||
|
||||
void DownloadingTorrents::sortProgressColumnDelayed() {
|
||||
|
@ -372,6 +373,10 @@ void DownloadingTorrents::updateDlList() {
|
|||
Q_ASSERT(row != -1);
|
||||
// No need to update a paused torrent
|
||||
if(h.is_paused()) continue;
|
||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
|
||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||
continue;
|
||||
}
|
||||
// Parse download state
|
||||
// Setting download state
|
||||
switch(h.state()) {
|
||||
|
@ -384,10 +389,8 @@ void DownloadingTorrents::updateDlList() {
|
|||
continue;
|
||||
case torrent_status::checking_files:
|
||||
case torrent_status::queued_for_checking:
|
||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
|
||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||
setRowColor(row, QString::fromUtf8("grey"));
|
||||
}
|
||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||
setRowColor(row, QString::fromUtf8("grey"));
|
||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||
break;
|
||||
case torrent_status::connecting_to_tracker:
|
||||
|
@ -414,7 +417,7 @@ void DownloadingTorrents::updateDlList() {
|
|||
}else{
|
||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/stalled.png"))), Qt::DecorationRole);
|
||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
||||
setRowColor(row, QString::fromUtf8("black"));
|
||||
setRowColor(row, QPalette::WindowText);
|
||||
}
|
||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
|
||||
|
@ -661,10 +664,10 @@ void DownloadingTorrents::portListeningFailure() {
|
|||
}
|
||||
|
||||
// Set the color of a row in data model
|
||||
void DownloadingTorrents::setRowColor(int row, QString color) {
|
||||
unsigned int nbColumns = DLListModel->columnCount();
|
||||
void DownloadingTorrents::setRowColor(int row, QColor color) {
|
||||
unsigned int nbColumns = DLListModel->columnCount()-1;
|
||||
for(unsigned int i=0; i<nbColumns; ++i) {
|
||||
DLListModel->setData(DLListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
|
||||
DLListModel->setData(DLListModel->index(row, i), QVariant(color), Qt::ForegroundRole);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -681,5 +684,5 @@ int DownloadingTorrents::getRowFromHash(QString hash) const{
|
|||
}
|
||||
|
||||
void DownloadingTorrents::displayDownloadingUrlInfos(QString url) {
|
||||
setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QString::fromUtf8("black"));
|
||||
setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QPalette::WindowText);
|
||||
}
|
||||
|
|
|
@ -76,13 +76,13 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
|
|||
void torrentDuplicate(QString path);
|
||||
void torrentCorrupted(QString path);
|
||||
void portListeningFailure();
|
||||
void setRowColor(int row, QString color);
|
||||
void setRowColor(int row, QColor color);
|
||||
void displayDownloadingUrlInfos(QString url);
|
||||
void showProperties(const QModelIndex &index);
|
||||
|
||||
public slots:
|
||||
void updateDlList();
|
||||
void setInfoBar(QString info, QString color="black");
|
||||
void setInfoBar(QString info, QColor color=QPalette::WindowText);
|
||||
void pauseTorrent(QString hash);
|
||||
void resumeTorrent(QString hash);
|
||||
void updateRatio();
|
||||
|
|
125
src/engineSelect.ui
Normal file
|
@ -0,0 +1,125 @@
|
|||
<ui version="4.0" >
|
||||
<class>engineSelect</class>
|
||||
<widget class="QDialog" name="engineSelect" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>541</width>
|
||||
<height>254</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Search plugins</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_engines" >
|
||||
<property name="font" >
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
<underline>true</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>Installed search engines:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="pluginsTree" >
|
||||
<property name="contextMenuPolicy" >
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode" >
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="uniformRowHeights" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text" >
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text" >
|
||||
<string>Url</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text" >
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="getNewEngine_lbl" >
|
||||
<property name="font" >
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>You can get new search engine plugins here: <a href="http:plugins.qbittorrent.org">http://plugins.qbittorrent.org</a></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="installButton" >
|
||||
<property name="text" >
|
||||
<string>Install a new one</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="updateButton" >
|
||||
<property name="text" >
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton" >
|
||||
<property name="text" >
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionEnable" >
|
||||
<property name="text" >
|
||||
<string>Enable</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDisable" >
|
||||
<property name="text" >
|
||||
<string>Disable</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUninstall" >
|
||||
<property name="text" >
|
||||
<string>Uninstall</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
697
src/engineSelectDlg.cpp
Normal file
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "engineSelectDlg.h"
|
||||
#include "downloadThread.h"
|
||||
#include "misc.h"
|
||||
#include "pluginSource.h"
|
||||
#include <QProcess>
|
||||
#include <QHeaderView>
|
||||
#include <QSettings>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QDropEvent>
|
||||
#include <QInputDialog>
|
||||
|
||||
#ifdef HAVE_MAGICK
|
||||
#include <Magick++.h>
|
||||
using namespace Magick;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ZZIP
|
||||
#include <zzip/zzip.h>
|
||||
#endif
|
||||
|
||||
#define ENGINE_NAME 0
|
||||
#define ENGINE_URL 1
|
||||
#define ENGINE_STATE 2
|
||||
#define ENGINE_ID 3
|
||||
|
||||
engineSelectDlg::engineSelectDlg(QWidget *parent) : QDialog(parent) {
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
pluginsTree->header()->resizeSection(0, 170);
|
||||
pluginsTree->header()->resizeSection(1, 220);
|
||||
pluginsTree->hideColumn(ENGINE_ID);
|
||||
actionEnable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||
actionDisable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||
actionUninstall->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
||||
connect(actionEnable, SIGNAL(triggered()), this, SLOT(enableSelection()));
|
||||
connect(actionDisable, SIGNAL(triggered()), this, SLOT(disableSelection()));
|
||||
connect(pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&)));
|
||||
downloader = new downloadThread(this);
|
||||
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
|
||||
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
||||
loadSupportedSearchEngines(true);
|
||||
connect(pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(toggleEngineState(QTreeWidgetItem*, int)));
|
||||
show();
|
||||
}
|
||||
|
||||
engineSelectDlg::~engineSelectDlg() {
|
||||
qDebug("Destroying engineSelectDlg");
|
||||
saveSettings();
|
||||
emit enginesChanged();
|
||||
qDebug("Before deleting downloader");
|
||||
delete downloader;
|
||||
qDebug("Engine plugins dialog destroyed");
|
||||
}
|
||||
|
||||
void engineSelectDlg::dropEvent(QDropEvent *event) {
|
||||
event->acceptProposedAction();
|
||||
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
|
||||
QString file;
|
||||
foreach(file, files) {
|
||||
qDebug("dropped %s", file.toUtf8().data());
|
||||
file = file.replace("file://", "");
|
||||
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
|
||||
downloader->downloadUrl(file);
|
||||
continue;
|
||||
}
|
||||
if(file.endsWith(".py", Qt::CaseInsensitive)) {
|
||||
QString plugin_name = file.split(QDir::separator()).last();
|
||||
plugin_name.replace(".py", "");
|
||||
installPlugin(file, plugin_name);
|
||||
}
|
||||
#ifdef HAVE_ZZIP
|
||||
if(file.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||
installZipPlugin(file);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Decode if we accept drag 'n drop or not
|
||||
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
|
||||
QString mime;
|
||||
foreach(mime, event->mimeData()->formats()){
|
||||
qDebug("mimeData: %s", mime.toUtf8().data());
|
||||
}
|
||||
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void engineSelectDlg::saveSettings() {
|
||||
qDebug("Saving engines settings");
|
||||
QStringList known_engines;
|
||||
QVariantList known_enginesEnabled;
|
||||
QString engine;
|
||||
foreach(engine, installed_engines.keys()) {
|
||||
known_engines << engine;
|
||||
known_enginesEnabled << QVariant(installed_engines.value(engine, true));
|
||||
qDebug("Engine %s has state: %d", engine.toUtf8().data(), installed_engines.value(engine, true));
|
||||
}
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
settings.setValue(QString::fromUtf8("SearchEngines/knownEngines"), known_engines);
|
||||
settings.setValue(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), known_enginesEnabled);
|
||||
}
|
||||
|
||||
void engineSelectDlg::on_updateButton_clicked() {
|
||||
// Download version file from primary server
|
||||
downloader->downloadUrl("http://www.dchris.eu/search_engine/versions.txt");
|
||||
}
|
||||
|
||||
void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
|
||||
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||
QString id = item->text(ENGINE_ID);
|
||||
bool new_val = !installed_engines.value(id, true);
|
||||
installed_engines[id] = new_val;
|
||||
QString enabledTxt;
|
||||
if(new_val){
|
||||
enabledTxt = tr("True");
|
||||
setRowColor(index, "green");
|
||||
}else{
|
||||
enabledTxt = tr("False");
|
||||
setRowColor(index, "red");
|
||||
}
|
||||
item->setText(ENGINE_STATE, enabledTxt);
|
||||
}
|
||||
|
||||
void engineSelectDlg::displayContextMenu(const QPoint& pos) {
|
||||
QMenu myContextMenu(this);
|
||||
QModelIndex index;
|
||||
// Enable/disable pause/start action given the DL state
|
||||
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||
bool has_enable = false, has_disable = false;
|
||||
QTreeWidgetItem *item;
|
||||
foreach(item, items) {
|
||||
QString id = item->text(ENGINE_ID);
|
||||
if(installed_engines.value(id, true) and !has_disable) {
|
||||
myContextMenu.addAction(actionDisable);
|
||||
has_disable = true;
|
||||
}
|
||||
if(!installed_engines.value(id, true) and !has_enable) {
|
||||
myContextMenu.addAction(actionEnable);
|
||||
has_enable = true;
|
||||
}
|
||||
if(has_enable && has_disable) break;
|
||||
}
|
||||
myContextMenu.addSeparator();
|
||||
myContextMenu.addAction(actionUninstall);
|
||||
myContextMenu.exec(mapToGlobal(pos)+QPoint(12, 58));
|
||||
}
|
||||
|
||||
void engineSelectDlg::on_closeButton_clicked() {
|
||||
close();
|
||||
}
|
||||
|
||||
void engineSelectDlg::on_actionUninstall_triggered() {
|
||||
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||
QTreeWidgetItem *item;
|
||||
bool change = false;
|
||||
bool error = false;
|
||||
foreach(item, items) {
|
||||
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||
Q_ASSERT(index != -1);
|
||||
QString id = item->text(ENGINE_ID);
|
||||
if(QFile::exists(":/search_engine/engines/"+id+".py")) {
|
||||
error = true;
|
||||
// Disable it instead
|
||||
installed_engines.insert(id, false);
|
||||
item->setText(ENGINE_STATE, tr("False"));
|
||||
setRowColor(index, "red");
|
||||
continue;
|
||||
}else {
|
||||
// Proceed with uninstall
|
||||
// remove it from hard drive
|
||||
QDir enginesFolder(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines");
|
||||
QStringList filters;
|
||||
filters << id+".*";
|
||||
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||
QString file;
|
||||
foreach(file, files) {
|
||||
enginesFolder.remove(file);
|
||||
}
|
||||
// Remove it from lists
|
||||
installed_engines.remove(id);
|
||||
delete item;
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if(error)
|
||||
QMessageBox::warning(0, tr("Uninstall warning"), tr("Some plugins could not be uninstalled because they are included in qBittorrent.\n Only the ones you added yourself can be uninstalled.\nHowever, those plugins were disabled."));
|
||||
else
|
||||
QMessageBox::information(0, tr("Uninstall success"), tr("All selected plugins were uninstalled successfully"));
|
||||
}
|
||||
|
||||
void engineSelectDlg::enableSelection() {
|
||||
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||
QTreeWidgetItem *item;
|
||||
foreach(item, items) {
|
||||
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||
Q_ASSERT(index != -1);
|
||||
QString id = item->text(ENGINE_ID);
|
||||
installed_engines.insert(id, true);
|
||||
item->setText(ENGINE_STATE, tr("True"));
|
||||
setRowColor(index, "green");
|
||||
}
|
||||
}
|
||||
|
||||
void engineSelectDlg::disableSelection() {
|
||||
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||
QTreeWidgetItem *item;
|
||||
foreach(item, items) {
|
||||
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||
Q_ASSERT(index != -1);
|
||||
QString id = item->text(ENGINE_ID);
|
||||
installed_engines.insert(id, false);
|
||||
item->setText(ENGINE_STATE, tr("False"));
|
||||
setRowColor(index, "red");
|
||||
}
|
||||
}
|
||||
|
||||
// Set the color of a row in data model
|
||||
void engineSelectDlg::setRowColor(int row, QString color){
|
||||
QTreeWidgetItem *item = pluginsTree->topLevelItem(row);
|
||||
for(int i=0; i<pluginsTree->columnCount()-1; ++i){
|
||||
item->setData(i, Qt::ForegroundRole, QVariant(QColor(color)));
|
||||
}
|
||||
}
|
||||
|
||||
bool engineSelectDlg::checkInstalled(QString plugin_name) const {
|
||||
QProcess nova;
|
||||
QStringList params;
|
||||
params << "--supported_engines";
|
||||
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
|
||||
nova.waitForStarted();
|
||||
nova.waitForFinished();
|
||||
QByteArray result = nova.readAll();
|
||||
result = result.replace("\n", "");
|
||||
QList<QByteArray> plugins_list = result.split(',');
|
||||
return plugins_list.contains(plugin_name.toUtf8());
|
||||
}
|
||||
|
||||
void engineSelectDlg::loadSupportedSearchEngines(bool first) {
|
||||
// Some clean up first
|
||||
pluginsTree->clear();
|
||||
QHash<QString, bool> old_engines;
|
||||
if(first) {
|
||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||
QStringList known_engines = settings.value(QString::fromUtf8("SearchEngines/knownEngines"), QStringList()).toStringList();
|
||||
QVariantList enabled = settings.value(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), QList<QVariant>()).toList();
|
||||
Q_ASSERT(known_engines.size() == enabled.size());
|
||||
unsigned int nbKnownEngines = known_engines.size();
|
||||
for(unsigned int i=0; i<nbKnownEngines; ++i) {
|
||||
old_engines[known_engines.at(i)] = enabled.at(i).toBool();
|
||||
}
|
||||
} else {
|
||||
old_engines = installed_engines;
|
||||
}
|
||||
installed_engines.clear();
|
||||
QStringList params;
|
||||
// Ask nova core for the supported search engines
|
||||
QProcess nova;
|
||||
params << "--supported_engines";
|
||||
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
|
||||
nova.waitForStarted();
|
||||
nova.waitForFinished();
|
||||
QByteArray result = nova.readAll();
|
||||
result = result.replace("\n", "");
|
||||
qDebug("read: %s", result.data());
|
||||
QByteArray e;
|
||||
QStringList supported_engines_ids;
|
||||
foreach(e, result.split(',')) {
|
||||
QString en = QString(e);
|
||||
supported_engines_ids << en;
|
||||
installed_engines[en] = old_engines.value(en, true);
|
||||
}
|
||||
params.clear();
|
||||
params << "--supported_engines_infos";
|
||||
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
|
||||
nova.waitForStarted();
|
||||
nova.waitForFinished();
|
||||
result = nova.readAll();
|
||||
result = result.replace("\n", "");
|
||||
qDebug("read: %s", result.data());
|
||||
unsigned int i = 0;
|
||||
foreach(e, result.split(',')) {
|
||||
QString id = supported_engines_ids.at(i);
|
||||
QString nameUrlCouple(e);
|
||||
QStringList line = nameUrlCouple.split('|');
|
||||
if(line.size() != 2) continue;
|
||||
QString enabledTxt;
|
||||
if(installed_engines.value(id, true)) {
|
||||
enabledTxt = tr("True");
|
||||
} else {
|
||||
enabledTxt = tr("False");
|
||||
}
|
||||
line << enabledTxt;
|
||||
line << id;
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(pluginsTree, line);
|
||||
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
||||
if(QFile::exists(iconPath)) {
|
||||
// Good, we already have the icon
|
||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||
} else {
|
||||
// Icon is missing, we must download it
|
||||
downloader->downloadUrl(line.at(1)+"/favicon.ico");
|
||||
}
|
||||
if(installed_engines.value(id, true))
|
||||
setRowColor(i, "green");
|
||||
else
|
||||
setRowColor(i, "red");
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> engineSelectDlg::findItemsWithUrl(QString url){
|
||||
QList<QTreeWidgetItem*> res;
|
||||
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
|
||||
if(url.startsWith(item->text(ENGINE_URL), Qt::CaseInsensitive))
|
||||
res << item;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id){
|
||||
QList<QTreeWidgetItem*> res;
|
||||
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
|
||||
if(id == item->text(ENGINE_ID))
|
||||
return item;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
|
||||
float old_version = misc::getPluginVersion(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
|
||||
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
|
||||
return (new_version > old_version);
|
||||
}
|
||||
|
||||
#ifdef HAVE_ZZIP
|
||||
void engineSelectDlg::installZipPlugin(QString path) {
|
||||
QStringList plugins;
|
||||
QStringList favicons;
|
||||
ZZIP_DIR* dir = zzip_dir_open(path.toUtf8().data(), 0);
|
||||
if(!dir) {
|
||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("Search engine plugin archive could not be read."));
|
||||
return;
|
||||
}
|
||||
ZZIP_DIRENT dirent;
|
||||
while(zzip_dir_read(dir, &dirent)) {
|
||||
/* show info for first file */
|
||||
QString name(dirent.d_name);
|
||||
if(name.endsWith(".py", Qt::CaseInsensitive)) {
|
||||
plugins << name;
|
||||
} else {
|
||||
if(name.endsWith(".png", Qt::CaseInsensitive)) {
|
||||
favicons << name;
|
||||
}
|
||||
}
|
||||
}
|
||||
QString plugin;
|
||||
std::cout << dirent.d_name << std::endl;
|
||||
ZZIP_FILE* fp = zzip_file_open(dir, dirent.d_name, 0);
|
||||
if (fp) {
|
||||
char buf[10];
|
||||
zzip_ssize_t len = zzip_file_read(fp, buf, 10);
|
||||
if (len) {
|
||||
/* show head of README */
|
||||
std::cout << buf;
|
||||
}
|
||||
zzip_file_close(fp);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
foreach(plugin, plugins) {
|
||||
QString plugin_name = plugin.split(QDir::separator()).last();
|
||||
plugin_name.chop(3); // Remove .py extension
|
||||
qDebug("Detected plugin %s in archive", plugin_name.toUtf8().data());
|
||||
ZZIP_FILE* fp = zzip_file_open(dir, plugin.toUtf8().data(), 0);
|
||||
if(fp) {
|
||||
QTemporaryFile *tmpfile = new QTemporaryFile();
|
||||
QString tmpPath;
|
||||
// Write file
|
||||
if(tmpfile->open()) {
|
||||
tmpPath = tmpfile->fileName();
|
||||
char buf[255];
|
||||
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
|
||||
while(len) {
|
||||
tmpfile->write(buf, len);
|
||||
len = zzip_file_read(fp, buf, 255);
|
||||
}
|
||||
zzip_file_close(fp);
|
||||
tmpfile->close();
|
||||
} else {
|
||||
qDebug("Could not open tmp file");
|
||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
delete tmpfile;
|
||||
continue;
|
||||
}
|
||||
// Install plugin
|
||||
installPlugin(tmpPath, plugin_name);
|
||||
qDebug("installPlugin() finished");
|
||||
delete tmpfile;
|
||||
qDebug("Deleted tmpfile");
|
||||
} else {
|
||||
qDebug("Cannot read file in archive");
|
||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
}
|
||||
}
|
||||
QString favicon;
|
||||
foreach(favicon, favicons) {
|
||||
qDebug("Detected favicon %s in archive", favicon.toUtf8().data());
|
||||
// Ok we have a favicon here
|
||||
QString plugin_name = favicon.split(QDir::separator()).last();
|
||||
plugin_name.chop(4); // Remove .png extension
|
||||
if(!QFile::exists(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py"))
|
||||
continue;
|
||||
// Check if we already have a favicon for this plugin
|
||||
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".png";
|
||||
if(QFile::exists(iconPath)) continue;
|
||||
ZZIP_FILE* fp = zzip_file_open(dir, favicon.toUtf8().data(), 0);
|
||||
if(fp) {
|
||||
QFile dest_icon(iconPath);
|
||||
// Write icon
|
||||
if(dest_icon.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
char buf[255];
|
||||
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
|
||||
while(len) {
|
||||
dest_icon.write(buf, len);
|
||||
len = zzip_file_read(fp, buf, 255);
|
||||
}
|
||||
zzip_file_close(fp);
|
||||
dest_icon.close();
|
||||
// Update icon in list
|
||||
QTreeWidgetItem *item = findItemWithID(plugin_name);
|
||||
Q_ASSERT(item);
|
||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
zzip_dir_close(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
|
||||
qDebug("Asked to install plugin at %s", path.toUtf8().data());
|
||||
float new_version = misc::getPluginVersion(path);
|
||||
qDebug("Version to be installed: %.2f", new_version);
|
||||
if(!isUpdateNeeded(plugin_name, new_version)) {
|
||||
qDebug("Apparently update it not needed, we have a more recent version");
|
||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
return;
|
||||
}
|
||||
// Process with install
|
||||
QString dest_path = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
|
||||
bool update = false;
|
||||
if(QFile::exists(dest_path)) {
|
||||
// Backup in case install fails
|
||||
QFile::copy(dest_path, dest_path+".bak");
|
||||
QFile::remove(dest_path);
|
||||
update = true;
|
||||
}
|
||||
// Copy the plugin
|
||||
QFile::copy(path, dest_path);
|
||||
// Check if this was correctly installed
|
||||
if(!checkInstalled(plugin_name)) {
|
||||
if(update) {
|
||||
// Remove broken file
|
||||
QFile::remove(dest_path);
|
||||
// restore backup
|
||||
QFile::copy(dest_path+".bak", dest_path);
|
||||
QFile::remove(dest_path+".bak");
|
||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
return;
|
||||
} else {
|
||||
// Remove broken file
|
||||
QFile::remove(dest_path);
|
||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Install was successful, remove backup
|
||||
if(update) {
|
||||
QFile::remove(dest_path+".bak");
|
||||
}
|
||||
// Refresh plugin list
|
||||
loadSupportedSearchEngines();
|
||||
if(update) {
|
||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
return;
|
||||
} else {
|
||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void engineSelectDlg::on_installButton_clicked() {
|
||||
pluginSourceDlg *dlg = new pluginSourceDlg(this);
|
||||
connect(dlg, SIGNAL(askForLocalFile()), this, SLOT(askForLocalPlugin()));
|
||||
connect(dlg, SIGNAL(askForUrl()), this, SLOT(askForPluginUrl()));
|
||||
}
|
||||
|
||||
void engineSelectDlg::askForPluginUrl() {
|
||||
bool ok;
|
||||
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
|
||||
tr("URL:"), QLineEdit::Normal,
|
||||
"http://", &ok);
|
||||
if (ok && !url.isEmpty())
|
||||
downloader->downloadUrl(url);
|
||||
}
|
||||
|
||||
void engineSelectDlg::askForLocalPlugin() {
|
||||
QStringList pathsList = QFileDialog::getOpenFileNames(0,
|
||||
tr("Select search plugins"), QDir::homePath(),
|
||||
#ifdef HAVE_ZZIP
|
||||
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py *.zip)"));
|
||||
#else
|
||||
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py)"));
|
||||
#endif
|
||||
QString path;
|
||||
foreach(path, pathsList) {
|
||||
if(path.endsWith(".py", Qt::CaseInsensitive)) {
|
||||
QString plugin_name = path.split(QDir::separator()).last();
|
||||
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
||||
installPlugin(path, plugin_name);
|
||||
}
|
||||
#ifdef HAVE_ZZIP
|
||||
else {
|
||||
if(path.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||
installZipPlugin(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool engineSelectDlg::parseVersionsFile(QString versions_file, QString updateServer) {
|
||||
qDebug("Checking if update is needed");
|
||||
bool file_correct = false;
|
||||
QFile versions(versions_file);
|
||||
if(!versions.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||
qDebug("* Error: Could not read versions.txt file");
|
||||
return false;
|
||||
}
|
||||
bool updated = false;
|
||||
while(!versions.atEnd()) {
|
||||
QByteArray line = versions.readLine();
|
||||
line.replace("\n", "");
|
||||
line = line.trimmed();
|
||||
if(line.isEmpty()) continue;
|
||||
if(line.startsWith("#")) continue;
|
||||
QList<QByteArray> list = line.split(' ');
|
||||
if(list.size() != 2) continue;
|
||||
QString plugin_name = QString(list.first());
|
||||
if(!plugin_name.endsWith(":")) continue;
|
||||
plugin_name.chop(1); // remove trailing ':'
|
||||
bool ok;
|
||||
float version = list.last().toFloat(&ok);
|
||||
qDebug("read line %s: %.2f", plugin_name.toUtf8().data(), version);
|
||||
if(!ok) continue;
|
||||
file_correct = true;
|
||||
if(isUpdateNeeded(plugin_name, version)) {
|
||||
qDebug("Plugin: %s is outdated", plugin_name.toUtf8().data());
|
||||
// Downloading update
|
||||
downloader->downloadUrl(updateServer+plugin_name+".pyqBT"); // Actually this is really a .py
|
||||
downloader->downloadUrl(updateServer+plugin_name+".png");
|
||||
updated = true;
|
||||
}else {
|
||||
qDebug("Plugin: %s is up to date", plugin_name.toUtf8().data());
|
||||
}
|
||||
}
|
||||
// Close file
|
||||
versions.close();
|
||||
// Clean up tmp file
|
||||
QFile::remove(versions_file);
|
||||
if(file_correct && !updated) {
|
||||
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
|
||||
}
|
||||
return file_correct;
|
||||
}
|
||||
|
||||
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
|
||||
qDebug("engineSelectDlg received %s", url.toUtf8().data());
|
||||
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||
// Icon downloaded
|
||||
QImage fileIcon;
|
||||
#ifdef HAVE_MAGICK
|
||||
try{
|
||||
QFile::copy(filePath, filePath+".ico");
|
||||
Image image(QDir::cleanPath(filePath+".ico").toUtf8().data());
|
||||
// Convert to PNG since we can't read ICO format
|
||||
image.magick("PNG");
|
||||
// Resize to 16x16px
|
||||
image.sample(Geometry(16, 16));
|
||||
image.write(filePath.toUtf8().data());
|
||||
QFile::remove(filePath+".ico");
|
||||
}catch(Magick::Exception &error_){
|
||||
qDebug("favicon conversion to PNG failure: %s", error_.what());
|
||||
}
|
||||
#endif
|
||||
if(fileIcon.load(filePath)) {
|
||||
QList<QTreeWidgetItem*> items = findItemsWithUrl(url);
|
||||
QTreeWidgetItem *item;
|
||||
foreach(item, items){
|
||||
QString id = item->text(ENGINE_ID);
|
||||
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
||||
QFile::copy(filePath, iconPath);
|
||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||
}
|
||||
}
|
||||
// Delete tmp file
|
||||
QFile::remove(filePath);
|
||||
return;
|
||||
}
|
||||
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
|
||||
if(!parseVersionsFile(filePath, "http://www.dchris.eu/search_engine/")) {
|
||||
qDebug("Primary update server failed, try secondary");
|
||||
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
|
||||
}
|
||||
QFile::remove(filePath);
|
||||
return;
|
||||
}
|
||||
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
|
||||
if(!parseVersionsFile(filePath, "http://hydr0g3n.free.fr/search_engine/")) {
|
||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
||||
}
|
||||
QFile::remove(filePath);
|
||||
return;
|
||||
}
|
||||
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
|
||||
QString plugin_name = url.split('/').last();
|
||||
plugin_name.replace(".pyqBT", "");
|
||||
plugin_name.replace(".py", "");
|
||||
installPlugin(filePath, plugin_name);
|
||||
QFile::remove(filePath);
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_ZZIP
|
||||
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||
installZipPlugin(filePath);
|
||||
QFile::remove(filePath);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
|
||||
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||
qDebug("Could not download favicon: %s, reason: %s", url.toUtf8().data(), reason.toUtf8().data());
|
||||
return;
|
||||
}
|
||||
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
|
||||
// Primary update server failed, try secondary
|
||||
qDebug("Primary update server failed, try secondary");
|
||||
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
|
||||
return;
|
||||
}
|
||||
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
|
||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
||||
return;
|
||||
}
|
||||
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
|
||||
// a plugin update download has been failed
|
||||
QString plugin_name = url.split('/').last();
|
||||
plugin_name.replace(".pyqBT", "", Qt::CaseInsensitive);
|
||||
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
}
|
||||
#ifdef HAVE_ZZIP
|
||||
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||
QString plugin_name = url.split('/').last();
|
||||
plugin_name.replace(".zip", "", Qt::CaseInsensitive);
|
||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||
}
|
||||
#endif
|
||||
}
|
76
src/engineSelectDlg.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_SELECT_DLG_H
|
||||
#define ENGINE_SELECT_DLG_H
|
||||
|
||||
#include "ui_engineSelect.h"
|
||||
|
||||
class downloadThread;
|
||||
class QDropEvent;
|
||||
|
||||
class engineSelectDlg : public QDialog, public Ui::engineSelect{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
// Search related
|
||||
QHash<QString, bool> installed_engines;
|
||||
downloadThread *downloader;
|
||||
|
||||
public:
|
||||
engineSelectDlg(QWidget *parent);
|
||||
~engineSelectDlg();
|
||||
QList<QTreeWidgetItem*> findItemsWithUrl(QString url);
|
||||
QTreeWidgetItem* findItemWithID(QString id);
|
||||
|
||||
protected:
|
||||
bool parseVersionsFile(QString versions_file, QString updateServer);
|
||||
bool isUpdateNeeded(QString plugin_name, float new_version) const;
|
||||
bool checkInstalled(QString plugin_name) const;
|
||||
|
||||
signals:
|
||||
void enginesChanged();
|
||||
|
||||
protected slots:
|
||||
void saveSettings();
|
||||
void on_closeButton_clicked();
|
||||
void loadSupportedSearchEngines(bool first=false);
|
||||
void toggleEngineState(QTreeWidgetItem*, int);
|
||||
void setRowColor(int row, QString color);
|
||||
void processDownloadedFile(QString url, QString filePath);
|
||||
void handleDownloadFailure(QString url, QString reason);
|
||||
void displayContextMenu(const QPoint& pos);
|
||||
void enableSelection();
|
||||
void disableSelection();
|
||||
void on_actionUninstall_triggered();
|
||||
void on_updateButton_clicked();
|
||||
void on_installButton_clicked();
|
||||
void dropEvent(QDropEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void installPlugin(QString plugin_path, QString plugin_name);
|
||||
void askForLocalPlugin();
|
||||
void askForPluginUrl();
|
||||
#ifdef HAVE_ZZIP
|
||||
void installZipPlugin(QString path);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
|
@ -12,7 +12,7 @@
|
|||
<file>Icons/downarrow.png</file>
|
||||
<file>Icons/description.png</file>
|
||||
<file>Icons/qbittorrent16.png</file>
|
||||
<file>Icons/exec.png</file>
|
||||
<file>Icons/file.png</file>
|
||||
<file>Icons/systemtray.png</file>
|
||||
<file>Icons/unhappy.png</file>
|
||||
<file>Icons/filter.png</file>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<file>Icons/style.png</file>
|
||||
<file>Icons/wizard.png</file>
|
||||
<file>Icons/password.png</file>
|
||||
<file>Icons/rss.png</file>
|
||||
<file>Icons/gear.png</file>
|
||||
<file>Icons/sphere2.png</file>
|
||||
<file>Icons/smile.png</file>
|
||||
<file>Icons/loading.png</file>
|
||||
|
@ -36,6 +36,17 @@
|
|||
<file>Icons/add_file.png</file>
|
||||
<file>Icons/home.png</file>
|
||||
<file>Icons/splash.png</file>
|
||||
<file>Icons/rss32.png</file>
|
||||
<file>Icons/rss16.png</file>
|
||||
<file>Icons/unsubscribe.png</file>
|
||||
<file>Icons/subscribe.png</file>
|
||||
<file>Icons/unsubscribe16.png</file>
|
||||
<file>Icons/subscribe16.png</file>
|
||||
<file>Icons/bt_settings.png</file>
|
||||
<file>Icons/star.png</file>
|
||||
<file>Icons/configure.png</file>
|
||||
<file>Icons/download.png</file>
|
||||
<file>Icons/folder.png</file>
|
||||
<file>Icons/flags/portugal.png</file>
|
||||
<file>Icons/flags/france.png</file>
|
||||
<file>Icons/flags/ukraine.png</file>
|
||||
|
@ -46,7 +57,6 @@
|
|||
<file>Icons/flags/slovakia.png</file>
|
||||
<file>Icons/flags/spain.png</file>
|
||||
<file>Icons/flags/finland.png</file>
|
||||
<file>Icons/flags/china_hong_kong.png</file>
|
||||
<file>Icons/flags/spain_catalunya.png</file>
|
||||
<file>Icons/flags/poland.png</file>
|
||||
<file>Icons/flags/hungary.png</file>
|
||||
|
|