Imported Upstream version 3.2.5

parent 6f340da6
* Sat Oct 31 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.5
- BUGFIX: Fix difficult to reproduce crash. (glassez)
- OTHER: Fix Windows' Qt5 build. (Gelmir)
* Sat Oct 10 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.4
- FEATURE: Select the file of single file torrents when opening destination folder (pmzqla)
- BUGFIX: Fix crash with invalid favicon. Closes #3632. (glassez)
- BUGFIX: Try to download favicon.png when the download of favicon.ico fails (pmzqla)
- BUGFIX: Try to avoid loading a corrupted configuration file. Also log errors encountered while saving/loading the configuration. Closes #3503. (sledgehammer999)
- BUGFIX: Allow adding torrent link from Torcache (jsayol)
- BUGFIX: Don't limit the number of torrents that can be announced to the tracker/dht/lsd. Closes #3473. (sledgehammer999)
- BUGFIX: Fix potential crash when memory allocation failed. Closes #3877. (Chocobo1)
- COSMETIC: Change Queue buttons order in the Toolbar (GUI & Web UI) (ngosang)
- COSMETIC: Move option "Ignore transfer limits on local network" to Speed page (Chocobo1)
- COSMETIC: Move option "Confirm torrent deletion" to Behavior page (Chocobo1)
- COSMETIC: Fix typos. Make `μTP` untranslatable. Use American variation of words. Closes #3654. (sledgehammer999)
- COSMETIC: Optimize text color for dark themes. Closes #3633 and #3815. (sledgehammer999)
- COSMETIC: Show current label in the torrent context menu. Closes #3776. (sledgehammer999)
- WEBUI: Add save_path to /query/torrents (Casey Bodley)
- WEBUI: Bump API_VERSION to 5
- SEARCH: Fix python detection when the 'Anaconda' software is installed. Closes #3731. (sledgehammer999)
- RSS: Handle magnet links as torrents instead of news URLs. Closes #3560 (ngosang)
- RSS: Trim elements text in RSS articles (ngosang)
- RSS: Fix contextual menu in RSS torrents list (ngosang)
- RSS: Improve error handling when a RSS feed doesn't contain torrents (ngosang)
- RSS: More precise message and code simplification in RSS feeds deletion (ngosang)
- RSS: Don't hide the elements in Unread list when clicked (ngosang)
- RSS: Allow multiple selection in RSS torrents list (ngosang)
- RSS: Simplify string translation (ngosang)
- RSS: Handle more types of RSS feeds (ngosang)
- RSS: Fix RSS panel position not saved (ngosang)
- RSS: Fix forgetting label changes to first item in RSS rule list. (Gelmir)
- RSS: Add label to UI when a new one is creating during rule addition. (Gelmir)
- RSS: Removes refresh message when adding a new feed (ngosang)
- RSS: Fix RSS crash when deleting RSS feeds. Closes #997, #2152, #2461, #3718, #3747, #3766, #3806, #3814, #3829 and #3846. (ngosang)
- RSS: Sort labels in RSS Downloader dialog, closes #3140. (Chocobo1)
- WINDOWS: Correctly show german letters in the installer. Closes #3574, #3566. (sledgehammer999)
- WINDOWS: Fix file selection on Explorer when the filename contains weird characters. Closes #3185. (sledgehammer999)
- WINDOWS: Fix wrong default download directory in Windows. Closes #2625. (Chocobo1)
- WINDOWS: Fix German translation of the installer. (netswap)
- LINUX: Fix broken .desktop file icon for some locales. See #3905. (sledgehammer999)
- OTHER: Fix ppc64le detection during configure (sledgehammer999)
- OTHER: Don't use sed in configure. Closes #3169. (pmzqla)
- OTHER: Fix broken donation link. Closes #3771. (sledgehammer999)
- OTHER: Add forum link in README. Closes #3853. (sledgehammer999)
- OTHER: New translation: Esperanto
- OTHER: Fix Qt5 nox build on non-Windows. (sledgehammer999)
* Sun Aug 02 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.3 * Sun Aug 02 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.3
- BUGFIX: Fix crash when closing a search tab while search is running (pmzqla) - BUGFIX: Fix crash when closing a search tab while search is running (pmzqla)
- SEARCH: Other minor search fixes and improvements (pmzqla) - SEARCH: Other minor search fixes and improvements (pmzqla)
......
...@@ -14,7 +14,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4 ...@@ -14,7 +14,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4
- pkg-config executable - pkg-config executable
- libtorrent-rasterbar by Arvid Norberg (>= 0.15.0) - libtorrent-rasterbar by Arvid Norberg (>= 0.16.19 OR >= 1.0.6)
-> http://www.libtorrent.net -> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name. Be careful: another library (the one used by rTorrent) uses a similar name.
...@@ -44,7 +44,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4 ...@@ -44,7 +44,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4
- pkg-config executable - pkg-config executable
- libtorrent-rasterbar by Arvid Norberg (>= v0.15.0) - libtorrent-rasterbar by Arvid Norberg (>= 0.16.19 OR >= v1.0.6)
-> http://www.libtorrent.net -> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name. Be careful: another library (the one used by rTorrent) uses a similar name.
......
...@@ -32,6 +32,9 @@ http://www.qbittorrent.org ...@@ -32,6 +32,9 @@ http://www.qbittorrent.org
or our wiki here: or our wiki here:
http://wiki.qbittorrent.org http://wiki.qbittorrent.org
Use the forum for troubleshooting before reporting bugs:
http://forum.qbittorrent.org
Please report any bug (or feature request) to: Please report any bug (or feature request) to:
http://bugs.qbittorrent.org http://bugs.qbittorrent.org
......
...@@ -5808,8 +5808,11 @@ extract() { ...@@ -5808,8 +5808,11 @@ extract() {
return 1 return 1
fi fi
# BSD sed needs an actual newline character in the substitute command
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces # Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e 's: -:\n:g' -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//') string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//')
SAVEIFS=$IFS SAVEIFS=$IFS
IFS=$(printf "\n\b") IFS=$(printf "\n\b")
for i in $string; do for i in $string; do
......
...@@ -258,8 +258,11 @@ extract() { ...@@ -258,8 +258,11 @@ extract() {
return 1 return 1
fi fi
# BSD sed needs an actual newline character in the substitute command
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces # Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e 's: -:\n:g' -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//') string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
SAVEIFS=$IFS SAVEIFS=$IFS
IFS=$(printf "\n\b") IFS=$(printf "\n\b")
for i in $string; do for i in $string; do
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.2.3</string> <string>3.2.5</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>qBit</string> <string>qBit</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
......
;Installer strings ;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)" ;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_GERMAN} "qBittorrent (erforderlich)" LangString inst_qbt_req ${LANG_GERMAN} "qBittorrent (erforderlich)"
...@@ -44,6 +44,6 @@ LangString remove_cache ${LANG_GERMAN} "Torrents und zwischengespeicherte Daten ...@@ -44,6 +44,6 @@ LangString remove_cache ${LANG_GERMAN} "Torrents und zwischengespeicherte Daten
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_GERMAN} "qBittorrent läuft gerade. Bitte das Programm vor der Deinstallation beenden." LangString uninst_warning ${LANG_GERMAN} "qBittorrent läuft gerade. Bitte das Programm vor der Deinstallation beenden."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_GERMAN} "Dateiverknüfung mit .torrent-Dateien konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:" LangString uninst_tor_warn ${LANG_GERMAN} "Dateiverknüpfung mit .torrent-Dateien konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_GERMAN} "Dateiverknüfung mit Magnet-Links konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:" LangString uninst_mag_warn ${LANG_GERMAN} "Dateiverknüpfung mit Magnet-Links konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
...@@ -19,7 +19,7 @@ XPStyle on ...@@ -19,7 +19,7 @@ XPStyle on
!define CSIDL_APPDATA '0x1A' ;Application Data path !define CSIDL_APPDATA '0x1A' ;Application Data path
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path !define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
!define PROG_VERSION "3.2.3" !define PROG_VERSION "3.2.5"
!define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt) !define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
# and this notice are preserved. This file is offered as-is, without any # and this notice are preserved. This file is offered as-is, without any
# warranty. # warranty.
#serial 23 #serial 26
AC_DEFUN([AX_BOOST_BASE], AC_DEFUN([AX_BOOST_BASE],
[ [
...@@ -92,8 +92,11 @@ if test "x$want_boost" = "xyes"; then ...@@ -92,8 +92,11 @@ if test "x$want_boost" = "xyes"; then
libsubdirs="lib" libsubdirs="lib"
ax_arch=`uname -m` ax_arch=`uname -m`
case $ax_arch in case $ax_arch in
x86_64|ppc64|s390x|sparc64|aarch64) x86_64)
libsubdirs="lib64 lib lib64" libsubdirs="lib64 libx32 lib lib64"
;;
ppc64|s390x|sparc64|aarch64|ppc64le)
libsubdirs="lib64 lib lib64 ppc64le"
;; ;;
esac esac
...@@ -170,6 +173,10 @@ if test "x$want_boost" = "xyes"; then ...@@ -170,6 +173,10 @@ if test "x$want_boost" = "xyes"; then
dnl if we found no boost with system layout we search for boost libraries dnl if we found no boost with system layout we search for boost libraries
dnl built and installed without the --layout=system option or for a staged(not installed) version dnl built and installed without the --layout=system option or for a staged(not installed) version
if test "x$succeeded" != "xyes"; then if test "x$succeeded" != "xyes"; then
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
BOOST_CPPFLAGS=
BOOST_LDFLAGS=
_version=0 _version=0
if test "$ac_boost_path" != ""; then if test "$ac_boost_path" != ""; then
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
...@@ -182,6 +189,12 @@ if test "x$want_boost" = "xyes"; then ...@@ -182,6 +189,12 @@ if test "x$want_boost" = "xyes"; then
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
done done
dnl if nothing found search for layout used in Windows distributions
if test -z "$BOOST_CPPFLAGS"; then
if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then
BOOST_CPPFLAGS="-I$ac_boost_path"
fi
fi
fi fi
else else
if test "$cross_compiling" != yes; then if test "$cross_compiling" != yes; then
......
...@@ -193,6 +193,8 @@ QNetworkReply* DownloadThread::downloadUrl(const QString &url, const QList<QNetw ...@@ -193,6 +193,8 @@ QNetworkReply* DownloadThread::downloadUrl(const QString &url, const QList<QNetw
QNetworkRequest request(qurl); QNetworkRequest request(qurl);
// Spoof Firefox 38 user agent to avoid web server banning // Spoof Firefox 38 user agent to avoid web server banning
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0"); request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0");
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request.setRawHeader("Referer", request.url().toEncoded().data());
qDebug("Downloading %s...", request.url().toEncoded().data()); qDebug("Downloading %s...", request.url().toEncoded().data());
qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size()); qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size());
for (int i=0; i<m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) { for (int i=0; i<m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
...@@ -276,7 +278,7 @@ QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) { ...@@ -276,7 +278,7 @@ QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
case QNetworkReply::ProxyTimeoutError: case QNetworkReply::ProxyTimeoutError:
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
case QNetworkReply::ProxyAuthenticationRequiredError: case QNetworkReply::ProxyAuthenticationRequiredError:
return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered"); return tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
case QNetworkReply::ContentAccessDenied: case QNetworkReply::ContentAccessDenied:
return tr("The access to the remote content was denied (401)"); return tr("The access to the remote content was denied (401)");
case QNetworkReply::ContentOperationNotPermittedError: case QNetworkReply::ContentOperationNotPermittedError:
......
...@@ -62,13 +62,15 @@ ...@@ -62,13 +62,15 @@
#include <winbase.h> #include <winbase.h>
#endif #endif
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
#ifndef DISABLE_GUI
#include <QDesktopServices> #include <QDesktopServices>
#endif
#else #else
#include <QStandardPaths> #include <QStandardPaths>
#endif #endif
#endif
using namespace libtorrent; using namespace libtorrent;
...@@ -464,62 +466,76 @@ QString fsutils::QDesktopServicesCacheLocation() { ...@@ -464,62 +466,76 @@ QString fsutils::QDesktopServicesCacheLocation() {
return result; return result;
} }
QString fsutils::QDesktopServicesDownloadLocation() { QString fsutils::QDesktopServicesDownloadLocation()
#if defined(Q_OS_WIN) || defined(Q_OS_OS2) {
// as long as it stays WinXP like we do the same on OS/2 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
// TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads #if defined(Q_OS_WIN)
// instead of hardcoding "Downloads" if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
// Unfortunately, this would break compatibility with WinXP return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) QCoreApplication::translate("fsutils", "Downloads"));
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath( #endif
QCoreApplication::translate("fsutils", "Downloads")); return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
#else #else
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads")); #if defined(Q_OS_OS2)
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
#endif #endif
#if defined(Q_OS_WIN)
// as long as it stays WinXP like we do the same on OS/2
// TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads
// instead of hardcoding "Downloads"
// Unfortunately, this would break compatibility with WinXP
if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
else
return QDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)).absoluteFilePath("Downloads");
#endif #endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
QString save_path; QString save_path;
// Default save path on Linux // Default save path on Linux
QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData()); QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData());
if (config_path.isEmpty()) if (config_path.isEmpty())
config_path = QDir::home().absoluteFilePath(".config"); config_path = QDir::home().absoluteFilePath(".config");
QString user_dirs_file = config_path + "/user-dirs.dirs"; QString user_dirs_file = config_path + "/user-dirs.dirs";
if (QFile::exists(user_dirs_file)) { if (QFile::exists(user_dirs_file)) {
QSettings settings(user_dirs_file, QSettings::IniFormat); QSettings settings(user_dirs_file, QSettings::IniFormat);
// We need to force UTF-8 encoding here since this is not // We need to force UTF-8 encoding here since this is not
// the default for Ini files. // the default for Ini files.
settings.setIniCodec("UTF-8"); settings.setIniCodec("UTF-8");
QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString(); QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString();
if (!xdg_download_dir.isEmpty()) { if (!xdg_download_dir.isEmpty()) {
// Resolve $HOME environment variables // Resolve $HOME environment variables
xdg_download_dir.replace("$HOME", QDir::homePath()); xdg_download_dir.replace("$HOME", QDir::homePath());
save_path = xdg_download_dir; save_path = xdg_download_dir;
qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path; qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path;
}
} }
}
// Fallback // Fallback
if (!save_path.isEmpty() && !QFile::exists(save_path)) { if (!save_path.isEmpty() && !QFile::exists(save_path)) {
QDir().mkpath(save_path); QDir().mkpath(save_path);
} }
if (save_path.isEmpty() || !QFile::exists(save_path)) { if (save_path.isEmpty() || !QFile::exists(save_path)) {
save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work"; qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work";
} }
return save_path; return save_path;
#endif #endif
#ifdef Q_OS_MAC #if defined(Q_OS_MAC)
// TODO: How to support this on Mac OS X? // TODO: How to support this on Mac OS?
#endif #endif
// Fallback // Fallback
return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
#endif
} }
QString fsutils::searchEngineLocation() { QString fsutils::searchEngineLocation() {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "core/unicodestrings.h" #include "core/unicodestrings.h"
#include "core/logger.h" #include "core/logger.h"
#include "misc.h" #include "misc.h"
#include "fs_utils.h"
#include <cmath> #include <cmath>
...@@ -52,6 +53,11 @@ ...@@ -52,6 +53,11 @@
#include <QDesktopWidget> #include <QDesktopWidget>
#endif #endif
#ifndef DISABLE_GUI
#include <QDesktopServices>
#include <QProcess>
#endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
#include <PowrProf.h> #include <PowrProf.h>
...@@ -332,9 +338,15 @@ QString misc::pythonVersionComplete() { ...@@ -332,9 +338,15 @@ QString misc::pythonVersionComplete() {
QByteArray output = pythonProc.readAllStandardOutput(); QByteArray output = pythonProc.readAllStandardOutput();
if (output.isEmpty()) if (output.isEmpty())
output = pythonProc.readAllStandardError(); output = pythonProc.readAllStandardError();
const QByteArray versionStr = output.split(' ').last();
version = versionStr.trimmed(); // Software 'Anaconda' installs its own python interpreter
Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python version: %1").arg(version), Log::INFO); // and `python --version` returns a string like this:
// `Python 3.4.3 :: Anaconda 2.3.0 (64-bit)`
const QList<QByteArray> verSplit = output.split(' ');
if (verSplit.size() > 1) {
version = verSplit.at(1).trimmed();
Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python version: %1").arg(version), Log::INFO);
}
} }
} }
return version; return version;
...@@ -592,7 +604,7 @@ QString misc::toQString(time_t t, Qt::DateFormat f) ...@@ -592,7 +604,7 @@ QString misc::toQString(time_t t, Qt::DateFormat f)
} }
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
bool misc::naturalSort(QString left, QString right, bool &result) // uses lessThan comparison bool misc::naturalSort(const QString &left, const QString &right, bool &result) // uses lessThan comparison
{ // Return value indicates if functions was successful { // Return value indicates if functions was successful
// result argument will contain actual comparison result if function was successful // result argument will contain actual comparison result if function was successful
int posL = 0; int posL = 0;
...@@ -651,6 +663,83 @@ bool misc::naturalSort(QString left, QString right, bool &result) // uses less ...@@ -651,6 +663,83 @@ bool misc::naturalSort(QString left, QString right, bool &result) // uses less
return false; return false;
} }
misc::NaturalCompare::NaturalCompare()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
#endif
}
bool misc::NaturalCompare::operator()(const QString &l, const QString &r)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(l, r);
#endif
return (m_collator.compare(l, r) < 0);
#else
return lessThan(l, r);
#endif
}
bool misc::NaturalCompare::lessThan(const QString &left, const QString &right)
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if (posL == left.size() || posR == right.size())
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = left[posL].toLower();
QChar rightChar = right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
}
#endif #endif
// to send numbers instead of strings with suffixes // to send numbers instead of strings with suffixes
...@@ -692,6 +781,90 @@ void misc::loadBencodedFile(const QString &filename, std::vector<char> &buffer, ...@@ -692,6 +781,90 @@ void misc::loadBencodedFile(const QString &filename, std::vector<char> &buffer,
lazy_bdecode(&buffer[0], &buffer[0] + buffer.size(), entry, ec); lazy_bdecode(&buffer[0], &buffer[0] + buffer.size(), entry, ec);
} }
#ifndef DISABLE_GUI
// Open the given path with an appropriate application
void misc::openPath(const QString& absolutePath)
{
const QString path = fsutils::fromNativePath(absolutePath);
// Hack to access samba shares with QDesktopServices::openUrl
if (path.startsWith("//"))
QDesktopServices::openUrl(fsutils::toNativePath("file:" + path));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
// Open the parent directory of the given path with a file manager and select
// (if possible) the item at the given path
void misc::openFolderSelect(const QString& absolutePath)
{
const QString path = fsutils::fromNativePath(absolutePath);
#ifdef Q_OS_WIN
if (QFileInfo(path).exists()) {
// Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select"
// Dir separators MUST be win-style slashes
// QProcess::startDetached() has an obscure bug. If the path has
// no spaces and a comma(and maybe other special characters) it doesn't
// get wrapped in quotes. So explorer.exe can't find the correct path and
// displays the default one. If we wrap the path in quotes and pass it to
// QProcess::startDetached() explorer.exe still shows the default path. In
// this case QProcess::startDetached() probably puts its own quotes around ours.
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo;
::ZeroMemory(&processInfo, sizeof(processInfo));
QString cmd = QString("explorer.exe /select,\"%1\"").arg(fsutils::toNativePath(absolutePath));
LPWSTR lpCmd = new WCHAR[cmd.size() + 1];
cmd.toWCharArray(lpCmd);
lpCmd[cmd.size()] = 0;
bool ret = ::CreateProcessW(NULL, lpCmd, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
delete [] lpCmd;
if (ret) {
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
}
}
else {
// If the item to select doesn't exist, try to open its parent
openPath(path.left(path.lastIndexOf("/")));
}
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (QFileInfo(path).exists()) {
QProcess proc;
QString output;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
proc.waitForFinished();
output = proc.readLine().simplified();
if (output == "dolphin.desktop" || output == "org.kde.dolphin.desktop")
proc.startDetached("dolphin", QStringList() << "--select" << fsutils::toNativePath(path));
else if (output == "nautilus.desktop" || output == "org.gnome.Nautilus.desktop"
|| output == "nautilus-folder-handler.desktop")
proc.startDetached("nautilus", QStringList() << "--no-desktop" << fsutils::toNativePath(path));
else if (output == "caja-folder-handler.desktop")
proc.startDetached("caja", QStringList() << "--no-desktop" << fsutils::toNativePath(path));
else if (output == "nemo.desktop")
proc.startDetached("nemo", QStringList() << "--no-desktop" << fsutils::toNativePath(path));
else if (output == "konqueror.desktop" || output == "kfmclient_dir.desktop")
proc.startDetached("konqueror", QStringList() << "--select" << fsutils::toNativePath(path));
else
openPath(path.left(path.lastIndexOf("/")));
}
else {
// If the item to select doesn't exist, try to open its parent
openPath(path.left(path.lastIndexOf("/")));
}
#else
openPath(path.left(path.lastIndexOf("/")));
#endif
}
#endif // DISABLE_GUI
namespace { namespace {
// Trick to get a portable sleep() function // Trick to get a portable sleep() function
class SleeperThread: public QThread { class SleeperThread: public QThread {
......
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
#include <QUrl> #include <QUrl>
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
#include <QIcon> #include <QIcon>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#include <QCollator>
#endif
#endif #endif
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
...@@ -106,7 +109,19 @@ namespace misc ...@@ -106,7 +109,19 @@ namespace misc
QString accurateDoubleToString(const double &n, const int &precision); QString accurateDoubleToString(const double &n, const int &precision);
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
bool naturalSort(QString left, QString right, bool& result); bool naturalSort(const QString &left, const QString &right, bool &result);
class NaturalCompare
{
public:
NaturalCompare();
bool operator()(const QString &l, const QString &r);
bool lessThan(const QString &left, const QString &right);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
private:
QCollator m_collator;
#endif
};