Compare commits

...

83 Commits

Author SHA1 Message Date
sledgehammer999
d2f3d1ec2c Bump to 4.0.4 2018-02-16 00:46:34 +02:00
sledgehammer999
bc6e3ae40d Sync translations from Transifex and run lupdate. 2018-02-09 21:14:58 +02:00
sledgehammer999
b02e239f7c Set default file log size to 65 KiB and delete backup logs older than 1 month. 2018-02-09 20:50:43 +02:00
Luís Pereira
397cd4bf60 Don't set application name twice
It's an artifact introduced by commit b3c73b7868.
2018-02-09 20:50:43 +02:00
Thomas Piccirello
409f972ad3 Add missing units 2018-02-09 20:50:42 +02:00
Thomas Piccirello
267961ffca Right-align stat values 2018-02-09 20:50:41 +02:00
Thomas Piccirello
dff753c452 Remove invalid subnets directly from list 2018-02-09 20:50:41 +02:00
Thomas Piccirello
ce3bafd30d Perform ip string validation inside setWebUiAuthSubnetWhitelist 2018-02-09 20:50:40 +02:00
Thomas Piccirello
e5538d9f25 Separate subnet whitelist options into two lines 2018-02-09 20:50:39 +02:00
Tom Piccirello
7a8a32b1c3 Remove default case, fix #8302 2018-02-09 20:50:39 +02:00
Vladimir Golovnev (Glassez)
cac5e0391b Remove legacy and corrupted RSS settings 2018-02-09 20:50:38 +02:00
Vladimir Golovnev (Glassez)
726790fa93 Require '#pragma once' in new code 2018-02-09 20:50:37 +02:00
Thomas Piccirello
c9c7a5be53 Add default case 2018-02-09 20:50:36 +02:00
sledgehammer999
1495513cfc Revert "Remove examples from gpl.html. Closes #7749."
The GPL folks are a bit overzealous. See https://www.gnu.org/licenses/gpl-faq.html#GPLOmitPreamble

This reverts commit 5cf4f00824.
2018-02-09 20:50:36 +02:00
sledgehammer999
085afaac14 Sync translations from Transifex and run lupdate. 2018-02-09 20:50:34 +02:00
Thomas Piccirello
d58a54c758 Use switch statement 2018-02-09 20:50:33 +02:00
Thomas Piccirello
78d9bcb6a1 Match webui statuses to gui, closes #7516 2018-02-09 20:50:33 +02:00
Tom Piccirello
1a43cd329d Only add search separators as needed 2018-02-09 20:50:32 +02:00
Jesse Bryan
2fe687eeca transferlist: added a force reannounce option. closes #6448. 2018-02-09 20:50:31 +02:00
Chocobo1
107bd8a54f Torrent creator: raise maximum piece size to 32 MiB 2018-02-09 20:50:31 +02:00
Chocobo1
865df3fcf1 Fix translation context.
Partially addresses #8220.

Also sort include headers
2018-02-09 20:50:30 +02:00
sledgehammer999
cbf9c52462 Update copyright year. 2018-02-09 20:50:29 +02:00
Chocobo1
171e25e962 Fix translation context. Closes #8211. 2018-02-09 20:50:11 +02:00
sledgehammer999
239d14fd10 Update configure version info. 2018-01-03 23:29:59 +02:00
sledgehammer999
0b7a175156 Update libtorrent bottle for Travis. 2018-01-03 23:28:24 +02:00
Chocobo1
b37dbb60b5 Sort filename lists in .pri files 2018-01-03 23:28:18 +02:00
sledgehammer999
d5acd1f210 configure: Parse all compiler related flags together. 2018-01-03 03:18:15 +02:00
sledgehammer999
65eda4a68e Fix splitting of compiler flags in configure.
Autoconf removes a set of [] during script translation, resulting in a wrong sed command.
2018-01-03 03:18:14 +02:00
sledgehammer999
7209881025 Fix constant status of '[F] Downloading'. Closes #7628. 2018-01-03 03:18:13 +02:00
Chocobo1
a5d0a4b619 Fix column size too narrow on resize
Instead of giving a value, just resize to the content size
2018-01-03 03:18:13 +02:00
Chocobo1
79276a8786 Resize dialog size on high DPI monitors 2018-01-03 03:18:12 +02:00
Chocobo1
fa2b645a64 Fix gui issues on high DPI monitor
Fix LineEdit widget size issues
Up-scale the icons on statusbar
Up-scale the icons in options dialog. Closes #7729.
Fix small icons in cookie manager
Fix progress bar height
Fix small icons in confirm delete dialog
Fix small icons in options dialog
Fix small images in about dialog
2018-01-03 03:18:11 +02:00
Chocobo1
4d5d6df734 Tweak spacing 2018-01-03 03:18:10 +02:00
Chocobo1
2c39b69c18 Cleanup
Use Qt5 connect syntax
Reorder include headers
2018-01-03 03:18:10 +02:00
Chocobo1
13c0077e95 Revert "Run external program" function
This revert df95efe33e partially
2018-01-03 03:18:09 +02:00
Vladimir Golovnev (Glassez)
9299e3f371 Don't process new/updated RSS rules when disabled 2018-01-03 03:18:08 +02:00
Thomas Piccirello
b9ddc6ee86 Use https for www.qbittorrent.org 2018-01-03 03:18:07 +02:00
Thomas Piccirello
276856a614 Add Time Active column 2018-01-03 03:18:07 +02:00
Thomas Piccirello
fbd6a8a0da Add Tags columns 2018-01-03 03:18:06 +02:00
Thomas Piccirello
6fc18b4af6 Reposition Total Size column to match gui 2018-01-03 03:18:05 +02:00
Thomas Piccirello
44d633fb68 Make value formatting consistent with GUI 2017-12-24 03:01:17 +02:00
Thomas Piccirello
eb4bf6cc68 Add "Remaining" and "Availability" columns to webui Content tab 2017-12-24 03:01:15 +02:00
jan.karberg
6db6c850eb search only when category is supported by plugin Closes #8053 2017-12-24 03:01:14 +02:00
thalieht
02ae1e3734 Make peer information flags in peerlist more readable 2017-12-24 03:01:13 +02:00
sledgehammer999
eb887139fd Fix application of speed limits on LAN and μTP connections. Closes #7745. 2017-12-24 03:01:12 +02:00
sledgehammer999
84805f7fb8 Fix natural sorting when the common part of 2 strings ends partially in a number which continues in the uncommon part.
Closes #8080 #6732.
2017-12-24 03:01:11 +02:00
sledgehammer999
2719131ed2 Simplify sorting code. 2017-12-24 03:01:10 +02:00
sledgehammer999
52401bd2b0 Fix sorting of country flags column in Peers tab. 2017-12-24 03:01:09 +02:00
Vladimir Golovnev (Glassez)
4834703bc4 Fix RSS rule updated when deleting
Closes #8094
2017-12-24 03:01:08 +02:00
Chocobo1
3ed73244b1 Use standard folder icon for open file behavior on Windows. Closes #7880. 2017-12-24 03:01:07 +02:00
vit9696
97cd430125 Fix cmd+w not closing the main window on macOS 2017-12-24 03:01:06 +02:00
vit9696
d202b85d51 Fix Finder reveal in preview and torrent contents 2017-12-24 03:01:05 +02:00
vit9696
c51b79e9fc Put macOS specific functions to MacUtils namespace 2017-12-24 03:01:04 +02:00
vit9696
4449018207 Fix torrent file selection in Finder on mac 2017-12-24 03:01:03 +02:00
Chocobo1
ced8e41473 Add source field in Torrent creator. Closes #7965.
This field is often used for cross-seeding between (private) trackers.
2017-12-24 03:01:02 +02:00
Chocobo1
2c66ed6708 Add struct TorrentCreatorParams for passing parameters
Filter out continuous newlines in Trackers field
Avoid adding empty url seed entries

Cleanup:
  Replace boost::bind
  Add const
  Use nullptr
  Use QString::SkipEmptyParts
  Rename variables
  Throw proper exception type
2017-12-24 03:01:01 +02:00
Chocobo1
c7d3d6ac90 WebUI: Only prepend scheme when it is not present. Closes #8057. 2017-12-24 03:00:55 +02:00
sledgehammer999
13210b3e9f Bump to 4.0.3 2017-12-17 23:31:59 +02:00
sledgehammer999
6e622fc23b Sync translations from Transifex and run lupdate. 2017-12-17 23:19:17 +02:00
Vladimir Golovnev (Glassez)
ae35111b59 Fix WebUI is not reachable via IPv6 2017-12-17 23:07:59 +02:00
Chocobo1
e1c3d419a7 Disable the "?" help button in all dialogs on Windows. Closes #7365. 2017-12-17 23:07:58 +02:00
Chocobo1
7396b8adba Partial revert eac8838dc2. Fixes #7952.
mapFromSource() didn't work as expected, when used in lessThan(), it sometimes
returns an invalid QModelIndex.
A crash is observed when filtering source model via filterAcceptsRow() in #7952,
the crash is due to endless recursive of filterAcceptsRow() & lessThan() calling
each other and mapFromSource() is the culprit of it.
2017-12-17 23:07:57 +02:00
Chocobo1
c09001545d Allow to drag-n-drop URLs into mainwindow to initiate download
Fix issue: https://github.com/qbittorrent/qBittorrent/issues/7785#issuecomment-347092418
Minor refactor
2017-12-17 23:07:55 +02:00
Chocobo1
f8d4315f7e [WebUI] Use POST for logout command
This is to avoid browser being smart to prefetch the link then logging
out the user.
2017-12-17 23:07:54 +02:00
Chocobo1
1fa2957d27 [WebUI] Add check to avoid type error after logout 2017-12-17 23:07:53 +02:00
Chocobo1
ade50d2b53 Fix missing qbt logo on login page in webUI. Closes #7953. 2017-12-17 23:07:52 +02:00
sledgehammer999
0fa1d35b87 Add height padding to the transfer list icons. Closes #7951. 2017-12-17 23:07:29 +02:00
sledgehammer999
6486fc5f4d Bump to 4.0.2 2017-12-01 01:59:53 +02:00
sledgehammer999
1e059ab1a2 Sync translations from Transifex and run lupdate. 2017-12-01 01:44:49 +02:00
Mike Tzou
15b137211b [GUI] Implement stable sort (#7703)
* NaturalCompare now returns compare result instead of "less than" result
* Change to stable sort in GUI components
* Add Utils::String::naturalLessThan() helper function
* Use Qt::CaseSensitivity type
2017-12-01 01:39:16 +02:00
thalieht
6f8f1d7bad Coding style for many files 2017-12-01 01:39:15 +02:00
sledgehammer999
a31f0c0a3d Stop logging IP filter parsing errors after a while.
When a lot of errors happen the resulting log lines introduce
a huge slowdown of the GUI, due to writing each log line to disk.

Closes #7755.
2017-12-01 01:39:08 +02:00
Evgeny Lensky
f977d1293a Fix i386 build (configure)
Fix building on i686 fails with wrong multilib library path #7845

and rebregenerate ./configure
2017-11-27 19:16:03 +02:00
sledgehammer999
1399be50cb Fix crash on some systems when creating address object for 255.255.255.255
Closes #7735.
2017-11-27 19:14:41 +02:00
Vladimir Golovnev (Glassez)
52dcf32cc8 Implement Import/Export RSS rules in JSON format 2017-11-27 19:14:40 +02:00
Vladimir Golovnev (Glassez)
52b2b807ab Implement Import/Export RSS rules in legacy format 2017-11-27 19:14:38 +02:00
sledgehammer999
5cf4f00824 Remove examples from gpl.html. Closes #7749. 2017-11-27 19:14:37 +02:00
TheNicker
faa6fad025 Fixed blurry text under Windows by setting DPI awareness to default 2017-11-27 19:14:36 +02:00
Vladimir Golovnev (Glassez)
9f94bbce3a Fix RSS Parser
Closes #7751
Closes #7763
Closes #7768
Closes #7786
2017-11-27 19:14:35 +02:00
Chocobo1
5c49b2486c Change MixedModeAlgorithm default to TCP. Closes #7779.
MixedModeAlgorithm::Proportional will throttle TCP connections when utp
is in use and users is expecting maximum speed no matter what, so now
disable the throttling.
2017-11-27 19:14:34 +02:00
thoradia
4f6e7f97c6 Fix missing include in rss_feed.cpp
Fixes #7805.
2017-11-27 19:14:33 +02:00
sledgehammer999
7751c5b75c Merge pull request #7835 from heirecka/v4_0_x
Fix build with --disable-webui
2017-11-23 01:50:21 +02:00
Heiko Becker
a1a9f3317b Fix build with --disable-webui
"app/application.cpp:108:7: error: class 'Application' does not have
any field named 'm_webui'"
2017-11-22 21:30:08 +01:00
218 changed files with 49403 additions and 45346 deletions

View File

@@ -76,10 +76,13 @@ before_install:
- shopt -s expand_aliases
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
#- libt_path="$HOME/libt_install"
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
- qbt_path="$HOME/qbt_install"
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH=/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
else
qbtconf="$qbtconf --prefix="$qbt_path""
fi
# options for specific branches
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
@@ -131,13 +134,13 @@ install:
cp "version" $HOME/hombebrew_cache
cd "$HOME/hombebrew_cache"
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz
fi
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
# Also install our custom libtorrent formula by passing the local path to it
# These 2 files are restored from Travis' cache.
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz" "$(brew --cache)"
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)"
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
if [ "$build_system" = "cmake" ]; then
@@ -168,11 +171,15 @@ script:
BUILD_TOOL="ninja"
fi
if [ "$build_system" = "qmake" ]; then
./bootstrap.sh && ./configure $qbtconf
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# For some reason for RC_1_1 we need to also specify the OpenSSL compiler/linker flags
# Homebrew doesn't symlink OpenSSL for security reasons
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags openssl)" LDFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --libs openssl)"
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
else
./bootstrap.sh && ./configure $qbtconf
fi
BUILD_TOOL="make"
fi

View File

@@ -240,7 +240,23 @@ Example:
```
### 9. Misc. ###
### 9. Include guard. ###
`#pragma once` should be used instead of "include guard" in new code:
```c++
// examplewidget.h
#pragma once
#include <QWidget>
class ExampleWidget : public QWidget
{
// (some code omitted)
};
```
### 10. Misc. ###
* Line breaks for long lines with operation:

View File

@@ -1,3 +1,65 @@
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
- FEATURE: Add a force reannounce option in the transfer list context menu. Closes #6448. (Jesse Bryan)
- BUGFIX: Fix sorting of country flags column in Peers tab. (sledgehammer999)
- BUGFIX: Fix natural sorting when the common part of 2 strings ends partially in a number which continues in the uncommon part. Closes #8080 #6732. (sledgehammer999)
- BUGFIX: Fix application of speed limits on LAN and μTP connections. Closes #7745. (sledgehammer999)
- BUGFIX: Make peer information flags in peerlist more readable. (thalieht)
- BUGFIX: Fix gui issues on high DPI monitor. (Chocobo1)
- BUGFIX: Fix dialog and column size on high DPI monitors. (Chocobo1)
- BUGFIX: Fix constant status of '[F] Downloading'. Closes #7628. (sledgehammer999)
- BUGFIX: Fix translation context. Closes #8211. (sledgehammer999)
- BUGFIX: Separate subnet whitelist options into two lines. (Thomas Piccirello)
- BUGFIX: Don't set application name twice. (Luís Pereira)
- BUGFIX: Set default file log size to 65 KiB and delete backup logs older than 1 month. (sledgehammer999)
- WEBUI: Only prepend scheme when it is not present. Closes #8057. (Chocobo1)
- WEBUI: Add "Remaining" and "Availability" columns to webui Content tab. (Thomas Piccirello)
- WEBUI: Make value formatting consistent with GUI (Thomas Piccirello)
- WEBUI: Reposition Total Size column to match gui (Thomas Piccirello)
- WEBUI: Add Tags and Time Active columns (Thomas Piccirello)
- WEBUI: Use https for www.qbittorrent.org (Thomas Piccirello)
- WEBUI: Match webui statuses to gui, closes #7516 (Thomas Piccirello)
- WEBUI: Right-align stat values (Thomas Piccirello)
- WEBUI: Add missing units. (Thomas Piccirello)
- RSS: Fix crash when deleting rule because it tries to update. Closes #8094 (glassez)
- RSS: Don't process new/updated RSS rules when disabled (glassez)
- RSS: Remove legacy and corrupted RSS settings (glassez)
- SEARCH: Search only when category is supported by plugin. Closes #8053. (jan.karberg)
- SEARCH: Only add search separators as needed. (Thomas Piccirello)
- COSMETIC: Tweak spacing in torrent properties widget and speed widget. (Chocobo1)
- WINDOWS: Use standard folder icon for open file behavior on Windows. Closes #7880. (Chocobo1)
- WINDOWS: Revert "Run external program" function. Now you will not be able to directly run batch scripts. (Chocobo1)
- MACOS: Fix torrent file selection in Finder on mac (vit9696)
- MACOS: Fix Finder reveal in preview and torrent contents (vit9696)
- MACOS: Fix cmd+w not closing the main window on macOS (vit9696)
- OTHER: Fix splitting of compiler flags in configure. Autoconf removes a set of [] during script translation, resulting in a wrong sed command. (sledgehammer999)
- OTHER: configure: Parse all compiler related flags together. (sledgehammer999)
- OTHER: Update copyright year. (sledgehammer999)
* Sun Dec 17 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.3
- BUGFIX: Add height padding to the transfer list icons. Closes #7951. (sledgehammer999)
- BUGFIX: Allow to drag-n-drop URLs into mainwindow to initiate download. (Chocobo1)
- BUGFIX: Fix crash when fitlering search results. Stable sorting is removed. Closes #7952.(Chocobo1)
- WEBUI: Fix missing qbt logo on login page in webUI. Closes #7953. (Chocobo1)
- WEBUI: Add check to avoid type error after logout. (Chocobo1)
- WEBUI: Use POST for logout command. This is to avoid browser being smart to prefetch the link then logging out the user. (Chocobo1)
- WEBUI: Fix WebUI is not reachable via IPv6. (glassez)
- WINDOWS: Disable the "?" help button in all dialogs on Windows. Closes #7365. Requires Qt 5.10. (Chocobo1)
* Fri Dec 01 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.2
- BUGFIX: Fix crash on some systems when creating address object for 255.255.255.255. Closes #7735. (sledgehammer999)
- PERFORMANCE: Change MixedModeAlgorithm default to TCP. This was the v3_3_x default and should sustain higher speeds. Closes #7779. (Chocobo1)
- PERFORMANCE: Stop logging IP filter parsing errors after a while, otherwise the GUI freezes or qBittorrent doesn't start. (sledgehammer999)
- GUI: Implement stable sort. Rows in transfer list shouldn't flicker anymore. (Chocobo1)
- WEBUI: Fix build when webui is disabled. (Heiko Becker)
- RSS: Fix build because of missing header. Closes #7805. (thoradia)
- RSS: Fix RSS parser. (glassez)
- RSS: Implement Import/Export RSS rules in legacy(aka v3_3_x) format. (glassez)
- RSS: Implement Import/Export RSS rules in JSON format. (glassez)
- WINDOWS: Fixed blurry text under Windows by setting DPI awareness to default. (TheNicker)
- LINUX: Fix i386 build. (Evgeny Lensky)
* Wed Nov 22 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.1
- BUGFIX: Fix crash on opening torrent/magnet (uninitialized pointer). Closes #7739 #7723. (sledgehammer999)
- BUGFIX: Enable preferences Apply button when ip banlist is modified (Thomas Piccirello)

View File

@@ -35,7 +35,7 @@ You can also download it from [here](https://github.com/qbittorrent/qBittorrent/
### Misc:
For more information please visit:
http://www.qbittorrent.org
https://www.qbittorrent.org
or our wiki here:
http://wiki.qbittorrent.org

40
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for qbittorrent v3.2.0alpha.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.0.4.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -580,10 +580,10 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v3.2.0alpha'
PACKAGE_STRING='qbittorrent v3.2.0alpha'
PACKAGE_VERSION='v4.0.4'
PACKAGE_STRING='qbittorrent v4.0.4'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='http://www.qbittorrent.org/'
PACKAGE_URL='https://www.qbittorrent.org/'
ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
@@ -1296,7 +1296,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v3.2.0alpha to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.0.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1367,7 +1367,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v3.2.0alpha:";;
short | recursive ) echo "Configuration of qbittorrent v4.0.4:";;
esac
cat <<\_ACEOF
@@ -1438,7 +1438,7 @@ Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>.
qbittorrent home page: <https://www.qbittorrent.org/>.
_ACEOF
ac_status=$?
fi
@@ -1501,7 +1501,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v3.2.0alpha
qbittorrent configure v4.0.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1640,7 +1640,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v3.2.0alpha, which was
It was created by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3818,7 +3818,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v3.2.0alpha'
VERSION='v4.0.4'
cat >>confdefs.h <<_ACEOF
@@ -4705,9 +4705,8 @@ fi
libsubdirs="lib64 libx32 lib lib64" ;; #(
ppc64|s390x|sparc64|aarch64|ppc64le) :
libsubdirs="lib64 lib lib64" ;; #(
libsubdirs="lib") :
;; #(
*) :
libsubdirs="lib"
;;
esac
@@ -5502,7 +5501,7 @@ extract() {
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e "s: -:\\${new_line}: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
IFS=$(printf "\n\b")
for i in $string; do
@@ -5516,9 +5515,8 @@ extract() {
IFS=$SAVEIFS
}
extract $CPPFLAGS
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
# Substitute the values of these vars in conf.pri.in
@@ -6100,7 +6098,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v3.2.0alpha, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6152,13 +6150,13 @@ Configuration commands:
$config_commands
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>."
qbittorrent home page: <https://www.qbittorrent.org/>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v3.2.0alpha
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7415,7 +7413,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v3.2.0alpha, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7467,13 +7465,13 @@ Configuration commands:
$config_commands
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>."
qbittorrent home page: <https://www.qbittorrent.org/>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v3.2.0alpha
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v3.2.0alpha], [bugs.qbittorrent.org], [], [http://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.0.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
@@ -190,7 +190,7 @@ extract() {
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e "s: -:\\${new_line}: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
IFS=$(printf "\n\b")
for i in $string; do
@@ -204,9 +204,8 @@ extract() {
IFS=$SAVEIFS
}
extract $CPPFLAGS
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
# Substitute the values of these vars in conf.pri.in
AC_SUBST(QBT_CONF_INCLUDES)

4
dist/mac/Info.plist vendored
View File

@@ -45,7 +45,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.0.1</string>
<string>4.0.4</string>
<key>CFBundleSignature</key>
<string>qBit</string>
<key>CFBundleExecutable</key>
@@ -59,7 +59,7 @@
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2006-2017 The qBittorrent project</string>
<string>Copyright © 2006-2018 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>

View File

@@ -56,7 +56,7 @@
</image>
</screenshot>
</screenshots>
<url type="homepage">http://www.qbittorrent.org/</url>
<url type="homepage">https://www.qbittorrent.org/</url>
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
<developer_name>The qBittorrent Project</developer_name>
<url type="bugtracker">http://bugs.qbittorrent.org/</url>

View File

@@ -86,7 +86,7 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "UninstallString" '"$INSTDIR\uninst.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayIcon" '"$INSTDIR\qbittorrent.exe",0'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "Publisher" "The qBittorrent project"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "http://www.qbittorrent.org"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "https://www.qbittorrent.org"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayVersion" "${PROG_VERSION}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoRepair" 1

View File

@@ -27,7 +27,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific
!define PROG_VERSION "4.0.1"
!define PROG_VERSION "4.0.4"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
@@ -50,7 +50,7 @@ XPStyle on
;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2017 The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2018 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}"

View File

@@ -2,4 +2,4 @@
Translations = translations
[Platforms]
WindowsArguments = dpiawareness=1
;WindowsArguments = dpiawareness=1

View File

@@ -114,7 +114,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
AS_CASE([${host_cpu}],
[x86_64],[libsubdirs="lib64 libx32 lib lib64"],
[ppc64|s390x|sparc64|aarch64|ppc64le],[libsubdirs="lib64 lib lib64"],
[libsubdirs="lib"],
[libsubdirs="lib"]
)
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give

View File

@@ -87,7 +87,7 @@ namespace
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld");
const QString KEY_FILELOGGER_MAXSIZE = FILELOGGER_SETTINGS_KEY("MaxSize");
const QString KEY_FILELOGGER_MAXSIZEBYTES = FILELOGGER_SETTINGS_KEY("MaxSizeBytes");
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
@@ -98,6 +98,10 @@ namespace
const char PARAMS_SEPARATOR[] = "|";
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
const int MIN_FILELOG_SIZE = 1024; // 1KiB
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB
}
Application::Application(const QString &id, int &argc, char **argv)
@@ -105,7 +109,9 @@ Application::Application(const QString &id, int &argc, char **argv)
, m_running(false)
, m_shutdownAct(ShutdownDialogAction::Exit)
, m_commandLineArgs(parseCommandLine(this->arguments()))
#ifndef DISABLE_WEBUI
, m_webui(nullptr)
#endif
{
qRegisterMetaType<Log::Msg>("Log::Msg");
@@ -126,7 +132,6 @@ Application::Application(const QString &id, int &argc, char **argv)
if (m_commandLineArgs.webUiPort > 0) // it will be -1 when user did not set any value
Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort);
setApplicationName("qBittorrent");
initializeTranslation();
#if !defined(DISABLE_GUI)
@@ -219,29 +224,22 @@ void Application::setFileLoggerDeleteOld(bool value)
int Application::fileLoggerMaxSize() const
{
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt();
if (val < 1)
return 1;
if (val > 1000)
return 1000;
return val;
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZEBYTES, DEFAULT_FILELOG_SIZE).toInt();
return std::min(std::max(val, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
}
void Application::setFileLoggerMaxSize(const int value)
void Application::setFileLoggerMaxSize(const int bytes)
{
int clampedValue = std::min(std::max(bytes, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
if (m_fileLogger)
m_fileLogger->setMaxSize(value);
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000));
m_fileLogger->setMaxSize(clampedValue);
settings()->storeValue(KEY_FILELOGGER_MAXSIZEBYTES, clampedValue);
}
int Application::fileLoggerAge() const
{
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt();
if (val < 1)
return 1;
if (val > 365)
return 365;
return val;
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 1).toInt();
return std::min(std::max(val, 1), 365);
}
void Application::setFileLoggerAge(const int value)
@@ -289,27 +287,6 @@ void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) c
#if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
program.prepend(QLatin1String("\"")).append(QLatin1String("\""));
program.prepend(Utils::Misc::windowsSystemPath() + QLatin1String("\\cmd.exe /C "));
const int cmdMaxLength = 32768; // max length (incl. terminate char) for `lpCommandLine` in `CreateProcessW()`
if ((program.size() + 1) > cmdMaxLength) {
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(cmdMaxLength), Log::CRITICAL);
return;
}
STARTUPINFOW si = {0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
WCHAR *arg = new WCHAR[program.size() + 1];
program.toWCharArray(arg);
arg[program.size()] = L'\0';
if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
delete[] arg;
#else
QProcess::startDetached(program);
#endif

View File

@@ -101,7 +101,7 @@ public:
bool isFileLoggerDeleteOld() const;
void setFileLoggerDeleteOld(bool value);
int fileLoggerMaxSize() const;
void setFileLoggerMaxSize(const int value);
void setFileLoggerMaxSize(const int bytes);
int fileLoggerAge() const;
void setFileLoggerAge(const int value);
int fileLoggerAgeType() const;

View File

@@ -135,7 +135,7 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
if (m_backup && (m_logFile->size() >= (m_maxSize * 1024 * 1024))) {
if (m_backup && (m_logFile->size() >= m_maxSize)) {
closeLogFile();
int counter = 0;
QString backupLogFilename = m_path + ".bak";

View File

@@ -212,6 +212,10 @@ int main(int argc, char *argv[])
// 3. https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// this is the default in Qt6
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
#endif
#if defined(Q_OS_MAC)

View File

@@ -1,124 +1,124 @@
HEADERS += \
$$PWD/asyncfilestorage.h \
$$PWD/types.h \
$$PWD/tristatebool.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/peerinfo.h \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/resumedatasavingmanager.h \
$$PWD/bittorrent/private/speedmonitor.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/trackerentry.h \
$$PWD/filesystemwatcher.h \
$$PWD/logger.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/preferences.h \
$$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/global.h \
$$PWD/http/connection.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/requestparser.h \
$$PWD/http/responsebuilder.h \
$$PWD/http/responsegenerator.h \
$$PWD/http/server.h \
$$PWD/http/types.h \
$$PWD/http/responsebuilder.h \
$$PWD/iconprovider.h \
$$PWD/indexrange.h \
$$PWD/logger.h \
$$PWD/net/dnsupdater.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/downloadhandler.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/peerinfo.h \
$$PWD/bittorrent/trackerentry.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/private/speedmonitor.h \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/bittorrent/private/resumedatasavingmanager.h \
$$PWD/preferences.h \
$$PWD/private/profile_p.h \
$$PWD/profile.h \
$$PWD/rss/private/rss_parser.h \
$$PWD/rss/rss_article.h \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_feed.h \
$$PWD/rss/rss_folder.h \
$$PWD/rss/rss_session.h \
$$PWD/rss/rss_autodownloader.h \
$$PWD/rss/rss_autodownloadrule.h \
$$PWD/rss/private/rss_parser.h \
$$PWD/rss/rss_feed.h \
$$PWD/rss/rss_folder.h \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_session.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/tristatebool.h \
$$PWD/types.h \
$$PWD/unicodestrings.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/misc.h \
$$PWD/utils/net.h \
$$PWD/utils/random.h \
$$PWD/utils/string.h \
$$PWD/utils/version.h \
$$PWD/profile.h \
$$PWD/private/profile_p.h \
$$PWD/unicodestrings.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h \
$$PWD/global.h
$$PWD/utils/version.h
SOURCES += \
$$PWD/asyncfilestorage.cpp \
$$PWD/tristatebool.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/peerinfo.cpp \
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
$$PWD/bittorrent/private/speedmonitor.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/logger.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/preferences.cpp \
$$PWD/iconprovider.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/requestparser.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/http/responsegenerator.cpp \
$$PWD/http/server.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/iconprovider.cpp \
$$PWD/logger.cpp \
$$PWD/net/dnsupdater.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/net/proxyconfigurationmanager.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/peerinfo.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/private/speedmonitor.cpp \
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
$$PWD/preferences.cpp \
$$PWD/private/profile_p.cpp \
$$PWD/profile.cpp \
$$PWD/rss/private/rss_parser.cpp \
$$PWD/rss/rss_article.cpp \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_feed.cpp \
$$PWD/rss/rss_folder.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/rss/rss_autodownloader.cpp \
$$PWD/rss/rss_autodownloadrule.cpp \
$$PWD/rss/private/rss_parser.cpp \
$$PWD/rss/rss_feed.cpp \
$$PWD/rss/rss_folder.cpp \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/tristatebool.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/net.cpp \
$$PWD/utils/random.cpp \
$$PWD/utils/string.cpp \
$$PWD/profile.cpp \
$$PWD/private/profile_p.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp
$$PWD/utils/string.cpp

View File

@@ -273,18 +273,20 @@ qreal PeerInfo::relevance() const
void PeerInfo::determineFlags()
{
QStringList flagsDescriptionList;
if (isInteresting()) {
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (isRemoteChocked()) {
m_flags += "d ";
m_flagsDescription += tr("interested(local) and choked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "d = "
+ tr("Interested(local) and Choked(peer)");
}
else {
// D = Currently downloading (interested and not choked)
m_flags += "D ";
m_flagsDescription += tr("interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "D = "
+ tr("interested(local) and unchoked(peer)");
}
}
@@ -292,97 +294,95 @@ void PeerInfo::determineFlags()
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (isChocked()) {
m_flags += "u ";
m_flagsDescription += tr("interested(peer) and choked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "u = "
+ tr("interested(peer) and choked(local)");
}
else {
// U = Currently uploading (interested and not choked)
m_flags += "U ";
m_flagsDescription += tr("interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "U = "
+ tr("interested(peer) and unchoked(local)");
}
}
// O = Optimistic unchoke
if (optimisticUnchoke()) {
m_flags += "O ";
m_flagsDescription += tr("optimistic unchoke");
m_flagsDescription += ", ";
flagsDescriptionList += "O = "
+ tr("optimistic unchoke");
}
// S = Peer is snubbed
if (isSnubbed()) {
m_flags += "S ";
m_flagsDescription += tr("peer snubbed");
m_flagsDescription += ", ";
flagsDescriptionList += "S = "
+ tr("peer snubbed");
}
// I = Peer is an incoming connection
if (!isLocalConnection()) {
m_flags += "I ";
m_flagsDescription += tr("incoming connection");
m_flagsDescription += ", ";
flagsDescriptionList += "I = "
+ tr("incoming connection");
}
// K = Peer is unchoking your client, but your client is not interested
if (!isRemoteChocked() && !isInteresting()) {
m_flags += "K ";
m_flagsDescription += tr("not interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "K = "
+ tr("not interested(local) and unchoked(peer)");
}
// ? = Your client unchoked the peer but the peer is not interested
if (!isChocked() && !isRemoteInterested()) {
m_flags += "? ";
m_flagsDescription += tr("not interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "? = "
+ tr("not interested(peer) and unchoked(local)");
}
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
if (fromPeX()) {
m_flags += "X ";
m_flagsDescription += tr("peer from PEX");
m_flagsDescription += ", ";
flagsDescriptionList += "X = "
+ tr("peer from PEX");
}
// H = Peer was obtained through DHT
if (fromDHT()) {
m_flags += "H ";
m_flagsDescription += tr("peer from DHT");
m_flagsDescription += ", ";
flagsDescriptionList += "H = "
+ tr("peer from DHT");
}
// E = Peer is using Protocol Encryption (all traffic)
if (isRC4Encrypted()) {
m_flags += "E ";
m_flagsDescription += tr("encrypted traffic");
m_flagsDescription += ", ";
flagsDescriptionList += "E = "
+ tr("encrypted traffic");
}
// e = Peer is using Protocol Encryption (handshake)
if (isPlaintextEncrypted()) {
m_flags += "e ";
m_flagsDescription += tr("encrypted handshake");
m_flagsDescription += ", ";
flagsDescriptionList += "e = "
+ tr("encrypted handshake");
}
// P = Peer is using uTorrent uTP
if (useUTPSocket()) {
m_flags += "P ";
m_flagsDescription += QString::fromUtf8(C_UTP);
m_flagsDescription += ", ";
flagsDescriptionList += "P = "
+ QString::fromUtf8(C_UTP);
}
// L = Peer is local
if (fromLSD()) {
m_flags += "L";
m_flagsDescription += tr("peer from LSD");
flagsDescriptionList += "L = "
+ tr("peer from LSD");
}
m_flags = m_flags.trimmed();
m_flagsDescription = m_flagsDescription.trimmed();
if (m_flagsDescription.endsWith(',', Qt::CaseInsensitive))
m_flagsDescription.chop(1);
m_flagsDescription = flagsDescriptionList.join('\n');
}
QString PeerInfo::flags() const

View File

@@ -102,6 +102,7 @@ namespace
}
const int BUFFER_SIZE = 2 * 1024 * 1024; // 2 MiB
const int MAX_LOGGED_ERRORS = 5;
}
FilterParserThread::FilterParserThread(QObject *parent)
@@ -134,6 +135,12 @@ int FilterParserThread::parseDATFilterFile()
int start = 0;
int endOfLine = -1;
int nbLine = 0;
int parseErrorCount = 0;
const auto addLog = [&parseErrorCount](const QString &msg)
{
if (parseErrorCount <= MAX_LOGGED_ERRORS)
LogMsg(msg, Log::CRITICAL);
};
while (true) {
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
@@ -202,7 +209,8 @@ int FilterParserThread::parseDATFilterFile()
int endOfIPRange = ((firstComma == -1) ? (endOfLine - 1) : (firstComma - 1));
int delimIP = findAndNullDelimiter(buffer.data(), '-', start, endOfIPRange);
if (delimIP == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -210,7 +218,8 @@ int FilterParserThread::parseDATFilterFile()
libt::address startAddr;
int newStart = trim(buffer.data(), start, delimIP - 1);
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -218,14 +227,16 @@ int FilterParserThread::parseDATFilterFile()
libt::address endAddr;
newStart = trim(buffer.data(), delimIP + 1, endOfIPRange);
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
if ((startAddr.is_v4() != endAddr.is_v4())
|| (startAddr.is_v6() != endAddr.is_v6())) {
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
start = endOfLine;
continue;
}
@@ -238,8 +249,9 @@ int FilterParserThread::parseDATFilterFile()
++ruleCount;
}
catch (std::exception &e) {
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
}
}
@@ -247,6 +259,9 @@ int FilterParserThread::parseDATFilterFile()
offset = 0;
}
if (parseErrorCount > MAX_LOGGED_ERRORS)
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
return ruleCount;
}
@@ -268,6 +283,12 @@ int FilterParserThread::parseP2PFilterFile()
int start = 0;
int endOfLine = -1;
int nbLine = 0;
int parseErrorCount = 0;
const auto addLog = [&parseErrorCount](const QString &msg)
{
if (parseErrorCount <= MAX_LOGGED_ERRORS)
LogMsg(msg, Log::CRITICAL);
};
while (true) {
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
@@ -319,7 +340,8 @@ int FilterParserThread::parseP2PFilterFile()
// The "Some organization" part might contain a ':' char itself so we find the last occurrence
int partsDelimiter = findAndNullDelimiter(buffer.data(), ':', start, endOfLine, true);
if (partsDelimiter == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -327,7 +349,8 @@ int FilterParserThread::parseP2PFilterFile()
// IP Range should be split by a dash
int delimIP = findAndNullDelimiter(buffer.data(), '-', partsDelimiter + 1, endOfLine);
if (delimIP == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -335,7 +358,8 @@ int FilterParserThread::parseP2PFilterFile()
libt::address startAddr;
int newStart = trim(buffer.data(), partsDelimiter + 1, delimIP - 1);
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -343,14 +367,16 @@ int FilterParserThread::parseP2PFilterFile()
libt::address endAddr;
newStart = trim(buffer.data(), delimIP + 1, endOfLine);
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
if ((startAddr.is_v4() != endAddr.is_v4())
|| (startAddr.is_v6() != endAddr.is_v6())) {
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
start = endOfLine;
continue;
}
@@ -362,8 +388,9 @@ int FilterParserThread::parseP2PFilterFile()
++ruleCount;
}
catch (std::exception &e) {
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
}
}
@@ -371,6 +398,9 @@ int FilterParserThread::parseP2PFilterFile()
offset = 0;
}
if (parseErrorCount > MAX_LOGGED_ERRORS)
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
return ruleCount;
}

View File

@@ -304,7 +304,7 @@ Session::Session(QObject *parent)
, m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
, clampValue(BTProtocol::Both, BTProtocol::UTP))
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::Proportional
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::TCP
, clampValue(MixedModeAlgorithm::TCP, MixedModeAlgorithm::Proportional))
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
@@ -1418,8 +1418,11 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
void Session::configurePeerClasses()
{
libt::ip_filter f;
f.add_rule(libt::address_v4::from_string("0.0.0.0")
, libt::address_v4::from_string("255.255.255.255")
// address_v4::from_string("255.255.255.255") crashes on some people's systems
// so instead we use address_v4::broadcast()
// Proactively do the same for 0.0.0.0 and address_v4::any()
f.add_rule(libt::address_v4::any()
, libt::address_v4::broadcast()
, 1 << libt::session::global_peer_class_id);
#if TORRENT_USE_IPV6
// IPv6 may not be available on OS and the parsing
@@ -1478,14 +1481,10 @@ void Session::configurePeerClasses()
peerClassTypeFilter.add(libt::peer_class_type_filter::tcp_socket, libt::session::tcp_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_tcp_socket, libt::session::tcp_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::i2p_socket, libt::session::tcp_peer_class_id);
if (isUTPRateLimited()) {
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
, libt::session::local_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
if (!isUTPRateLimited()) {
peerClassTypeFilter.disallow(libt::peer_class_type_filter::utp_socket
, libt::session::global_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
, libt::session::local_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
peerClassTypeFilter.disallow(libt::peer_class_type_filter::ssl_utp_socket
, libt::session::global_peer_class_id);
}
m_nativeSession->set_peer_class_type_filter(peerClassTypeFilter);

View File

@@ -59,8 +59,6 @@ using namespace BitTorrent;
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
: QThread(parent)
, m_private(false)
, m_pieceSize(0)
{
}
@@ -70,97 +68,91 @@ TorrentCreatorThread::~TorrentCreatorThread()
wait();
}
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
void TorrentCreatorThread::create(const TorrentCreatorParams &params)
{
m_inputPath = Utils::Fs::fromNativePath(inputPath);
m_savePath = Utils::Fs::fromNativePath(savePath);
if (QFile(m_savePath).exists())
Utils::Fs::forceRemove(m_savePath);
m_trackers = trackers;
m_urlSeeds = urlSeeds;
m_comment = comment;
m_private = isPrivate;
m_pieceSize = pieceSize;
m_params = params;
start();
}
void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces)
void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPieces)
{
emit updateProgress(static_cast<int>((numHashes * 100.) / numPieces));
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
}
void TorrentCreatorThread::run()
{
const QString creatorStr("qBittorrent " QBT_VERSION);
emit updateProgress(0);
QString creator_str("qBittorrent " QBT_VERSION);
try {
libt::file_storage fs;
// Adding files to the torrent
libt::add_files(fs, Utils::Fs::toNativePath(m_inputPath).toStdString(), fileFilter);
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
if (isInterruptionRequested()) return;
libt::create_torrent t(fs, m_pieceSize);
libt::create_torrent newTorrent(fs, m_params.pieceSize);
// Add url seeds
foreach (const QString &seed, m_urlSeeds)
t.add_url_seed(seed.trimmed().toStdString());
foreach (QString seed, m_params.urlSeeds) {
seed = seed.trimmed();
if (!seed.isEmpty())
newTorrent.add_url_seed(seed.toStdString());
}
int tier = 0;
bool newline = false;
foreach (const QString &tracker, m_trackers) {
if (tracker.isEmpty()) {
if (newline)
continue;
foreach (const QString &tracker, m_params.trackers) {
if (tracker.isEmpty())
++tier;
newline = true;
continue;
}
t.add_tracker(tracker.trimmed().toStdString(), tier);
newline = false;
else
newTorrent.add_tracker(tracker.trimmed().toStdString(), tier);
}
if (isInterruptionRequested()) return;
// calculate the hash for all pieces
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/";
libt::set_piece_hashes(t, Utils::Fs::toNativePath(parentPath).toStdString(), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces()));
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
// Set qBittorrent as creator and add user comment to
// torrent_info structure
t.set_creator(creator_str.toUtf8().constData());
t.set_comment(m_comment.toUtf8().constData());
newTorrent.set_creator(creatorStr.toUtf8().constData());
newTorrent.set_comment(m_params.comment.toUtf8().constData());
// Is private ?
t.set_priv(m_private);
newTorrent.set_priv(m_params.isPrivate);
if (isInterruptionRequested()) return;
// create the torrent and print it to out
qDebug("Saving to %s", qUtf8Printable(m_savePath));
libt::entry entry = newTorrent.generate();
// add source field
if (!m_params.source.isEmpty())
entry["info"]["source"] = m_params.source.toStdString();
if (isInterruptionRequested()) return;
// create the torrent
std::ofstream outfile(
#ifdef _MSC_VER
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1];
int len = Utils::Fs::toNativePath(m_savePath).toWCharArray(savePathW);
savePathW[len] = L'\0';
std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary);
delete[] savePathW;
Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str()
#else
std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary);
Utils::Fs::toNativePath(m_params.savePath).toUtf8().constData()
#endif
, (std::ios_base::out | std::ios_base::binary | std::ios_base::trunc));
if (outfile.fail())
throw std::exception();
throw std::runtime_error(tr("create new torrent file failed").toStdString());
if (isInterruptionRequested()) return;
libt::bencode(std::ostream_iterator<char>(outfile), t.generate());
libt::bencode(std::ostream_iterator<char>(outfile), entry);
outfile.close();
emit updateProgress(100);
emit creationSuccess(m_savePath, parentPath);
emit creationSuccess(m_params.savePath, parentPath);
}
catch (std::exception& e) {
emit creationFailure(QString::fromStdString(e.what()));
catch (const std::exception &e) {
emit creationFailure(e.what());
}
}

View File

@@ -36,16 +36,27 @@
namespace BitTorrent
{
struct TorrentCreatorParams
{
bool isPrivate;
int pieceSize;
QString inputPath;
QString savePath;
QString comment;
QString source;
QStringList trackers;
QStringList urlSeeds;
};
class TorrentCreatorThread : public QThread
{
Q_OBJECT
public:
TorrentCreatorThread(QObject *parent = 0);
TorrentCreatorThread(QObject *parent = nullptr);
~TorrentCreatorThread();
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
void create(const TorrentCreatorParams &params);
static int calculateTotalPieces(const QString &inputPath, const int pieceSize);
@@ -58,15 +69,9 @@ namespace BitTorrent
void updateProgress(int progress);
private:
void sendProgressSignal(int numHashes, int numPieces);
void sendProgressSignal(int currentPieceIdx, int totalPieces);
QString m_inputPath;
QString m_savePath;
QStringList m_trackers;
QStringList m_urlSeeds;
QString m_comment;
bool m_private;
int m_pieceSize;
TorrentCreatorParams m_params;
};
}

View File

@@ -156,6 +156,8 @@ const int TorrentHandle::MAX_SEEDING_TIME = 525600;
// The following can be removed after one or two libtorrent releases on each branch.
namespace
{
const char i18nContext[] = "TorrentHandle";
// new constructor is available
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
T makeTorrentCreator(const libtorrent::torrent_info & ti)
@@ -1469,7 +1471,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
return;
}
Logger::instance()->addMessage(tr("Could not move torrent: '%1'. Reason: %2")
LogMsg(QCoreApplication::translate(i18nContext, "Could not move torrent: '%1'. Reason: %2")
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
m_moveStorageInfo.newPath.clear();
@@ -1647,13 +1649,13 @@ void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejecte
updateStatus();
if (p->error.value() == libt::errors::mismatching_file_size) {
// Mismatching file size (files were probably moved)
logger->addMessage(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
logger->addMessage(QCoreApplication::translate(i18nContext, "File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
m_hasMissingFiles = true;
if (!isPaused())
pause();
}
else {
logger->addMessage(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
logger->addMessage(QCoreApplication::translate(i18nContext, "Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
}
}

View File

@@ -59,6 +59,7 @@ namespace Http
const char CONTENT_TYPE_JSON[] = "application/json";
const char CONTENT_TYPE_PNG[] = "image/png";
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
const char CONTENT_TYPE_SVG[] = "image/svg+xml";
// portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'};

View File

@@ -35,6 +35,7 @@
#include <QCryptographicHash>
#include <QDir>
#include <QLocale>
#include <QMutableListIterator>
#include <QPair>
#include <QSettings>
@@ -487,13 +488,17 @@ QList<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
return subnets;
}
void Preferences::setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets)
void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
{
QStringList subnetsStringList;
for (const Utils::Net::Subnet &subnet : subnets)
subnetsStringList.append(Utils::Net::subnetToString(subnet));
QMutableListIterator<QString> i(subnets);
while (i.hasNext()) {
bool ok = false;
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(i.next().trimmed(), &ok);
if (!ok)
i.remove();
}
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnetsStringList);
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnets);
}
QString Preferences::getServerDomains() const
@@ -1216,9 +1221,9 @@ void Preferences::setMainLastDir(const QString &path)
setValue("MainWindowLastDir", path);
}
QSize Preferences::getPrefSize(const QSize &defaultSize) const
QSize Preferences::getPrefSize() const
{
return value("Preferences/State/size", defaultSize).toSize();
return value("Preferences/State/size").toSize();
}
void Preferences::setPrefSize(const QSize &size)
@@ -1296,9 +1301,9 @@ void Preferences::setPropTrackerListState(const QByteArray &state)
setValue("TorrentProperties/Trackers/qt5/TrackerListState", state);
}
QSize Preferences::getRssGeometrySize(const QSize &defaultSize) const
QSize Preferences::getRssGeometrySize() const
{
return value("RssFeedDownloader/geometrySize", defaultSize).toSize();
return value("RssFeedDownloader/geometrySize").toSize();
}
void Preferences::setRssGeometrySize(const QSize &geometry)

View File

@@ -191,7 +191,7 @@ public:
bool isWebUiAuthSubnetWhitelistEnabled() const;
void setWebUiAuthSubnetWhitelistEnabled(bool enabled);
QList<Utils::Net::Subnet> getWebUiAuthSubnetWhitelist() const;
void setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets);
void setWebUiAuthSubnetWhitelist(QStringList subnets);
QString getWebUiUsername() const;
void setWebUiUsername(const QString &username);
QString getWebUiPassword() const;
@@ -301,7 +301,7 @@ public:
void setMainVSplitterState(const QByteArray &state);
QString getMainLastDir() const;
void setMainLastDir(const QString &path);
QSize getPrefSize(const QSize &defaultSize) const;
QSize getPrefSize() const;
void setPrefSize(const QSize &size);
QStringList getPrefHSplitterSizes() const;
void setPrefHSplitterSizes(const QStringList &sizes);
@@ -317,7 +317,7 @@ public:
void setPropVisible(const bool visible);
QByteArray getPropTrackerListState() const;
void setPropTrackerListState(const QByteArray &state);
QSize getRssGeometrySize(const QSize &defaultSize) const;
QSize getRssGeometrySize() const;
void setRssGeometrySize(const QSize &geometry);
QByteArray getRssHSplitterSizes() const;
void setRssHSplitterSizes(const QByteArray &sizes);

View File

@@ -227,10 +227,9 @@ void Parser::parse(const QByteArray &feedData)
// read and create items from a rss document
void Parser::parse_impl(const QByteArray &feedData)
{
qDebug() << Q_FUNC_INFO;
QXmlStreamReader xml(feedData);
bool foundChannel = false;
while (xml.readNextStartElement()) {
if (xml.name() == "rss") {
// Find channels
@@ -258,11 +257,15 @@ void Parser::parse_impl(const QByteArray &feedData)
}
}
if (xml.hasError())
m_result.error = xml.errorString();
else if (!foundChannel)
if (!foundChannel) {
m_result.error = tr("Invalid RSS feed.");
else
}
else if (xml.hasError()) {
m_result.error = tr("%1 (line: %2, column: %3, offset: %4).")
.arg(xml.errorString()).arg(xml.lineNumber())
.arg(xml.columnNumber()).arg(xml.characterOffset());
}
else {
// Sort article list chronologically
// NOTE: We don't need to sort it here if articles are always
// sorted in fetched XML in reverse chronological order
@@ -271,6 +274,7 @@ void Parser::parse_impl(const QByteArray &feedData)
{
return a1["date"].toDateTime() < a2["date"].toDateTime();
});
}
emit finished(m_result);
m_result.articles.clear(); // clear articles only
@@ -288,35 +292,34 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
break;
if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text;
article[Article::KeyTitle] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("enclosure")) {
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
}
else if (name == QLatin1String("link")) {
const QString text {xml.readElementText().trimmed()};
if (text.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
article[Article::KeyTorrentURL] = text; // magnet link instead of a news URL
else
article[Article::KeyLink] = text;
}
else if (name == QLatin1String("description")) {
article[Article::KeyDescription] = text;
article[Article::KeyDescription] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
else if (name == QLatin1String("pubDate")) {
article[Article::KeyDate] = parseDate(text);
article[Article::KeyDate] = parseDate(xml.readElementText().trimmed());
}
else if (name == QLatin1String("author")) {
article[Article::KeyAuthor] = text;
article[Article::KeyAuthor] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("guid")) {
article[Article::KeyId] = text;
article[Article::KeyId] = xml.readElementText().trimmed();
}
else {
article[name] = text;
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
}
}
@@ -326,17 +329,14 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
void Parser::parseRSSChannel(QXmlStreamReader &xml)
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == "title") {
if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText();
}
else if (xml.name() == "lastBuildDate") {
else if (xml.name() == QLatin1String("lastBuildDate")) {
QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) {
@@ -346,7 +346,7 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate;
}
}
else if (xml.name() == "item") {
else if (xml.name() == QLatin1String("item")) {
parseRssArticle(xml);
}
}
@@ -366,14 +366,12 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
break;
if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text;
article[Article::KeyTitle] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("link")) {
QString link = (xml.attributes().isEmpty()
? text
? xml.readElementText().trimmed()
: xml.attributes().value(QLatin1String("href")).toString());
if (link.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
@@ -385,42 +383,38 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
article[Article::KeyLink] = (m_baseUrl.isEmpty() ? link : m_baseUrl + link);
}
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))){
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))) {
if (doubleContent) { // Duplicate content -> ignore
xml.readNext();
while ((xml.name() != QLatin1String("summary")) && (xml.name() != QLatin1String("content")))
xml.readNext();
xml.skipCurrentElement();
continue;
}
// Try to also parse broken articles, which don't use html '&' escapes
// Actually works great for non-broken content too
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements);
if (!feedText.isEmpty())
article[Article::KeyDescription] = feedText.trimmed();
doubleContent = true;
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements).trimmed();
if (!feedText.isEmpty()) {
article[Article::KeyDescription] = feedText;
doubleContent = true;
}
}
else if (name == QLatin1String("updated")) {
// ATOM uses standard compliant date, don't do fancy stuff
QDateTime articleDate = QDateTime::fromString(text, Qt::ISODate);
QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate);
article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime());
}
else if (name == QLatin1String("author")) {
xml.readNext();
while (xml.name() != QLatin1String("author")) {
while (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("name"))
article[Article::KeyAuthor] = xml.readElementText().trimmed();
xml.readNext();
else
xml.skipCurrentElement();
}
}
else if (name == QLatin1String("id")) {
article[Article::KeyId] = text;
article[Article::KeyId] = xml.readElementText().trimmed();
}
else {
article[name] = text;
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
}
}
@@ -430,19 +424,16 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
void Parser::parseAtomChannel(QXmlStreamReader &xml)
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT(xml.isStartElement() && xml.name() == "feed");
m_baseUrl = xml.attributes().value("xml:base").toString();
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == "title") {
if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText();
}
else if (xml.name() == "updated") {
else if (xml.name() == QLatin1String("updated")) {
QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) {
@@ -452,7 +443,7 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate;
}
}
else if (xml.name() == "entry") {
else if (xml.name() == QLatin1String("entry")) {
parseAtomArticle(xml);
}
}

View File

@@ -28,6 +28,7 @@
#include "rss_autodownloader.h"
#include <QDataStream>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
@@ -37,6 +38,7 @@
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <QVector>
#include "../bittorrent/magneturi.h"
#include "../bittorrent/session.h"
@@ -63,6 +65,32 @@ const QString RulesFileName(QStringLiteral("download_rules.json"));
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing"));
namespace
{
QVector<RSS::AutoDownloadRule> rulesFromJSON(const QByteArray &jsonData)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
throw RSS::ParsingError(jsonError.errorString());
if (!jsonDoc.isObject())
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
const QJsonObject jsonObj {jsonDoc.object()};
QVector<RSS::AutoDownloadRule> rules;
for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it) {
const QJsonValue jsonVal {it.value()};
if (!jsonVal.isObject())
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
rules.append(RSS::AutoDownloadRule::fromJsonObject(jsonVal.toObject(), it.key()));
}
return rules;
}
}
using namespace RSS;
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
@@ -84,8 +112,8 @@ AutoDownloader::AutoDownloader()
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
connect(m_fileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
{
Logger::instance()->addMessage(QString("Couldn't save RSS AutoDownloader data in %1. Error: %2")
.arg(fileName).arg(errorString), Log::WARNING);
LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
.arg(fileName).arg(errorString), Log::CRITICAL);
});
m_ioThread->start();
@@ -174,6 +202,70 @@ void AutoDownloader::removeRule(const QString &ruleName)
}
}
QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const
{
switch (format) {
case RulesFileFormat::Legacy:
return exportRulesToLegacyFormat();
default:
return exportRulesToJSONFormat();
}
}
void AutoDownloader::importRules(const QByteArray &data, AutoDownloader::RulesFileFormat format)
{
switch (format) {
case RulesFileFormat::Legacy:
importRulesFromLegacyFormat(data);
break;
default:
importRulesFromJSONFormat(data);
}
}
QByteArray AutoDownloader::exportRulesToJSONFormat() const
{
QJsonObject jsonObj;
for (const auto &rule : rules())
jsonObj.insert(rule.name(), rule.toJsonObject());
return QJsonDocument(jsonObj).toJson();
}
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
{
const auto rules = rulesFromJSON(data);
for (const auto &rule : rules)
insertRule(rule);
}
QByteArray AutoDownloader::exportRulesToLegacyFormat() const
{
QVariantHash dict;
for (const auto &rule : rules())
dict[rule.name()] = rule.toLegacyDict();
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_5);
out << dict;
return data;
}
void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
{
QDataStream in(data);
in.setVersion(QDataStream::Qt_4_5);
QVariantHash dict;
in >> dict;
if (in.status() != QDataStream::Ok)
throw ParsingError(tr("Invalid data format"));
for (const QVariant &val : dict)
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
}
void AutoDownloader::process()
{
if (m_processingQueue.isEmpty()) return; // processing was disabled
@@ -276,39 +368,20 @@ void AutoDownloader::load()
else if (rulesFile.open(QFile::ReadOnly))
loadRules(rulesFile.readAll());
else
Logger::instance()->addMessage(
QString("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::WARNING);
LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::CRITICAL);
}
void AutoDownloader::loadRules(const QByteArray &data)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
Logger::instance()->addMessage(
QString("Couldn't parse RSS AutoDownloader rules. Error: %1")
.arg(jsonError.errorString()), Log::WARNING);
return;
try {
const auto rules = rulesFromJSON(data);
for (const auto &rule : rules)
setRule_impl(rule);
}
if (!jsonDoc.isObject()) {
Logger::instance()->addMessage(
QString("Couldn't load RSS AutoDownloader rules. Invalid data format."), Log::WARNING);
return;
}
QJsonObject jsonObj = jsonDoc.object();
foreach (const QString &key, jsonObj.keys()) {
const QJsonValue jsonVal = jsonObj.value(key);
if (!jsonVal.isObject()) {
Logger::instance()->addMessage(
QString("Couldn't load RSS AutoDownloader rule '%1'. Invalid data format.")
.arg(key), Log::WARNING);
continue;
}
setRule_impl(AutoDownloadRule::fromJsonObject(jsonVal.toObject(), key));
catch (const ParsingError &error) {
LogMsg(tr("Couldn't load RSS AutoDownloader rules. Reason: %1")
.arg(error.message()), Log::CRITICAL);
}
}
@@ -317,7 +390,7 @@ void AutoDownloader::loadRulesLegacy()
SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss"));
QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
foreach (const QVariant &ruleVar, rules) {
auto rule = AutoDownloadRule::fromVariantHash(ruleVar.toHash());
auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
if (!rule.name().isEmpty())
insertRule(rule);
}
@@ -351,6 +424,8 @@ bool AutoDownloader::isProcessingEnabled() const
void AutoDownloader::resetProcessingQueue()
{
m_processingQueue.clear();
if (!m_processingEnabled) return;
foreach (Article *article, Session::instance()->rootFolder()->articles()) {
if (!article->isRead() && !article->torrentUrl().isEmpty())
addJobForArticle(article);
@@ -385,3 +460,13 @@ void AutoDownloader::timerEvent(QTimerEvent *event)
Q_UNUSED(event);
store();
}
ParsingError::ParsingError(const QString &message)
: std::runtime_error(message.toUtf8().data())
{
}
QString ParsingError::message() const
{
return what();
}

View File

@@ -28,6 +28,8 @@
#pragma once
#include <stdexcept>
#include <QBasicTimer>
#include <QHash>
#include <QList>
@@ -49,6 +51,13 @@ namespace RSS
class AutoDownloadRule;
class ParsingError : public std::runtime_error
{
public:
explicit ParsingError(const QString &message);
QString message() const;
};
class AutoDownloader final: public QObject
{
Q_OBJECT
@@ -60,6 +69,12 @@ namespace RSS
~AutoDownloader() override;
public:
enum class RulesFileFormat
{
Legacy,
JSON
};
static AutoDownloader *instance();
bool isProcessingEnabled() const;
@@ -73,6 +88,9 @@ namespace RSS
bool renameRule(const QString &ruleName, const QString &newRuleName);
void removeRule(const QString &ruleName);
QByteArray exportRules(RulesFileFormat format = RulesFileFormat::JSON) const;
void importRules(const QByteArray &data, RulesFileFormat format = RulesFileFormat::JSON);
signals:
void processingStateChanged(bool enabled);
void ruleAdded(const QString &ruleName);
@@ -98,6 +116,10 @@ namespace RSS
void loadRulesLegacy();
void store();
void storeDeferred();
QByteArray exportRulesToJSONFormat() const;
void importRulesFromJSONFormat(const QByteArray &data);
QByteArray exportRulesToLegacyFormat() const;
void importRulesFromLegacyFormat(const QByteArray &data);
static QPointer<AutoDownloader> m_instance;

View File

@@ -63,11 +63,29 @@ namespace
QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool)
{
switch (static_cast<int>(triStateBool)) {
case 0: return false; break;
case 1: return true; break;
case 0: return false;
case 1: return true;
default: return QJsonValue();
}
}
TriStateBool addPausedLegacyToTriStateBool(int val)
{
switch (val) {
case 1: return TriStateBool::True; // always
case 2: return TriStateBool::False; // never
default: return TriStateBool::Undefined; // default
}
}
int triStateBoolToAddPausedLegacy(const TriStateBool &triStateBool)
{
switch (static_cast<int>(triStateBool)) {
case 0: return 2; // never
case 1: return 1; // always
default: return 0; // default
}
}
}
const QString Str_Name(QStringLiteral("name"));
@@ -378,21 +396,37 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
return rule;
}
AutoDownloadRule AutoDownloadRule::fromVariantHash(const QVariantHash &varHash)
QVariantHash AutoDownloadRule::toLegacyDict() const
{
AutoDownloadRule rule(varHash.value("name").toString());
return {{"name", name()},
{"must_contain", mustContain()},
{"must_not_contain", mustNotContain()},
{"save_path", savePath()},
{"affected_feeds", feedURLs()},
{"enabled", isEnabled()},
{"category_assigned", assignedCategory()},
{"use_regex", useRegex()},
{"add_paused", triStateBoolToAddPausedLegacy(addPaused())},
{"episode_filter", episodeFilter()},
{"last_match", lastMatch()},
{"ignore_days", ignoreDays()}};
}
rule.setUseRegex(varHash.value("use_regex", false).toBool());
rule.setMustContain(varHash.value("must_contain").toString());
rule.setMustNotContain(varHash.value("must_not_contain").toString());
rule.setEpisodeFilter(varHash.value("episode_filter").toString());
rule.setFeedURLs(varHash.value("affected_feeds").toStringList());
rule.setEnabled(varHash.value("enabled", false).toBool());
rule.setSavePath(varHash.value("save_path").toString());
rule.setCategory(varHash.value("category_assigned").toString());
rule.setAddPaused(TriStateBool(varHash.value("add_paused").toInt() - 1));
rule.setLastMatch(varHash.value("last_match").toDateTime());
rule.setIgnoreDays(varHash.value("ignore_days").toInt());
AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
{
AutoDownloadRule rule(dict.value("name").toString());
rule.setUseRegex(dict.value("use_regex", false).toBool());
rule.setMustContain(dict.value("must_contain").toString());
rule.setMustNotContain(dict.value("must_not_contain").toString());
rule.setEpisodeFilter(dict.value("episode_filter").toString());
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
rule.setEnabled(dict.value("enabled", false).toBool());
rule.setSavePath(dict.value("save_path").toString());
rule.setCategory(dict.value("category_assigned").toString());
rule.setAddPaused(addPausedLegacyToTriStateBool(dict.value("add_paused").toInt()));
rule.setLastMatch(dict.value("last_match").toDateTime());
rule.setIgnoreDays(dict.value("ignore_days").toInt());
return rule;
}

View File

@@ -84,7 +84,9 @@ namespace RSS
QJsonObject toJsonObject() const;
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = "");
static AutoDownloadRule fromVariantHash(const QVariantHash &varHash);
QVariantHash toLegacyDict() const;
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
private:
bool matches(const QString &articleTitle, const QString &expression) const;

View File

@@ -31,6 +31,7 @@
#include "rss_feed.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>

View File

@@ -102,6 +102,27 @@ Session::Session()
m_refreshTimer.start(m_refreshInterval * MsecsPerMin);
refresh();
}
// Remove legacy/corrupted settings
// (at least on Windows, QSettings is case-insensitive and it can get
// confused when asked about settings that differ only in their case)
auto settingsStorage = SettingsStorage::instance();
settingsStorage->removeValue("Rss/streamList");
settingsStorage->removeValue("Rss/streamAlias");
settingsStorage->removeValue("Rss/open_folders");
settingsStorage->removeValue("Rss/qt5/splitter_h");
settingsStorage->removeValue("Rss/qt5/splitterMain");
settingsStorage->removeValue("Rss/hosts_cookies");
settingsStorage->removeValue("RSS/streamList");
settingsStorage->removeValue("RSS/streamAlias");
settingsStorage->removeValue("RSS/open_folders");
settingsStorage->removeValue("RSS/qt5/splitter_h");
settingsStorage->removeValue("RSS/qt5/splitterMain");
settingsStorage->removeValue("RSS/hosts_cookies");
settingsStorage->removeValue("Rss/Session/EnableProcessing");
settingsStorage->removeValue("Rss/Session/RefreshInterval");
settingsStorage->removeValue("Rss/Session/MaxArticlesPerFeed");
settingsStorage->removeValue("Rss/AutoDownloader/EnableProcessing");
}
Session::~Session()
@@ -295,20 +316,6 @@ void Session::loadFolder(const QJsonObject &jsonObj, Folder *folder)
void Session::loadLegacy()
{
struct LegacySettingsDeleter
{
~LegacySettingsDeleter()
{
auto settingsStorage = SettingsStorage::instance();
settingsStorage->removeValue("Rss/streamList");
settingsStorage->removeValue("Rss/streamAlias");
settingsStorage->removeValue("Rss/open_folders");
settingsStorage->removeValue("Rss/qt5/splitter_h");
settingsStorage->removeValue("Rss/qt5/splitterMain");
settingsStorage->removeValue("Rss/hosts_cookies");
}
} legacySettingsDeleter;
const QStringList legacyFeedPaths = SettingsStorage::instance()->loadValue("Rss/streamList").toStringList();
const QStringList feedAliases = SettingsStorage::instance()->loadValue("Rss/streamAlias").toStringList();
if (legacyFeedPaths.size() != feedAliases.size()) {

View File

@@ -632,19 +632,6 @@ void Utils::Misc::openFolderSelect(const QString &absolutePath)
#endif
}
QSize Utils::Misc::smallIconSize()
{
// Get DPI scaled icon size (device-dependent), see QT source
int s = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
return QSize(s, s);
}
QSize Utils::Misc::largeIconSize()
{
// Get DPI scaled icon size (device-dependent), see QT source
int s = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
return QSize(s, s);
}
#endif // DISABLE_GUI
QString Utils::Misc::osName()

View File

@@ -103,8 +103,6 @@ namespace Utils
void openFolderSelect(const QString& absolutePath);
QPoint screenCenter(const QWidget *w);
QSize smallIconSize();
QSize largeIconSize();
#endif
#ifdef Q_OS_WIN

View File

@@ -33,9 +33,9 @@
#include <QByteArray>
#include <QCollator>
#include <QtGlobal>
#include <QLocale>
#include <QRegExp>
#include <QtGlobal>
#ifdef Q_OS_MAC
#include <QThreadStorage>
#endif
@@ -45,110 +45,106 @@ namespace
class NaturalCompare
{
public:
explicit NaturalCompare(const bool caseSensitive = true)
: m_caseSensitive(caseSensitive)
explicit NaturalCompare(const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
: m_caseSensitivity(caseSensitivity)
{
#if defined(Q_OS_WIN)
#ifdef Q_OS_WIN
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on an OS older than Win7
#else
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
bool operator()(const QString &left, const QString &right) const
{
#if defined(Q_OS_WIN)
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(left, right);
m_collator.setCaseSensitivity(caseSensitivity);
#endif
return (m_collator.compare(left, right) < 0);
}
bool lessThan(const QString &left, const QString &right) const
int operator()(const QString &left, const QString &right) const
{
// 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 = m_caseSensitive ? left[posL] : left[posL].toLower();
QChar rightChar = m_caseSensitive ? right[posR] : 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;
int numL = left.midRef(startL, posL - startL).toInt();
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
int numR = right.midRef(startR, posR - startR).toInt();
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
#ifdef Q_OS_WIN
return compare(left, right);
#else
return m_collator.compare(left, right);
#endif
}
private:
int compare(const QString &left, const QString &right) const
{
// Return value <0: `left` is smaller than `right`
// Return value >0: `left` is greater than `right`
// Return value =0: both strings are equal
int posL = 0;
int posR = 0;
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
const QChar leftChar = (m_caseSensitivity == Qt::CaseSensitive) ? left[posL] : left[posL].toLower();
const QChar rightChar = (m_caseSensitivity == Qt::CaseSensitive) ? right[posR] : right[posR].toLower();
// Compare only non-digits.
// Numbers should be compared as a whole
// otherwise the string->int conversion can yield a wrong value
if ((leftChar == rightChar) && !leftChar.isDigit()) {
// compare next character
++posL;
++posR;
}
else if (leftChar.isDigit() && rightChar.isDigit()) {
// Both are digits, compare the numbers
const auto consumeNumber = [](const QString &str, int &pos) -> int
{
const int start = pos;
while ((pos < str.size()) && str[pos].isDigit())
++pos;
return str.midRef(start, (pos - start)).toInt();
};
const int numL = consumeNumber(left, posL);
const int numR = consumeNumber(right, posR);
if (numL != numR)
return (numL - numR);
// String + digits do match and we haven't hit the end of both strings
// then continue to consume the remainings
}
else {
return (leftChar.unicode() - rightChar.unicode());
}
}
}
QCollator m_collator;
const bool m_caseSensitive;
const Qt::CaseSensitivity m_caseSensitivity;
};
}
bool Utils::String::naturalCompareCaseSensitive(const QString &left, const QString &right)
int Utils::String::naturalCompare(const QString &left, const QString &right, const Qt::CaseSensitivity caseSensitivity)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
if (caseSensitivity == Qt::CaseSensitive) {
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(true));
return (nCmp.localData())(left, right);
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData())
nCmp.setLocalData(NaturalCompare(Qt::CaseSensitive));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(true);
return nCmp(left, right);
thread_local NaturalCompare nCmp(Qt::CaseSensitive);
return nCmp(left, right);
#endif
}
}
bool Utils::String::naturalCompareCaseInsensitive(const QString &left, const QString &right)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
#ifdef Q_OS_MAC
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(false));
if (!nCmp.hasLocalData())
nCmp.setLocalData(NaturalCompare(Qt::CaseInsensitive));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(false);
thread_local NaturalCompare nCmp(Qt::CaseInsensitive);
return nCmp(left, right);
#endif
}
@@ -188,4 +184,3 @@ QString Utils::String::wildcardToRegex(const QString &pattern)
{
return qt_regexp_toCanonical(pattern, QRegExp::Wildcard);
}

View File

@@ -45,8 +45,12 @@ namespace Utils
// Taken from https://crackstation.net/hashing-security.htm
bool slowEquals(const QByteArray &a, const QByteArray &b);
bool naturalCompareCaseSensitive(const QString &left, const QString &right);
bool naturalCompareCaseInsensitive(const QString &left, const QString &right);
int naturalCompare(const QString &left, const QString &right, const Qt::CaseSensitivity caseSensitivity);
template <Qt::CaseSensitivity caseSensitivity>
bool naturalLessThan(const QString &left, const QString &right)
{
return (naturalCompare(left, right, caseSensitivity) < 0);
}
QString wildcardToRegex(const QString &pattern);

View File

@@ -74,6 +74,7 @@ transferlistfilterswidget.h
transferlistsortmodel.h
transferlistwidget.h
updownratiodlg.h
utils.h
)
set(QBT_GUI_SOURCES
@@ -119,6 +120,7 @@ transferlistfilterswidget.cpp
transferlistsortmodel.cpp
transferlistwidget.cpp
updownratiodlg.cpp
utils.cpp
)
if (APPLE)

View File

@@ -18,11 +18,7 @@
<item>
<layout class="QHBoxLayout" name="titleHBoxLayout">
<item>
<widget class="QLabel" name="logo">
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/skin/qbittorrent32.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="logo"/>
</item>
<item>
<widget class="QLabel" name="lb_name">
@@ -57,11 +53,7 @@
</attribute>
<layout class="QGridLayout" name="aboutTabLayout">
<item row="0" column="0">
<widget class="QLabel" name="mascot_lbl">
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/skin/mascot.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="labelMascot"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lb_about">

View File

@@ -31,10 +31,12 @@
#ifndef ABOUT_H
#define ABOUT_H
#include "ui_about.h"
#include <QFile>
#include "base/utils/misc.h"
#include "base/unicodestrings.h"
#include "ui_about.h"
#include "utils.h"
class about: public QDialog, private Ui::AboutDlg
{
@@ -53,24 +55,28 @@ public:
lb_name->setText("<b><h2>qBittorrent " QBT_VERSION " (32-bit)</h2></b>");
#endif
logo->setPixmap(Utils::Gui::scaledPixmap(":/icons/skin/qbittorrent32.png", this));
// About
QString aboutText = QString(
"<p style=\"white-space: pre-wrap;\">"
"%1\n\n"
"%2\n\n"
"<table>"
"<tr><td>%3</td><td><a href=\"http://www.qbittorrent.org\">http://www.qbittorrent.org</a></td></tr>"
"<tr><td>%3</td><td><a href=\"https://www.qbittorrent.org\">https://www.qbittorrent.org</a></td></tr>"
"<tr><td>%4</td><td><a href=\"http://forum.qbittorrent.org\">http://forum.qbittorrent.org</a></td></tr>"
"<tr><td>%5</td><td><a href=\"http://bugs.qbittorrent.org\">http://bugs.qbittorrent.org</a></td></tr>"
"</table>"
"</p>")
.arg(tr("An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar."))
.arg(tr("Copyright %1 2006-2017 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT)))
.arg(tr("Copyright %1 2006-2018 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT)))
.arg(tr("Home Page:"))
.arg(tr("Forum:"))
.arg(tr("Bug Tracker:"));
lb_about->setText(aboutText);
labelMascot->setPixmap(Utils::Gui::scaledPixmap(":/icons/skin/mascot.png", this));
// Thanks
QFile thanksfile(":/thanks.html");
if (thanksfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -97,6 +103,7 @@ public:
label_12->setText(Utils::Misc::libtorrentVersionString());
label_13->setText(Utils::Misc::boostVersionString());
Utils::Gui::resize(this);
show();
}
};

View File

@@ -28,36 +28,38 @@
* Contact : chris@qbittorrent.org
*/
#include <QDebug>
#include <QString>
#include <QFile>
#include <QUrl>
#include <QMenu>
#include <QFileDialog>
#include <QPushButton>
#include "addnewtorrentdialog.h"
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QMenu>
#include <QPushButton>
#include <QString>
#include <QUrl>
#include "autoexpandabledialog.h"
#include "base/bittorrent/magneturi.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/settingvalue.h"
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/magneturi.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/torrentfileguard.h"
#include "base/unicodestrings.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/torrentfileguard.h"
#include "base/unicodestrings.h"
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
#include "messageboxraised.h"
#include "proplistdelegate.h"
#include "torrentcontentmodel.h"
#include "torrentcontentfiltermodel.h"
#include "torrentcontentmodel.h"
#include "ui_addnewtorrentdialog.h"
#include "addnewtorrentdialog.h"
#include "utils.h"
namespace
{
@@ -128,7 +130,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
// Load categories
QStringList categories = session->categories().keys();
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
std::sort(categories.begin(), categories.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString();
if (!m_torrentParams.category.isEmpty())
@@ -212,10 +214,11 @@ CachedSettingValue<int> &AddNewTorrentDialog::savePathHistoryLengthSetting()
void AddNewTorrentDialog::loadState()
{
m_headerState = settings()->loadValue(KEY_TREEHEADERSTATE).toByteArray();
int width = settings()->loadValue(KEY_WIDTH, -1).toInt();
QSize geo = size();
geo.setWidth(width);
resize(geo);
const QSize newSize = Utils::Gui::scaledSize(this, size());
const int width = settings()->loadValue(KEY_WIDTH, newSize.width()).toInt();
const int height = newSize.height();
resize(width, height);
ui->adv_button->setChecked(settings()->loadValue(KEY_EXPANDED).toBool());
}

View File

@@ -32,6 +32,7 @@
#include "mainwindow.h"
#include "ui_autoexpandabledialog.h"
#include "utils.h"
AutoExpandableDialog::AutoExpandableDialog(QWidget *parent)
: QDialog(parent)
@@ -68,30 +69,29 @@ QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, con
void AutoExpandableDialog::showEvent(QShowEvent *e)
{
// Overriding showEvent is required for consistent UI with fixed size under custom DPI
// Show dialog
QDialog::showEvent(e);
// and resize textbox to fit the text
// NOTE: For some strange reason QFontMetrics gets more accurate
// when called from showEvent. Only 6 symbols off instead of 11 symbols off.
int textW = m_ui->textEdit->fontMetrics().width(m_ui->textEdit->text()) + 4;
int wd = textW;
// Show dialog and resize textbox to fit the text
// NOTE: For unknown reason QFontMetrics gets more accurate when called from showEvent.
int wd = m_ui->textEdit->fontMetrics().width(m_ui->textEdit->text()) + 4;
if (!windowTitle().isEmpty()) {
int w = fontMetrics().width(windowTitle());
if (w > wd)
wd = w;
// not really the font metrics in window title, so we enlarge it a bit,
// including the small icon and close button width
int w = fontMetrics().width(windowTitle()) * 1.8;
wd = std::max(wd, w);
}
if (!m_ui->textLabel->text().isEmpty()) {
int w = m_ui->textLabel->fontMetrics().width(m_ui->textLabel->text());
if (w > wd)
wd = w;
wd = std::max(wd, w);
}
// Now resize the dialog to fit the contents
// max width of text from either of: label, title, textedit
// If the value is less than dialog default size, default size is used
if (wd > width())
resize(width() - m_ui->verticalLayout->sizeHint().width() + wd, height());
if (wd > width()) {
QSize size = {width() - m_ui->verticalLayout->sizeHint().width() + wd, height()};
Utils::Gui::resize(this, size);
}
}

View File

@@ -36,6 +36,7 @@
#include "base/bittorrent/session.h"
#include "base/utils/net.h"
#include "ui_banlistoptions.h"
#include "utils.h"
BanListOptions::BanListOptions(QWidget *parent)
: QDialog(parent)
@@ -52,6 +53,8 @@ BanListOptions::BanListOptions(QWidget *parent)
m_ui->bannedIPList->setModel(m_sortFilter);
m_ui->bannedIPList->sortByColumn(0, Qt::AscendingOrder);
m_ui->buttonBanIP->setEnabled(false);
Utils::Gui::resize(this);
}
BanListOptions::~BanListOptions()

View File

@@ -50,8 +50,10 @@ bool CategoryFilterProxyModel::lessThan(const QModelIndex &left, const QModelInd
{
// "All" and "Uncategorized" must be left in place
if (CategoryFilterModel::isSpecialItem(left) || CategoryFilterModel::isSpecialItem(right))
return left.row() < right.row();
else
return Utils::String::naturalCompareCaseInsensitive(
left.data().toString(), right.data().toString());
return (left < right);
int result = Utils::String::naturalCompare(left.data().toString(), right.data().toString()
, Qt::CaseInsensitive);
return (result < 0);
}

View File

@@ -35,11 +35,11 @@
#include <QMessageBox>
#include "base/bittorrent/session.h"
#include "base/utils/misc.h"
#include "categoryfiltermodel.h"
#include "categoryfilterproxymodel.h"
#include "guiiconprovider.h"
#include "torrentcategorydialog.h"
#include "utils.h"
namespace
{
@@ -70,7 +70,7 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setUniformRowHeights(true);
setHeaderHidden(true);
setIconSize(Utils::Misc::smallIconSize());
setIconSize(Utils::Gui::smallIconSize());
#ifdef Q_OS_MAC
setAttribute(Qt::WA_MacShowFocusRect, false);
#endif

View File

@@ -35,6 +35,7 @@
#include "cookiesmodel.h"
#include "guiiconprovider.h"
#include "ui_cookiesdialog.h"
#include "utils.h"
#define SETTINGS_KEY(name) "CookiesDialog/" name
const QString KEY_SIZE = SETTINGS_KEY("Size");
@@ -50,6 +51,8 @@ CookiesDialog::CookiesDialog(QWidget *parent)
setWindowIcon(GuiIconProvider::instance()->getIcon("preferences-web-browser-cookies"));
m_ui->buttonAdd->setIcon(GuiIconProvider::instance()->getIcon("list-add"));
m_ui->buttonDelete->setIcon(GuiIconProvider::instance()->getIcon("list-remove"));
m_ui->buttonAdd->setIconSize(Utils::Gui::mediumIconSize());
m_ui->buttonDelete->setIconSize(Utils::Gui::mediumIconSize());
m_ui->treeView->setModel(m_cookiesModel);
if (m_cookiesModel->rowCount() > 0)
@@ -57,7 +60,7 @@ CookiesDialog::CookiesDialog(QWidget *parent)
m_cookiesModel->index(0, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
resize(SettingsStorage::instance()->loadValue(KEY_SIZE, size()).toSize());
Utils::Gui::resize(this, SettingsStorage::instance()->loadValue(KEY_SIZE).toSize());
m_ui->treeView->header()->restoreState(
SettingsStorage::instance()->loadValue(KEY_COOKIESVIEWSTATE).toByteArray());
}

View File

@@ -49,12 +49,6 @@
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
@@ -78,12 +72,6 @@
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>

View File

@@ -33,11 +33,13 @@
#include <QDialog>
#include <QPushButton>
#include "ui_confirmdeletiondlg.h"
#include "base/preferences.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "guiiconprovider.h"
#include "ui_confirmdeletiondlg.h"
#include "utils.h"
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
Q_OBJECT
@@ -50,13 +52,17 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
else
label->setText(tr("Are you sure you want to delete these %1 torrents from the transfer list?", "Are you sure you want to delete these 5 torrents from the transfer list?").arg(QString::number(size)));
// Icons
lbl_warn->setPixmap(GuiIconProvider::instance()->getIcon("dialog-warning").pixmap(lbl_warn->height()));
lbl_warn->setFixedWidth(lbl_warn->height());
const QSize iconSize = Utils::Gui::largeIconSize();
lbl_warn->setPixmap(GuiIconProvider::instance()->getIcon("dialog-warning").pixmap(iconSize));
lbl_warn->setFixedWidth(iconSize.width());
rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked"));
rememberBtn->setIconSize(Utils::Gui::mediumIconSize());
checkPermDelete->setChecked(defaultDeleteFiles || Preferences::instance()->deleteTorrentFilesAsDefault());
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
Utils::Gui::resize(this);
}
bool shouldDeleteLocalFiles() const {

View File

@@ -40,6 +40,7 @@
#include <QStringList>
#include "ui_downloadfromurldlg.h"
#include "utils.h"
class downloadFromURL : public QDialog, private Ui::downloadFromURL
{
@@ -82,6 +83,7 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL
if (clip_txt_list_cleaned.size() > 0)
textUrls->setText(clip_txt_list_cleaned.join("\n"));
Utils::Gui::resize(this);
show();
}

View File

@@ -30,6 +30,7 @@
#include <QAction>
#include <QApplication>
#include <QCoreApplication>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QStyle>
@@ -40,6 +41,7 @@
namespace
{
const char i18nContext[] = "FileSystemPathEdit";
struct TrStringWithComment
{
const char *source;
@@ -47,18 +49,18 @@ namespace
QString tr() const
{
return QObject::tr(source, comment);
return QCoreApplication::translate(i18nContext, source, comment);
}
};
constexpr TrStringWithComment browseButtonBriefText =
QT_TRANSLATE_NOOP3("FileSystemPathEdit", "...", "Launch file dialog button text (brief)");
QT_TRANSLATE_NOOP3(i18nContext, "...", "Launch file dialog button text (brief)");
constexpr TrStringWithComment browseButtonFullText =
QT_TRANSLATE_NOOP3("FileSystemPathEdit", "&Browse...", "Launch file dialog button text (full)");
QT_TRANSLATE_NOOP3(i18nContext, "&Browse...", "Launch file dialog button text (full)");
constexpr TrStringWithComment defaultDialogCaptionForFile =
QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a file", "Caption for file open/save dialog");
QT_TRANSLATE_NOOP3(i18nContext, "Choose a file", "Caption for file open/save dialog");
constexpr TrStringWithComment defaultDialogCaptionForDirectory =
QT_TRANSLATE_NOOP3("FileSystemPathEdit", "Choose a folder", "Caption for directory open dialog");
QT_TRANSLATE_NOOP3(i18nContext, "Choose a folder", "Caption for directory open dialog");
}
class FileSystemPathEdit::FileSystemPathEditPrivate
@@ -154,7 +156,9 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::modeChanged()
switch (m_mode) {
case FileSystemPathEdit::Mode::FileOpen:
case FileSystemPathEdit::Mode::FileSave:
pixmap = QStyle::SP_DialogOpenButton;
#ifdef Q_OS_WIN
pixmap = QStyle::SP_DirOpenIcon;
#endif
showDirsOnly = false;
break;
case FileSystemPathEdit::Mode::DirectoryOpen:

View File

@@ -1,124 +1,126 @@
INCLUDEPATH += $$PWD
include(lineedit/lineedit.pri)
include(properties/properties.pri)
include(powermanagement/powermanagement.pri)
include(properties/properties.pri)
unix:!macx:dbus: include(qtnotify/qtnotify.pri)
HEADERS += \
$$PWD/mainwindow.h \
$$PWD/transferlistwidget.h \
$$PWD/transferlistdelegate.h \
$$PWD/transferlistfilterswidget.h \
$$PWD/transferlistsortmodel.h \
$$PWD/torrentcategorydialog.h \
$$PWD/torrentcontentmodel.h \
$$PWD/torrentcontentmodelitem.h \
$$PWD/torrentcontentmodelfolder.h \
$$PWD/torrentcontentmodelfile.h \
$$PWD/torrentcontentfiltermodel.h \
$$PWD/torrentcontenttreeview.h \
$$PWD/deletionconfirmationdlg.h \
$$PWD/statusbar.h \
$$PWD/speedlimitdlg.h \
$$PWD/about_imp.h \
$$PWD/previewlistdelegate.h \
$$PWD/downloadfromurldlg.h \
$$PWD/trackerlogin.h \
$$PWD/hidabletabwidget.h \
$$PWD/executionlog.h \
$$PWD/guiiconprovider.h \
$$PWD/updownratiodlg.h \
$$PWD/loglistwidget.h \
$$PWD/addnewtorrentdialog.h \
$$PWD/advancedsettings.h \
$$PWD/autoexpandabledialog.h \
$$PWD/statsdialog.h \
$$PWD/banlistoptions.h \
$$PWD/categoryfiltermodel.h \
$$PWD/categoryfilterproxymodel.h \
$$PWD/categoryfilterwidget.h \
$$PWD/cookiesdialog.h \
$$PWD/cookiesmodel.h \
$$PWD/deletionconfirmationdlg.h \
$$PWD/downloadfromurldlg.h \
$$PWD/executionlog.h \
$$PWD/fspathedit.h \
$$PWD/fspathedit_p.h \
$$PWD/guiiconprovider.h \
$$PWD/hidabletabwidget.h \
$$PWD/ipsubnetwhitelistoptionsdialog.h \
$$PWD/loglistwidget.h \
$$PWD/mainwindow.h \
$$PWD/messageboxraised.h \
$$PWD/optionsdlg.h \
$$PWD/advancedsettings.h \
$$PWD/shutdownconfirmdlg.h \
$$PWD/torrentmodel.h \
$$PWD/torrentcreatordlg.h \
$$PWD/previewlistdelegate.h \
$$PWD/previewselectdialog.h \
$$PWD/rss/articlelistwidget.h \
$$PWD/rss/automatedrssdownloader.h \
$$PWD/rss/feedlistwidget.h \
$$PWD/rss/htmlbrowser.h \
$$PWD/rss/rsswidget.h \
$$PWD/scanfoldersdelegate.h \
$$PWD/search/searchwidget.h \
$$PWD/search/searchtab.h \
$$PWD/search/pluginselectdlg.h \
$$PWD/search/pluginsourcedlg.h \
$$PWD/search/searchlistdelegate.h \
$$PWD/search/searchsortmodel.h \
$$PWD/cookiesmodel.h \
$$PWD/cookiesdialog.h \
$$PWD/categoryfiltermodel.h \
$$PWD/categoryfilterproxymodel.h \
$$PWD/categoryfilterwidget.h \
$$PWD/search/searchtab.h \
$$PWD/search/searchwidget.h \
$$PWD/shutdownconfirmdlg.h \
$$PWD/speedlimitdlg.h \
$$PWD/statsdialog.h \
$$PWD/statusbar.h \
$$PWD/tagfiltermodel.h \
$$PWD/tagfilterproxymodel.h \
$$PWD/tagfilterwidget.h \
$$PWD/banlistoptions.h \
$$PWD/ipsubnetwhitelistoptionsdialog.h \
$$PWD/rss/rsswidget.h \
$$PWD/rss/articlelistwidget.h \
$$PWD/rss/feedlistwidget.h \
$$PWD/rss/automatedrssdownloader.h \
$$PWD/rss/htmlbrowser.h \
$$PWD/fspathedit.h \
$$PWD/fspathedit_p.h \
$$PWD/previewselectdialog.h \
$$PWD/torrentcategorydialog.h \
$$PWD/torrentcontentfiltermodel.h \
$$PWD/torrentcontentmodel.h \
$$PWD/torrentcontentmodelfile.h \
$$PWD/torrentcontentmodelfolder.h \
$$PWD/torrentcontentmodelitem.h \
$$PWD/torrentcontenttreeview.h \
$$PWD/torrentcreatordlg.h \
$$PWD/torrentmodel.h \
$$PWD/trackerlogin.h \
$$PWD/transferlistdelegate.h \
$$PWD/transferlistfilterswidget.h \
$$PWD/transferlistsortmodel.h \
$$PWD/transferlistwidget.h \
$$PWD/updownratiodlg.h \
$$PWD/utils.h
SOURCES += \
$$PWD/mainwindow.cpp \
$$PWD/transferlistwidget.cpp \
$$PWD/transferlistsortmodel.cpp \
$$PWD/transferlistdelegate.cpp \
$$PWD/transferlistfilterswidget.cpp \
$$PWD/torrentcategorydialog.cpp \
$$PWD/torrentcontentmodel.cpp \
$$PWD/torrentcontentmodelitem.cpp \
$$PWD/torrentcontentmodelfolder.cpp \
$$PWD/torrentcontentmodelfile.cpp \
$$PWD/torrentcontentfiltermodel.cpp \
$$PWD/torrentcontenttreeview.cpp \
$$PWD/executionlog.cpp \
$$PWD/speedlimitdlg.cpp \
$$PWD/guiiconprovider.cpp \
$$PWD/updownratiodlg.cpp \
$$PWD/loglistwidget.cpp \
$$PWD/addnewtorrentdialog.cpp \
$$PWD/autoexpandabledialog.cpp \
$$PWD/statsdialog.cpp \
$$PWD/messageboxraised.cpp \
$$PWD/statusbar.cpp \
$$PWD/advancedsettings.cpp \
$$PWD/trackerlogin.cpp \
$$PWD/autoexpandabledialog.cpp \
$$PWD/banlistoptions.cpp \
$$PWD/categoryfiltermodel.cpp \
$$PWD/categoryfilterproxymodel.cpp \
$$PWD/categoryfilterwidget.cpp \
$$PWD/cookiesdialog.cpp \
$$PWD/cookiesmodel.cpp \
$$PWD/executionlog.cpp \
$$PWD/fspathedit.cpp \
$$PWD/fspathedit_p.cpp \
$$PWD/guiiconprovider.cpp \
$$PWD/ipsubnetwhitelistoptionsdialog.cpp \
$$PWD/loglistwidget.cpp \
$$PWD/mainwindow.cpp \
$$PWD/messageboxraised.cpp \
$$PWD/optionsdlg.cpp \
$$PWD/shutdownconfirmdlg.cpp \
$$PWD/torrentmodel.cpp \
$$PWD/torrentcreatordlg.cpp \
$$PWD/previewselectdialog.cpp \
$$PWD/rss/articlelistwidget.cpp \
$$PWD/rss/automatedrssdownloader.cpp \
$$PWD/rss/feedlistwidget.cpp \
$$PWD/rss/htmlbrowser.cpp \
$$PWD/rss/rsswidget.cpp \
$$PWD/scanfoldersdelegate.cpp \
$$PWD/search/searchwidget.cpp \
$$PWD/search/searchtab.cpp \
$$PWD/search/pluginselectdlg.cpp \
$$PWD/search/pluginsourcedlg.cpp \
$$PWD/search/searchlistdelegate.cpp \
$$PWD/search/searchsortmodel.cpp \
$$PWD/cookiesmodel.cpp \
$$PWD/cookiesdialog.cpp \
$$PWD/categoryfiltermodel.cpp \
$$PWD/categoryfilterproxymodel.cpp \
$$PWD/categoryfilterwidget.cpp \
$$PWD/search/searchtab.cpp \
$$PWD/search/searchwidget.cpp \
$$PWD/shutdownconfirmdlg.cpp \
$$PWD/speedlimitdlg.cpp \
$$PWD/statsdialog.cpp \
$$PWD/statusbar.cpp \
$$PWD/tagfiltermodel.cpp \
$$PWD/tagfilterproxymodel.cpp \
$$PWD/tagfilterwidget.cpp \
$$PWD/banlistoptions.cpp \
$$PWD/ipsubnetwhitelistoptionsdialog.cpp \
$$PWD/rss/rsswidget.cpp \
$$PWD/rss/articlelistwidget.cpp \
$$PWD/rss/feedlistwidget.cpp \
$$PWD/rss/automatedrssdownloader.cpp \
$$PWD/rss/htmlbrowser.cpp \
$$PWD/fspathedit.cpp \
$$PWD/fspathedit_p.cpp \
$$PWD/previewselectdialog.cpp \
$$PWD/torrentcategorydialog.cpp \
$$PWD/torrentcontentfiltermodel.cpp \
$$PWD/torrentcontentmodel.cpp \
$$PWD/torrentcontentmodelfile.cpp \
$$PWD/torrentcontentmodelfolder.cpp \
$$PWD/torrentcontentmodelitem.cpp \
$$PWD/torrentcontenttreeview.cpp \
$$PWD/torrentcreatordlg.cpp \
$$PWD/torrentmodel.cpp \
$$PWD/trackerlogin.cpp \
$$PWD/transferlistdelegate.cpp \
$$PWD/transferlistfilterswidget.cpp \
$$PWD/transferlistsortmodel.cpp \
$$PWD/transferlistwidget.cpp \
$$PWD/updownratiodlg.cpp \
$$PWD/utils.cpp
win32|macx {
HEADERS += $$PWD/programupdater.h
@@ -131,30 +133,30 @@ macx {
}
FORMS += \
$$PWD/mainwindow.ui \
$$PWD/about.ui \
$$PWD/previewselectdialog.ui \
$$PWD/login.ui \
$$PWD/downloadfromurldlg.ui \
$$PWD/bandwidth_limit.ui \
$$PWD/updownratiodlg.ui \
$$PWD/confirmdeletiondlg.ui \
$$PWD/shutdownconfirmdlg.ui \
$$PWD/executionlog.ui \
$$PWD/addnewtorrentdialog.ui \
$$PWD/autoexpandabledialog.ui \
$$PWD/statsdialog.ui \
$$PWD/bandwidth_limit.ui \
$$PWD/banlistoptions.ui \
$$PWD/confirmdeletiondlg.ui \
$$PWD/cookiesdialog.ui \
$$PWD/downloadfromurldlg.ui \
$$PWD/executionlog.ui \
$$PWD/ipsubnetwhitelistoptionsdialog.ui \
$$PWD/login.ui \
$$PWD/mainwindow.ui \
$$PWD/optionsdlg.ui \
$$PWD/torrentcreatordlg.ui \
$$PWD/search/searchwidget.ui \
$$PWD/previewselectdialog.ui \
$$PWD/rss/automatedrssdownloader.ui \
$$PWD/rss/rsswidget.ui \
$$PWD/search/pluginselectdlg.ui \
$$PWD/search/pluginsourcedlg.ui \
$$PWD/search/searchtab.ui \
$$PWD/cookiesdialog.ui \
$$PWD/banlistoptions.ui \
$$PWD/ipsubnetwhitelistoptionsdialog.ui \
$$PWD/rss/rsswidget.ui \
$$PWD/rss/automatedrssdownloader.ui \
$$PWD/torrentcategorydialog.ui
$$PWD/search/searchwidget.ui \
$$PWD/shutdownconfirmdlg.ui \
$$PWD/statsdialog.ui \
$$PWD/torrentcategorydialog.ui \
$$PWD/torrentcreatordlg.ui \
$$PWD/updownratiodlg.ui
RESOURCES += $$PWD/about.qrc

View File

@@ -37,6 +37,7 @@
#include "base/preferences.h"
#include "base/utils/net.h"
#include "ui_ipsubnetwhitelistoptionsdialog.h"
#include "utils.h"
IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent)
: QDialog(parent)
@@ -57,6 +58,8 @@ IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent)
m_ui->whitelistedIPSubnetList->setModel(m_sortFilter);
m_ui->whitelistedIPSubnetList->sortByColumn(0, Qt::AscendingOrder);
m_ui->buttonWhitelistIPSubnet->setEnabled(false);
Utils::Gui::resize(this);
}
IPSubnetWhitelistOptionsDialog::~IPSubnetWhitelistOptionsDialog()
@@ -68,12 +71,10 @@ void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted()
{
if (m_modified) {
// save to session
QList<Utils::Net::Subnet> subnets;
QStringList subnets;
// Operate on the m_sortFilter to grab the strings in sorted order
for (int i = 0; i < m_sortFilter->rowCount(); ++i) {
const QString subnet = m_sortFilter->index(i, 0).data().toString();
subnets.append(QHostAddress::parseSubnet(subnet));
}
for (int i = 0; i < m_sortFilter->rowCount(); ++i)
subnets.append(m_sortFilter->index(i, 0).data().toString());
Preferences::instance()->setWebUiAuthSubnetWhitelist(subnets);
QDialog::accept();
}

View File

@@ -25,7 +25,7 @@
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_21">
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<widget class="QTreeView" name="whitelistedIPSubnetList">
<property name="rootIsDecorated">
@@ -46,7 +46,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="txtIPSubnet">
<property name="placeholderText">
@@ -54,6 +54,10 @@
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="buttonWhitelistIPSubnet">
<property name="text">

View File

@@ -6,11 +6,5 @@ set(QBT_LINEEDIT_HEADERS
src/lineedit.h
)
set(QBT_LINEEDIT_RESOURCES
resources/lineeditimages.qrc
)
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS})
target_link_libraries(qbt_lineedit Qt5::Widgets)
qbt_target_sources(${QBT_LINEEDIT_RESOURCES})

View File

@@ -1,4 +1,3 @@
INCLUDEPATH += $$PWD/src
HEADERS += $$PWD/src/lineedit.h
SOURCES += $$PWD/src/lineedit.cpp
RESOURCES += $$PWD/resources/lineeditimages.qrc

View File

@@ -1,5 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>lineeditimages/search.png</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

View File

@@ -8,37 +8,35 @@
****************************************************************************/
#include "lineedit.h"
#include <algorithm>
#include <QResizeEvent>
#include <QStyle>
#include <QToolButton>
#include <QResizeEvent>
#include "guiiconprovider.h"
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
{
QPixmap pixmap1(":/lineeditimages/search.png");
searchButton = new QToolButton(this);
searchButton->setIcon(QIcon(pixmap1));
searchButton->setIconSize(pixmap1.size());
searchButton->setCursor(Qt::ArrowCursor);
searchButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
QSize searchButtonHint = searchButton->sizeHint();
m_searchButton = new QToolButton(this);
m_searchButton->setIcon(GuiIconProvider::instance()->getIcon("edit-find"));
m_searchButton->setCursor(Qt::ArrowCursor);
m_searchButton->setStyleSheet("QToolButton {border: none; padding: 2px;}");
// padding between text and widget borders
setStyleSheet(QString("QLineEdit {padding-left: %1px;}").arg(m_searchButton->sizeHint().width()));
QSize clearButtonHint(0, 0);
setClearButtonEnabled(true);
setStyleSheet(QString("QLineEdit { padding-left: %1px; }").arg(searchButtonHint.width())); // padding between text and widget borders
QSize widgetHint = sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setMaximumHeight(std::max({ widgetHint.height(), searchButtonHint.height(), clearButtonHint.height() }) + frameWidth * 2);
const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setMaximumHeight(std::max(sizeHint().height(), m_searchButton->sizeHint().height()) + frameWidth * 2);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
}
void LineEdit::resizeEvent(QResizeEvent *e)
{
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
QSize sz = searchButton->sizeHint();
searchButton->move(frameWidth, (e->size().height() - sz.height()) / 2);
const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
m_searchButton->move(frameWidth, (e->size().height() - m_searchButton->sizeHint().height()) / 2);
}

View File

@@ -22,10 +22,10 @@ public:
LineEdit(QWidget *parent);
protected:
void resizeEvent(QResizeEvent *e);
void resizeEvent(QResizeEvent *e) override;
private:
QToolButton *searchButton;
QToolButton *m_searchButton;
};
#endif // LIENEDIT_H

View File

@@ -33,8 +33,12 @@
#include <QSize>
#include <objc/objc.h>
QPixmap pixmapForExtension(const QString &ext, const QSize &size);
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...));
void displayNotification(const QString &title, const QString &message);
namespace MacUtils
{
QPixmap pixmapForExtension(const QString &ext, const QSize &size);
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...));
void displayNotification(const QString &title, const QString &message);
void openFiles(const QSet<QString> &pathsList);
}
#endif // MACUTILITIES_H

View File

@@ -28,56 +28,72 @@
#include "macutilities.h"
#include <QSet>
#include <QtMac>
#include <objc/message.h>
#import <Cocoa/Cocoa.h>
QPixmap pixmapForExtension(const QString &ext, const QSize &size)
namespace MacUtils
{
@autoreleasepool {
NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()];
if (image) {
NSRect rect = NSMakeRect(0, 0, size.width(), size.height());
CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil];
return QtMac::fromCGImageRef(cgImage);
QPixmap pixmapForExtension(const QString &ext, const QSize &size)
{
@autoreleasepool {
NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()];
if (image) {
NSRect rect = NSMakeRect(0, 0, size.width(), size.height());
CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil];
return QtMac::fromCGImageRef(cgImage);
}
return QPixmap();
}
}
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...))
{
NSApplication *appInst = [NSApplication sharedApplication];
if (!appInst)
return;
Class delClass = [[appInst delegate] class];
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
if (class_getInstanceMethod(delClass, shouldHandle)) {
if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"))
qDebug("Registered dock click handler (replaced original method)");
else
qWarning("Failed to replace method for dock click handler");
}
else {
if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"))
qDebug("Registered dock click handler");
else
qWarning("Failed to register dock click handler");
}
}
void displayNotification(const QString &title, const QString &message)
{
@autoreleasepool {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = title.toNSString();
notification.informativeText = message.toNSString();
notification.soundName = NSUserNotificationDefaultSoundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
}
void openFiles(const QSet<QString> &pathsList)
{
@autoreleasepool {
NSMutableArray *pathURLs = [NSMutableArray arrayWithCapacity:pathsList.size()];
for (const auto &path : pathsList)
[pathURLs addObject:[NSURL fileURLWithPath:path.toNSString()]];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:pathURLs];
}
return QPixmap();
}
}
void overrideDockClickHandler(bool (*dockClickHandler)(id, SEL, ...))
{
NSApplication *appInst = [NSApplication sharedApplication];
if (!appInst)
return;
Class delClass = [[appInst delegate] class];
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
if (class_getInstanceMethod(delClass, shouldHandle)) {
if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"))
qDebug("Registered dock click handler (replaced original method)");
else
qWarning("Failed to replace method for dock click handler");
}
else {
if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"))
qDebug("Registered dock click handler");
else
qWarning("Failed to register dock click handler");
}
}
void displayNotification(const QString &title, const QString &message)
{
@autoreleasepool {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = title.toNSString();
notification.informativeText = message.toNSString();
notification.soundName = NSUserNotificationDefaultSoundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
}

View File

@@ -30,84 +30,84 @@
#include "mainwindow.h"
#ifdef Q_OS_MAC
#include <QtMacExtras>
#include <QtMac>
#endif
#include <QClipboard>
#include <QCloseEvent>
#include <QCryptographicHash>
#include <QDebug>
#include <QDesktopServices>
#include <QFileDialog>
#include <QFileSystemWatcher>
#include <QMessageBox>
#include <QMimeData>
#include <QProcess>
#include <QScrollBar>
#include <QShortcut>
#include <QSplitter>
#include <QStatusBar>
#include <QSysInfo>
#include <QtGlobal>
#include <QTimer>
#ifdef Q_OS_MAC
#include <QtMac>
#include <QtMacExtras>
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
#include <QDBusConnection>
#include "notifications.h"
#endif
#include <QDebug>
#include <QFileDialog>
#include <QFileSystemWatcher>
#include <QMessageBox>
#include <QTimer>
#include <QDesktopServices>
#include <QStatusBar>
#include <QClipboard>
#include <QCloseEvent>
#include <QShortcut>
#include <QScrollBar>
#include <QSplitter>
#include <QSysInfo>
#include <QMimeData>
#include <QCryptographicHash>
#include <QProcess>
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/logger.h"
#include "base/utils/misc.h"
#include "base/utils/fs.h"
#ifdef Q_OS_WIN
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#endif
#include "about_imp.h"
#include "addnewtorrentdialog.h"
#include "application.h"
#include "autoexpandabledialog.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/rss/rss_folder.h"
#include "base/rss/rss_session.h"
#include "base/settingsstorage.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "cookiesdialog.h"
#include "downloadfromurldlg.h"
#include "executionlog.h"
#include "guiiconprovider.h"
#include "hidabletabwidget.h"
#include "lineedit.h"
#include "optionsdlg.h"
#include "peerlistwidget.h"
#include "powermanagement.h"
#include "propertieswidget.h"
#include "rss/rsswidget.h"
#include "search/searchwidget.h"
#include "speedlimitdlg.h"
#include "statsdialog.h"
#include "statusbar.h"
#include "torrentcreatordlg.h"
#include "torrentmodel.h"
#include "trackerlist.h"
#include "transferlistfilterswidget.h"
#include "transferlistwidget.h"
#include "ui_mainwindow.h"
#include "utils.h"
#include "application.h"
#ifdef Q_OS_WIN
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#endif
#ifdef Q_OS_MAC
#include "macutilities.h"
#endif
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
#include "programupdater.h"
#endif
#include "powermanagement.h"
#include "guiiconprovider.h"
#include "torrentmodel.h"
#include "autoexpandabledialog.h"
#include "torrentcreatordlg.h"
#include "downloadfromurldlg.h"
#include "addnewtorrentdialog.h"
#include "statsdialog.h"
#include "cookiesdialog.h"
#include "speedlimitdlg.h"
#include "transferlistwidget.h"
#include "search/searchwidget.h"
#include "trackerlist.h"
#include "peerlistwidget.h"
#include "transferlistfilterswidget.h"
#include "propertieswidget.h"
#include "statusbar.h"
#include "rss/rsswidget.h"
#include "about_imp.h"
#include "optionsdlg.h"
#if LIBTORRENT_VERSION_NUM < 10100
#include "trackerlogin.h"
#endif
#include "lineedit.h"
#include "executionlog.h"
#include "hidabletabwidget.h"
#include "ui_mainwindow.h"
#ifdef Q_OS_MAC
#include "macutilities.h"
#endif
#ifdef Q_OS_MAC
void qt_mac_set_dock_menu(QMenu *menu);
@@ -231,7 +231,7 @@ MainWindow::MainWindow(QWidget *parent)
m_searchFilter = new LineEdit(this);
m_searchFilterAction = m_ui->toolBar->insertWidget(m_ui->actionLock, m_searchFilter);
m_searchFilter->setPlaceholderText(tr("Filter torrent list..."));
m_searchFilter->setFixedWidth(200);
m_searchFilter->setFixedWidth(Utils::Gui::scaledSize(this, 200));
QWidget *spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -843,6 +843,11 @@ void MainWindow::createKeyboardShortcuts()
m_ui->actionDelete->setShortcutContext(Qt::WidgetShortcut); // nullify its effect: delete key event is handled by respective widgets, not here
m_ui->actionDownloadFromURL->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_O);
m_ui->actionExit->setShortcut(Qt::CTRL + Qt::Key_Q);
#ifdef Q_OS_MAC
m_ui->actionCloseWindow->setShortcut(QKeySequence::Close);
#else
m_ui->actionCloseWindow->setVisible(false);
#endif
QShortcut *switchTransferShortcut = new QShortcut(Qt::ALT + Qt::Key_1, this);
connect(switchTransferShortcut, &QShortcut::activated, this, &MainWindow::displayTransferTab);
@@ -978,6 +983,16 @@ void MainWindow::on_actionExit_triggered()
close();
}
#ifdef Q_OS_MAC
void MainWindow::on_actionCloseWindow_triggered()
{
// On macOS window close is basically equivalent to window hide.
// If you decide to implement this functionality for other OS,
// then you will also need ui lock checks like in actionExit.
close();
}
#endif
QWidget *MainWindow::currentTabWidget() const
{
if (isMinimized() || !isVisible())
@@ -1225,25 +1240,26 @@ void MainWindow::dropEvent(QDropEvent *event)
// remove scheme
QStringList files;
if (event->mimeData()->hasUrls()) {
const QList<QUrl> urls = event->mimeData()->urls();
foreach (const QUrl &url, urls) {
if (!url.isEmpty()) {
if (url.scheme().compare("file", Qt::CaseInsensitive) == 0)
files << url.toLocalFile();
else
files << url.toString();
}
foreach (const QUrl &url, event->mimeData()->urls()) {
if (url.isEmpty())
continue;
files << ((url.scheme().compare("file", Qt::CaseInsensitive) == 0)
? url.toLocalFile()
: url.toString());
}
}
else {
files = event->mimeData()->text().split('\n');
}
// differentiate ".torrent" files and others
// differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles, otherFiles;
foreach (const QString &file, files) {
if (file.startsWith("magnet:", Qt::CaseInsensitive)
|| file.endsWith(C_TORRENT_FILE_EXTENSION, Qt::CaseInsensitive))
const bool isTorrentLink = (file.startsWith("magnet:", Qt::CaseInsensitive)
|| file.endsWith(C_TORRENT_FILE_EXTENSION, Qt::CaseInsensitive)
|| Utils::Misc::isUrl(file));
if (isTorrentLink)
torrentFiles << file;
else
otherFiles << file;
@@ -1297,7 +1313,7 @@ static bool dockClickHandler(id self, SEL cmd, ...)
void MainWindow::setupDockClickHandler()
{
dockMainWindowHandle = this;
overrideDockClickHandler(dockClickHandler);
MacUtils::overrideDockClickHandler(dockClickHandler);
}
#endif
@@ -1556,7 +1572,7 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const
if (!reply.isError())
return;
#elif defined(Q_OS_MAC)
displayNotification(title, msg);
MacUtils::displayNotification(title, msg);
#else
if (m_systrayIcon && QSystemTrayIcon::supportsMessages())
m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, TIME_TRAY_BALLOON);
@@ -1838,7 +1854,7 @@ void MainWindow::toggleAlternativeSpeeds()
void MainWindow::on_actionDonateMoney_triggered()
{
QDesktopServices::openUrl(QUrl("http://www.qbittorrent.org/donate"));
QDesktopServices::openUrl(QUrl("https://www.qbittorrent.org/donate"));
}
void MainWindow::showConnectionSettings()

View File

@@ -187,7 +187,9 @@ private slots:
void toolbarTextBeside();
void toolbarTextUnder();
void toolbarFollowSystem();
#ifndef Q_OS_MAC
#ifdef Q_OS_MAC
void on_actionCloseWindow_triggered();
#else
void toggleVisibility(const QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Trigger);
void createSystrayDelayed();
void updateTrayIconMenu();

View File

@@ -90,6 +90,7 @@
</property>
<addaction name="actionOpen"/>
<addaction name="actionDownloadFromURL"/>
<addaction name="actionCloseWindow"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
@@ -466,6 +467,11 @@
<string>Critical Messages</string>
</property>
</action>
<action name="actionCloseWindow">
<property name="text">
<string>Close Window</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@@ -68,6 +68,7 @@
#include "ipsubnetwhitelistoptionsdialog.h"
#include "guiiconprovider.h"
#include "scanfoldersdelegate.h"
#include "utils.h"
#include "ui_optionsdlg.h"
@@ -99,13 +100,21 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true);
#endif
m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(GuiIconProvider::instance()->getIcon("preferences-other"));
// set uniform size for all icons
int maxHeight = -1;
for (int i = 0; i < m_ui->tabSelection->count(); ++i)
maxHeight = std::max(maxHeight, m_ui->tabSelection->visualItemRect(m_ui->tabSelection->item(i)).size().height());
for (int i = 0; i < m_ui->tabSelection->count(); ++i) {
// uniform size for all icons
m_ui->tabSelection->item(i)->setSizeHint(QSize(std::numeric_limits<int>::max(), 62));
const QSize size(std::numeric_limits<int>::max(), static_cast<int>(maxHeight * 1.2));
m_ui->tabSelection->item(i)->setSizeHint(size);
}
m_ui->IpFilterRefreshBtn->setIcon(GuiIconProvider::instance()->getIcon("view-refresh"));
m_ui->labelGlobalRate->setPixmap(Utils::Gui::scaledPixmap(":/icons/slow_off.png", this, 16));
m_ui->labelAltRate->setPixmap(Utils::Gui::scaledPixmap(":/icons/slow.png", this, 16));
m_ui->deleteTorrentWarningIcon->setPixmap(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(16, 16));
m_ui->deleteTorrentWarningIcon->hide();
m_ui->deleteTorrentWarningLabel->hide();
@@ -139,7 +148,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(ScanFoldersModel::instance(), &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleScanFolderViewSelectionChanged);
connect(m_ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(applySettings(QAbstractButton*)));
connect(m_ui->buttonBox, &QDialogButtonBox::clicked, this, &OptionsDialog::applySettings);
// Languages supported
initializeLanguageCombo();
@@ -445,25 +454,19 @@ void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previo
void OptionsDialog::loadWindowState()
{
const Preferences* const pref = Preferences::instance();
resize(pref->getPrefSize(this->size()));
Utils::Gui::resize(this, Preferences::instance()->getPrefSize());
}
void OptionsDialog::loadSplitterState()
{
const Preferences* const pref = Preferences::instance();
const QStringList sizesStr = Preferences::instance()->getPrefHSplitterSizes();
const QStringList sizes_str = pref->getPrefHSplitterSizes();
QList<int> sizes;
if (sizes_str.size() == 2) {
sizes << sizes_str.first().toInt();
sizes << sizes_str.last().toInt();
}
else {
sizes << 116;
sizes << m_ui->hsplitter->width() - 116;
}
// width has been modified, use height as width reference instead
const int width = Utils::Gui::scaledSize(this
, (m_ui->tabSelection->item(TAB_UI)->sizeHint().height() * 2));
QList<int> sizes {width, (m_ui->hsplitter->width() - width)};
if (sizesStr.size() == 2)
sizes = {sizesStr.first().toInt(), sizesStr.last().toInt()};
m_ui->hsplitter->setSizes(sizes);
}
@@ -534,7 +537,7 @@ void OptionsDialog::saveOptions()
Application * const app = static_cast<Application*>(QCoreApplication::instance());
app->setFileLoggerPath(m_ui->textFileLogPath->selectedPath());
app->setFileLoggerBackup(m_ui->checkFileLogBackup->isChecked());
app->setFileLoggerMaxSize(m_ui->spinFileLogSize->value());
app->setFileLoggerMaxSize(m_ui->spinFileLogSize->value() * 1024);
app->setFileLoggerAge(m_ui->spinFileLogAge->value());
app->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex());
app->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked());
@@ -762,7 +765,7 @@ void OptionsDialog::loadOptions()
m_ui->checkFileLogDelete->setChecked(fileLogDelete);
m_ui->spinFileLogAge->setEnabled(fileLogDelete);
m_ui->comboFileLogAgeType->setEnabled(fileLogDelete);
m_ui->spinFileLogSize->setValue(app->fileLoggerMaxSize());
m_ui->spinFileLogSize->setValue(app->fileLoggerMaxSize() / 1024);
m_ui->spinFileLogAge->setValue(app->fileLoggerAge());
m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType());
// End General preferences
@@ -1578,7 +1581,7 @@ void OptionsDialog::on_IpFilterRefreshBtn_clicked()
session->setIPFilteringEnabled(true);
session->setIPFilterFile(""); // forcing Session reload filter file
session->setIPFilterFile(getFilter());
connect(session, SIGNAL(IPFilterParsed(bool, int)), SLOT(handleIPFilterParsed(bool, int)));
connect(session, &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
setCursor(QCursor(Qt::WaitCursor));
}
@@ -1590,7 +1593,7 @@ void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
else
QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
m_refreshingIpFilter = false;
disconnect(BitTorrent::Session::instance(), SIGNAL(IPFilterParsed(bool, int)), this, SLOT(handleIPFilterParsed(bool, int)));
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
}
QString OptionsDialog::languageToLocalizedString(const QLocale &locale)
@@ -1675,11 +1678,11 @@ bool OptionsDialog::setSslKey(const QByteArray &key)
// try different formats
const bool isKeyValid = (!QSslKey(key, QSsl::Rsa).isNull() || !QSslKey(key, QSsl::Ec).isNull());
if (isKeyValid) {
m_ui->lblSslKeyStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-high.png").scaledToHeight(20, Qt::SmoothTransformation));
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmap(":/icons/qbt-theme/security-high.png", this, 24));
m_sslKey = key;
}
else {
m_ui->lblSslKeyStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-low.png").scaledToHeight(20, Qt::SmoothTransformation));
m_ui->lblSslKeyStatus->setPixmap(Utils::Gui::scaledPixmap(":/icons/qbt-theme/security-low.png", this, 24));
m_sslKey.clear();
}
return isKeyValid;
@@ -1694,11 +1697,11 @@ bool OptionsDialog::setSslCertificate(const QByteArray &cert)
#ifndef QT_NO_OPENSSL
const bool isCertValid = !QSslCertificate(cert).isNull();
if (isCertValid) {
m_ui->lblSslCertStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-high.png").scaledToHeight(20, Qt::SmoothTransformation));
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmap(":/icons/qbt-theme/security-high.png", this, 24));
m_sslCert = cert;
}
else {
m_ui->lblSslCertStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-low.png").scaledToHeight(20, Qt::SmoothTransformation));
m_ui->lblSslCertStatus->setPixmap(Utils::Gui::scaledPixmap(":/icons/qbt-theme/security-low.png", this, 24));
m_sslCert.clear();
}
return isCertValid;

View File

@@ -535,16 +535,16 @@
<item>
<widget class="QSpinBox" name="spinFileLogSize">
<property name="suffix">
<string> MB</string>
<string> KiB</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
<number>1024000</number>
</property>
<property name="value">
<number>10</number>
<number>65</number>
</property>
</widget>
</item>
@@ -1823,11 +1823,7 @@
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_5">
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/slow_off.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="labelGlobalRate"/>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkDownloadLimit">
@@ -1970,11 +1966,7 @@
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_16">
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/slow.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="labelAltRate"/>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkUploadLimitAlt">
@@ -2853,20 +2845,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<widget class="QLabel" name="lblSslCertStatus">
<property name="minimumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
<string notr="true"/>
</property>
</widget>
</item>
@@ -2906,20 +2886,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblSslKeyStatus">
<property name="minimumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
<string notr="true"/>
</property>
</widget>
</item>

View File

@@ -39,6 +39,7 @@
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "previewlistdelegate.h"
#include "utils.h"
#define SETTINGS_KEY(name) "PreviewSelectDialog/" name
@@ -146,7 +147,7 @@ void PreviewSelectDialog::previewButtonClicked()
void PreviewSelectDialog::saveWindowState()
{
// Persist dialog size
m_storeDialogSize = this->size();
m_storeDialogSize = size();
// Persist TreeView Header state
m_storeTreeHeaderState = previewList->header()->saveState();
}
@@ -154,9 +155,8 @@ void PreviewSelectDialog::saveWindowState()
void PreviewSelectDialog::loadWindowState()
{
// Restore dialog size
if (m_storeDialogSize.value().isValid()) {
resize(m_storeDialogSize);
}
Utils::Gui::resize(this, m_storeDialogSize);
// Restore TreeView Header state
if (!m_storeTreeHeaderState.value().isEmpty()) {
m_headerStateInitialized = previewList->header()->restoreState(m_storeTreeHeaderState);

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "downloadedpiecesbar.h"
@@ -127,24 +125,24 @@ bool DownloadedPiecesBar::updateImage(QImage &image)
return true;
}
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
QVector<float> scaledPieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaledPiecesDl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
if (pieces2_val_dl != 0) {
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
for (int x = 0; x < scaledPieces.size(); ++x) {
float piecesToValue = scaledPieces.at(x);
float piecesToValueDl = scaledPiecesDl.at(x);
if (piecesToValueDl != 0) {
float fillRatio = piecesToValue + piecesToValueDl;
float ratio = piecesToValueDl / fillRatio;
QRgb mixedColor = mixTwoColors(pieceColor().rgb(), m_dlPieceColor.rgb(), ratio);
mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fill_ratio);
mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fillRatio);
image2.setPixel(x, 0, mixedColor);
}
else {
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]);
}
}
image = image2;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,20 +24,18 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef DOWNLOADEDPIECESBAR_H
#define DOWNLOADEDPIECESBAR_H
#include <QWidget>
#include <QBitArray>
#include <QVector>
#include <QWidget>
#include "piecesbar.h"
class DownloadedPiecesBar: public PiecesBar
class DownloadedPiecesBar : public PiecesBar
{
using base = PiecesBar;
Q_OBJECT

View File

@@ -61,7 +61,6 @@ public:
COL_COUNT
};
public:
PeerListDelegate(QObject *parent) : QItemDelegate(parent) {}
~PeerListDelegate() {}

View File

@@ -50,13 +50,15 @@ protected:
switch (sortColumn()) {
case PeerListDelegate::IP:
case PeerListDelegate::CLIENT: {
QString vL = left.data().toString();
QString vR = right.data().toString();
return Utils::String::naturalCompareCaseInsensitive(vL, vR);
}
const QString strL = left.data().toString();
const QString strR = right.data().toString();
const int result = Utils::String::naturalCompare(strL, strR, Qt::CaseInsensitive);
return (result < 0);
}
break;
default:
return QSortFilterProxyModel::lessThan(left, right);
};
return QSortFilterProxyModel::lessThan(left, right);
}
};

View File

@@ -180,10 +180,9 @@ void PeerListWidget::displayToggleColumnsMenu(const QPoint &)
Q_ASSERT(visibleCols > 0);
if (!isColumnHidden(col) && (visibleCols == 1))
return;
qDebug("Toggling column %d visibility", col);
setColumnHidden(col, !isColumnHidden(col));
if (!isColumnHidden(col) && (columnWidth(col) <= 5))
setColumnWidth(col, 100);
resizeColumnToContents(col);
saveSettings();
}
}
@@ -231,8 +230,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
emptyMenu = false;
}
QAction *banAct = 0;
QAction *copyPeerAct = 0;
QAction *banAct = nullptr;
QAction *copyPeerAct = nullptr;
if (!selectionModel()->selectedRows().isEmpty()) {
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy IP:port"));
menu.addSeparator();
@@ -241,7 +240,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
}
if (emptyMenu) return;
QAction *act = menu.exec(QCursor::pos());
if (act == 0) return;
if (!act) return;
if (act == addPeerAct) {
QList<BitTorrent::PeerAddress> peersList = PeersAdditionDlg::askForPeers(this);
int peerCount = 0;
@@ -249,7 +249,7 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
if (torrent->connectPeer(addr)) {
qDebug("Adding peer %s...", qUtf8Printable(addr.ip.toString()));
Logger::instance()->addMessage(tr("Manually adding peer '%1'...").arg(addr.ip.toString()));
peerCount++;
++peerCount;
}
else {
Logger::instance()->addMessage(tr("The peer '%1' could not be added to this torrent.").arg(addr.ip.toString()), Log::WARNING);
@@ -277,8 +277,7 @@ void PeerListWidget::banSelectedPeers()
int ret = QMessageBox::question(this, tr("Ban peer permanently"), tr("Are you sure you want to ban permanently the selected peers?"),
tr("&Yes"), tr("&No"),
QString(), 0, 1);
if (ret)
return;
if (ret) return;
QModelIndexList selectedIndexes = selectionModel()->selectedRows();
foreach (const QModelIndex &index, selectedIndexes) {

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,14 +24,12 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "peersadditiondlg.h"
#include <QMessageBox>
#include <QHostAddress>
#include <QMessageBox>
#include "ui_peersadditiondlg.h"

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef PEERADDITION_H
@@ -42,7 +40,7 @@ namespace Ui
class addPeersDialog;
}
class PeersAdditionDlg: public QDialog
class PeersAdditionDlg : public QDialog
{
Q_OBJECT
@@ -60,7 +58,6 @@ private:
Ui::addPeersDialog *m_ui;
QList<BitTorrent::PeerAddress> m_peersList;
};
#endif // PEERADDITION_H

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "pieceavailabilitybar.h"
@@ -134,12 +132,12 @@ bool PieceAvailabilityBar::updateImage(QImage &image)
return true;
}
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
QVector<float> scaledPieces = intToFloatVector(m_pieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
for (int x = 0; x < scaledPieces.size(); ++x) {
float piecesToValue = scaledPieces.at(x);
image2.setPixel(x, 0, pieceColors()[piecesToValue * 255]);
}
image = image2;
return true;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef PIECEAVAILABILITYBAR_H
@@ -33,7 +31,7 @@
#include "piecesbar.h"
class PieceAvailabilityBar: public PiecesBar
class PieceAvailabilityBar : public PiecesBar
{
using base = PiecesBar;
Q_OBJECT

View File

@@ -41,7 +41,7 @@ namespace BitTorrent
class TorrentHandle;
}
class PiecesBar: public QWidget
class PiecesBar : public QWidget
{
using base = QWidget;
Q_OBJECT

View File

@@ -1,33 +1,36 @@
INCLUDEPATH += $$PWD
FORMS += $$PWD/propertieswidget.ui \
$$PWD/trackersadditiondlg.ui \
$$PWD/peersadditiondlg.ui
FORMS += \
$$PWD/peersadditiondlg.ui \
$$PWD/propertieswidget.ui \
$$PWD/trackersadditiondlg.ui
HEADERS += $$PWD/propertieswidget.h \
$$PWD/peerlistwidget.h \
$$PWD/proplistdelegate.h \
$$PWD/trackerlist.h \
$$PWD/downloadedpiecesbar.h \
$$PWD/peerlistdelegate.h \
$$PWD/peerlistsortmodel.h \
$$PWD/peersadditiondlg.h \
$$PWD/trackersadditiondlg.h \
$$PWD/pieceavailabilitybar.h \
$$PWD/proptabbar.h \
$$PWD/speedwidget.h \
$$PWD/speedplotview.h \
$$PWD/piecesbar.h
HEADERS += \
$$PWD/downloadedpiecesbar.h \
$$PWD/peerlistdelegate.h \
$$PWD/peerlistsortmodel.h \
$$PWD/peerlistwidget.h \
$$PWD/peersadditiondlg.h \
$$PWD/pieceavailabilitybar.h \
$$PWD/piecesbar.h \
$$PWD/propertieswidget.h \
$$PWD/proplistdelegate.h \
$$PWD/proptabbar.h \
$$PWD/speedplotview.h \
$$PWD/speedwidget.h \
$$PWD/trackerlist.h \
$$PWD/trackersadditiondlg.h
SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/proplistdelegate.cpp \
$$PWD/peerlistwidget.cpp \
$$PWD/trackerlist.cpp \
$$PWD/peersadditiondlg.cpp \
$$PWD/downloadedpiecesbar.cpp \
$$PWD/trackersadditiondlg.cpp \
$$PWD/pieceavailabilitybar.cpp \
$$PWD/proptabbar.cpp \
$$PWD/speedwidget.cpp \
$$PWD/speedplotview.cpp \
$$PWD/piecesbar.cpp
SOURCES += \
$$PWD/downloadedpiecesbar.cpp \
$$PWD/peerlistwidget.cpp \
$$PWD/peersadditiondlg.cpp \
$$PWD/pieceavailabilitybar.cpp \
$$PWD/piecesbar.cpp \
$$PWD/propertieswidget.cpp \
$$PWD/proplistdelegate.cpp \
$$PWD/proptabbar.cpp \
$$PWD/speedplotview.cpp \
$$PWD/speedwidget.cpp \
$$PWD/trackerlist.cpp \
$$PWD/trackersadditiondlg.cpp

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "propertieswidget.h"
@@ -64,89 +62,95 @@
#include "torrentcontentmodel.h"
#include "trackerlist.h"
#include "transferlistwidget.h"
#include "utils.h"
#include "ui_propertieswidget.h"
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList)
#ifdef Q_OS_MAC
#include "macutilities.h"
#endif
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *mainWindow, TransferListWidget *transferList)
: QWidget(parent)
, m_ui(new Ui::PropertiesWidget())
, transferList(transferList)
, main_window(main_window)
, m_transferList(transferList)
, m_mainWindow(mainWindow)
, m_torrent(nullptr)
{
m_ui->setupUi(this);
setAutoFillBackground(true);
state = VISIBLE;
m_state = VISIBLE;
// Set Properties list model
PropListModel = new TorrentContentFilterModel();
m_ui->filesList->setModel(PropListModel);
PropDelegate = new PropListDelegate(this);
m_ui->filesList->setItemDelegate(PropDelegate);
m_propListModel = new TorrentContentFilterModel();
m_ui->filesList->setModel(m_propListModel);
m_propListDelegate = new PropListDelegate(this);
m_ui->filesList->setItemDelegate(m_propListDelegate);
m_ui->filesList->setSortingEnabled(true);
// Torrent content filtering
m_contentFilterLine = new LineEdit(this);
m_contentFilterLine->setPlaceholderText(tr("Filter files..."));
m_contentFilterLine->setMaximumSize(300, m_contentFilterLine->size().height());
m_contentFilterLine->setFixedWidth(Utils::Gui::scaledSize(this, 300));
connect(m_contentFilterLine, SIGNAL(textChanged(QString)), this, SLOT(filterText(QString)));
m_ui->contentFilterLayout->insertWidget(3, m_contentFilterLine);
// SIGNAL/SLOTS
connect(m_ui->filesList, SIGNAL(clicked(const QModelIndex&)), m_ui->filesList, SLOT(edit(const QModelIndex&)));
connect(m_ui->selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
connect(m_ui->selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
connect(m_ui->selectAllButton, SIGNAL(clicked()), m_propListModel, SLOT(selectAll()));
connect(m_ui->selectNoneButton, SIGNAL(clicked()), m_propListModel, SLOT(selectNone()));
connect(m_ui->filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
connect(m_ui->filesList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(openDoubleClickedFile(const QModelIndex&)));
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(m_propListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(m_ui->listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&)));
connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle * const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle * const)));
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle *const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle *const)));
connect(m_propListDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(m_ui->stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle * const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle * const)));
connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle * const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle * const)));
connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle *const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle *const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle *const)));
connect(m_ui->filesList->header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveSettings()));
connect(m_ui->filesList->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveSettings()));
connect(m_ui->filesList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSettings()));
// set bar height relative to screen dpi
int barHeight = devicePixelRatio() * 18;
const int barHeight = Utils::Gui::scaledSize(this, 18);
// Downloaded pieces progress bar
m_ui->tempProgressBarArea->setVisible(false);
downloaded_pieces = new DownloadedPiecesBar(this);
m_ui->groupBarLayout->addWidget(downloaded_pieces, 0, 1);
downloaded_pieces->setFixedHeight(barHeight);
downloaded_pieces->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_downloadedPieces = new DownloadedPiecesBar(this);
m_ui->groupBarLayout->addWidget(m_downloadedPieces, 0, 1);
m_downloadedPieces->setFixedHeight(barHeight);
m_downloadedPieces->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Pieces availability bar
m_ui->tempAvailabilityBarArea->setVisible(false);
pieces_availability = new PieceAvailabilityBar(this);
m_ui->groupBarLayout->addWidget(pieces_availability, 1, 1);
pieces_availability->setFixedHeight(barHeight);
pieces_availability->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_piecesAvailability = new PieceAvailabilityBar(this);
m_ui->groupBarLayout->addWidget(m_piecesAvailability, 1, 1);
m_piecesAvailability->setFixedHeight(barHeight);
m_piecesAvailability->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Tracker list
trackerList = new TrackerList(this);
m_trackerList = new TrackerList(this);
m_ui->trackerUpButton->setIcon(GuiIconProvider::instance()->getIcon("go-up"));
m_ui->trackerUpButton->setIconSize(Utils::Misc::smallIconSize());
m_ui->trackerUpButton->setIconSize(Utils::Gui::smallIconSize());
m_ui->trackerDownButton->setIcon(GuiIconProvider::instance()->getIcon("go-down"));
m_ui->trackerDownButton->setIconSize(Utils::Misc::smallIconSize());
connect(m_ui->trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
connect(m_ui->trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
m_ui->horizontalLayout_trackers->insertWidget(0, trackerList);
connect(trackerList->header(), SIGNAL(sectionMoved(int,int,int)), trackerList, SLOT(saveSettings()));
connect(trackerList->header(), SIGNAL(sectionResized(int,int,int)), trackerList, SLOT(saveSettings()));
connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), trackerList, SLOT(saveSettings()));
m_ui->trackerDownButton->setIconSize(Utils::Gui::smallIconSize());
connect(m_ui->trackerUpButton, SIGNAL(clicked()), m_trackerList, SLOT(moveSelectionUp()));
connect(m_ui->trackerDownButton, SIGNAL(clicked()), m_trackerList, SLOT(moveSelectionDown()));
m_ui->horizontalLayout_trackers->insertWidget(0, m_trackerList);
connect(m_trackerList->header(), SIGNAL(sectionMoved(int,int,int)), m_trackerList, SLOT(saveSettings()));
connect(m_trackerList->header(), SIGNAL(sectionResized(int,int,int)), m_trackerList, SLOT(saveSettings()));
connect(m_trackerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), m_trackerList, SLOT(saveSettings()));
// Peers list
peersList = new PeerListWidget(this);
m_ui->peerpage_layout->addWidget(peersList);
connect(peersList->header(), SIGNAL(sectionMoved(int,int,int)), peersList, SLOT(saveSettings()));
connect(peersList->header(), SIGNAL(sectionResized(int,int,int)), peersList, SLOT(saveSettings()));
connect(peersList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), peersList, SLOT(saveSettings()));
m_peerList = new PeerListWidget(this);
m_ui->peerpage_layout->addWidget(m_peerList);
connect(m_peerList->header(), SIGNAL(sectionMoved(int,int,int)), m_peerList, SLOT(saveSettings()));
connect(m_peerList->header(), SIGNAL(sectionResized(int,int,int)), m_peerList, SLOT(saveSettings()));
connect(m_peerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), m_peerList, SLOT(saveSettings()));
// Speed widget
speedWidget = new SpeedWidget(this);
m_ui->speed_layout->addWidget(speedWidget);
m_speedWidget = new SpeedWidget(this);
m_ui->speedLayout->addWidget(m_speedWidget);
// Tab bar
m_tabBar = new PropTabBar();
m_tabBar->setContentsMargins(0, 5, 0, 0);
@@ -156,79 +160,79 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, Tra
connect(m_tabBar, SIGNAL(visibilityToggled(bool)), SLOT(setVisibility(bool)));
connect(m_tabBar, SIGNAL(visibilityToggled(bool)), this, SLOT(saveSettings()));
// Dynamic data refresher
refreshTimer = new QTimer(this);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
refreshTimer->start(3000); // 3sec
editHotkeyFile = new QShortcut(Qt::Key_F2, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(editHotkeyFile, SIGNAL(activated()), SLOT(renameSelectedFile()));
editHotkeyWeb = new QShortcut(Qt::Key_F2, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(editHotkeyWeb, SIGNAL(activated()), SLOT(editWebSeed()));
m_refreshTimer = new QTimer(this);
connect(m_refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
m_refreshTimer->start(3000); // 3sec
m_editHotkeyFile = new QShortcut(Qt::Key_F2, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(m_editHotkeyFile, SIGNAL(activated()), SLOT(renameSelectedFile()));
m_editHotkeyWeb = new QShortcut(Qt::Key_F2, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(m_editHotkeyWeb, SIGNAL(activated()), SLOT(editWebSeed()));
connect(m_ui->listWebSeeds, SIGNAL(doubleClicked(QModelIndex)), SLOT(editWebSeed()));
deleteHotkeyWeb = new QShortcut(QKeySequence::Delete, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds()));
openHotkeyFile = new QShortcut(Qt::Key_Return, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile()));
m_deleteHotkeyWeb = new QShortcut(QKeySequence::Delete, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(m_deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds()));
m_openHotkeyFile = new QShortcut(Qt::Key_Return, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(m_openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile()));
}
PropertiesWidget::~PropertiesWidget()
{
qDebug() << Q_FUNC_INFO << "ENTER";
delete refreshTimer;
delete trackerList;
delete peersList;
delete speedWidget;
delete downloaded_pieces;
delete pieces_availability;
delete PropListModel;
delete PropDelegate;
delete m_refreshTimer;
delete m_trackerList;
delete m_peerList;
delete m_speedWidget;
delete m_downloadedPieces;
delete m_piecesAvailability;
delete m_propListModel;
delete m_propListDelegate;
delete m_tabBar;
delete editHotkeyFile;
delete editHotkeyWeb;
delete deleteHotkeyWeb;
delete openHotkeyFile;
delete m_editHotkeyFile;
delete m_editHotkeyWeb;
delete m_deleteHotkeyWeb;
delete m_openHotkeyFile;
delete m_ui;
qDebug() << Q_FUNC_INFO << "EXIT";
}
void PropertiesWidget::showPiecesAvailability(bool show)
{
m_ui->avail_pieces_lbl->setVisible(show);
pieces_availability->setVisible(show);
m_ui->avail_average_lbl->setVisible(show);
if (show || !downloaded_pieces->isVisible())
m_ui->line_2->setVisible(show);
m_ui->labelPiecesAvailability->setVisible(show);
m_piecesAvailability->setVisible(show);
m_ui->labelAverageAvailabilityVal->setVisible(show);
if (show || !m_downloadedPieces->isVisible())
m_ui->lineBelowBars->setVisible(show);
}
void PropertiesWidget::showPiecesDownloaded(bool show)
{
m_ui->downloaded_pieces_lbl->setVisible(show);
downloaded_pieces->setVisible(show);
m_ui->progress_lbl->setVisible(show);
if (show || !pieces_availability->isVisible())
m_ui->line_2->setVisible(show);
m_ui->labelDownloadedPieces->setVisible(show);
m_downloadedPieces->setVisible(show);
m_ui->labelProgressVal->setVisible(show);
if (show || !m_piecesAvailability->isVisible())
m_ui->lineBelowBars->setVisible(show);
}
void PropertiesWidget::setVisibility(bool visible)
{
if (!visible && (state == VISIBLE)) {
if (!visible && (m_state == VISIBLE)) {
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
m_ui->stackedProperties->setVisible(false);
slideSizes = hSplitter->sizes();
m_slideSizes = hSplitter->sizes();
hSplitter->handle(1)->setVisible(false);
hSplitter->handle(1)->setDisabled(true);
QList<int> sizes = QList<int>() << hSplitter->geometry().height() - 30 << 30;
hSplitter->setSizes(sizes);
state = REDUCED;
m_state = REDUCED;
return;
}
if (visible && (state == REDUCED)) {
if (visible && (m_state == REDUCED)) {
m_ui->stackedProperties->setVisible(true);
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
hSplitter->handle(1)->setDisabled(false);
hSplitter->handle(1)->setVisible(true);
hSplitter->setSizes(slideSizes);
state = VISIBLE;
hSplitter->setSizes(m_slideSizes);
m_state = VISIBLE;
// Force refresh
loadDynamicData();
}
@@ -237,39 +241,39 @@ void PropertiesWidget::setVisibility(bool visible)
void PropertiesWidget::clear()
{
qDebug("Clearing torrent properties");
m_ui->save_path->clear();
m_ui->lbl_creationDate->clear();
m_ui->label_total_pieces_val->clear();
m_ui->hash_lbl->clear();
m_ui->comment_text->clear();
m_ui->progress_lbl->clear();
trackerList->clear();
downloaded_pieces->clear();
pieces_availability->clear();
m_ui->avail_average_lbl->clear();
m_ui->wasted->clear();
m_ui->upTotal->clear();
m_ui->dlTotal->clear();
peersList->clear();
m_ui->lbl_uplimit->clear();
m_ui->lbl_dllimit->clear();
m_ui->lbl_elapsed->clear();
m_ui->lbl_connections->clear();
m_ui->reannounce_lbl->clear();
m_ui->shareRatio->clear();
m_ui->labelSavePathVal->clear();
m_ui->labelCreatedOnVal->clear();
m_ui->labelTotalPiecesVal->clear();
m_ui->labelHashVal->clear();
m_ui->labelCommentVal->clear();
m_ui->labelProgressVal->clear();
m_ui->labelAverageAvailabilityVal->clear();
m_ui->labelWastedVal->clear();
m_ui->labelUpTotalVal->clear();
m_ui->labelDlTotalVal->clear();
m_ui->labelUpLimitVal->clear();
m_ui->labelDlLimitVal->clear();
m_ui->labelElapsedVal->clear();
m_ui->labelConnectionsVal->clear();
m_ui->labelReannounceInVal->clear();
m_ui->labelShareRatioVal->clear();
m_ui->listWebSeeds->clear();
m_ui->labelETAVal->clear();
m_ui->labelSeedsVal->clear();
m_ui->labelPeersVal->clear();
m_ui->labelDlSpeedVal->clear();
m_ui->labelUpSpeedVal->clear();
m_ui->labelTotalSizeVal->clear();
m_ui->labelCompletedOnVal->clear();
m_ui->labelLastSeenCompleteVal->clear();
m_ui->labelCreatedByVal->clear();
m_ui->labelAddedOnVal->clear();
m_trackerList->clear();
m_downloadedPieces->clear();
m_piecesAvailability->clear();
m_peerList->clear();
m_contentFilterLine->clear();
PropListModel->model()->clear();
m_ui->label_eta_val->clear();
m_ui->label_seeds_val->clear();
m_ui->label_peers_val->clear();
m_ui->label_dl_speed_val->clear();
m_ui->label_upload_speed_val->clear();
m_ui->label_total_size_val->clear();
m_ui->label_completed_on_val->clear();
m_ui->label_last_complete_val->clear();
m_ui->label_created_by_val->clear();
m_ui->label_added_on_val->clear();
m_propListModel->model()->clear();
}
BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
@@ -277,26 +281,41 @@ BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
return m_torrent;
}
TrackerList *PropertiesWidget::getTrackerList() const
{
return m_trackerList;
}
PeerListWidget *PropertiesWidget::getPeerList() const
{
return m_peerList;
}
QTreeView *PropertiesWidget::getFilesList() const
{
return m_ui->filesList;
}
SpeedWidget *PropertiesWidget::getSpeedWidget() const
{
return m_speedWidget;
}
void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent)
{
if (m_torrent == torrent)
m_ui->save_path->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
if (torrent == m_torrent)
m_ui->labelSavePathVal->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
}
void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent)
{
if (torrent == m_torrent)
trackerList->loadTrackers();
m_trackerList->loadTrackers();
}
void PropertiesWidget::updateTorrentInfos(BitTorrent::TorrentHandle *const torrent)
{
if (m_torrent == torrent)
if (torrent == m_torrent)
loadTorrentInfos(m_torrent);
}
@@ -304,36 +323,36 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
{
clear();
m_torrent = torrent;
downloaded_pieces->setTorrent(m_torrent);
pieces_availability->setTorrent(m_torrent);
m_downloadedPieces->setTorrent(m_torrent);
m_piecesAvailability->setTorrent(m_torrent);
if (!m_torrent) return;
// Save path
updateSavePath(m_torrent);
// Hash
m_ui->hash_lbl->setText(m_torrent->hash());
PropListModel->model()->clear();
m_ui->labelHashVal->setText(m_torrent->hash());
m_propListModel->model()->clear();
if (m_torrent->hasMetadata()) {
// Creation date
m_ui->lbl_creationDate->setText(m_torrent->creationDate().toString(Qt::DefaultLocaleShortDate));
m_ui->labelCreatedOnVal->setText(m_torrent->creationDate().toString(Qt::DefaultLocaleShortDate));
m_ui->label_total_size_val->setText(Utils::Misc::friendlyUnit(m_torrent->totalSize()));
m_ui->labelTotalSizeVal->setText(Utils::Misc::friendlyUnit(m_torrent->totalSize()));
// Comment
m_ui->comment_text->setText(Utils::Misc::parseHtmlLinks(m_torrent->comment().toHtmlEscaped()));
m_ui->labelCommentVal->setText(Utils::Misc::parseHtmlLinks(m_torrent->comment().toHtmlEscaped()));
// URL seeds
loadUrlSeeds();
m_ui->label_created_by_val->setText(m_torrent->creator().toHtmlEscaped());
m_ui->labelCreatedByVal->setText(m_torrent->creator().toHtmlEscaped());
// List files in torrent
PropListModel->model()->setupModelData(m_torrent->info());
if (PropListModel->model()->rowCount() == 1)
m_ui->filesList->setExpanded(PropListModel->index(0, 0), true);
m_propListModel->model()->setupModelData(m_torrent->info());
if (m_propListModel->model()->rowCount() == 1)
m_ui->filesList->setExpanded(m_propListModel->index(0, 0), true);
// Load file priorities
PropListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
m_propListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
}
// Load dynamic data
loadDynamicData();
@@ -343,18 +362,16 @@ void PropertiesWidget::readSettings()
{
const Preferences *const pref = Preferences::instance();
// Restore splitter sizes
QStringList sizes_str = pref->getPropSplitterSizes().split(",");
if (sizes_str.size() == 2) {
slideSizes << sizes_str.first().toInt();
slideSizes << sizes_str.last().toInt();
QStringList sizesStr = pref->getPropSplitterSizes().split(",");
if (sizesStr.size() == 2) {
m_slideSizes << sizesStr.first().toInt();
m_slideSizes << sizesStr.last().toInt();
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
hSplitter->setSizes(slideSizes);
hSplitter->setSizes(m_slideSizes);
}
const int current_tab = pref->getPropCurTab();
const bool visible = pref->getPropVisible();
// the following will call saveSettings but shouldn't change any state
if (!m_ui->filesList->header()->restoreState(pref->getPropFileListState()))
m_ui->filesList->header()->resizeSection(0, 400); // Default
m_ui->filesList->header()->restoreState(pref->getPropFileListState());
m_tabBar->setCurrentIndex(current_tab);
if (!visible)
setVisibility(false);
@@ -363,14 +380,14 @@ void PropertiesWidget::readSettings()
void PropertiesWidget::saveSettings()
{
Preferences *const pref = Preferences::instance();
pref->setPropVisible(state == VISIBLE);
pref->setPropVisible(m_state == VISIBLE);
// Splitter sizes
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
QList<int> sizes;
if (state == VISIBLE)
if (m_state == VISIBLE)
sizes = hSplitter->sizes();
else
sizes = slideSizes;
sizes = m_slideSizes;
qDebug("Sizes: %d", sizes.size());
if (sizes.size() == 2)
pref->setPropSplitterSizes(QString::number(sizes.first()) + ',' + QString::number(sizes.last()));
@@ -382,118 +399,112 @@ void PropertiesWidget::saveSettings()
void PropertiesWidget::reloadPreferences()
{
// Take program preferences into consideration
peersList->updatePeerHostNameResolutionState();
peersList->updatePeerCountryResolutionState();
m_peerList->updatePeerHostNameResolutionState();
m_peerList->updatePeerCountryResolutionState();
}
void PropertiesWidget::loadDynamicData()
{
// Refresh only if the torrent handle is valid and if visible
if (!m_torrent || (main_window->currentTabWidget() != transferList) || (state != VISIBLE)) return;
// Refresh only if the torrent handle is valid and visible
if (!m_torrent || (m_mainWindow->currentTabWidget() != m_transferList) || (m_state != VISIBLE)) return;
// Transfer infos
switch (m_ui->stackedProperties->currentIndex()) {
case PropTabBar::MAIN_TAB: {
m_ui->wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
case PropTabBar::MainTab: {
m_ui->labelWastedVal->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
m_ui->upTotal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload()))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalPayloadUpload())));
m_ui->labelUpTotalVal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload()))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalPayloadUpload())));
m_ui->dlTotal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalDownload()))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalPayloadDownload())));
m_ui->labelDlTotalVal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalDownload()))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalPayloadDownload())));
m_ui->lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->uploadLimit(), true));
m_ui->labelUpLimitVal->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->uploadLimit(), true));
m_ui->lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->downloadLimit(), true));
m_ui->labelDlLimitVal->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->downloadLimit(), true));
QString elapsed_txt;
if (m_torrent->isSeed())
elapsed_txt = tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
.arg(Utils::Misc::userFriendlyDuration(m_torrent->activeTime()))
.arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime()));
else
elapsed_txt = Utils::Misc::userFriendlyDuration(m_torrent->activeTime());
m_ui->lbl_elapsed->setText(elapsed_txt);
QString elapsedString;
if (m_torrent->isSeed())
elapsedString = tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
.arg(Utils::Misc::userFriendlyDuration(m_torrent->activeTime()))
.arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime()));
else
elapsedString = Utils::Misc::userFriendlyDuration(m_torrent->activeTime());
m_ui->labelElapsedVal->setText(elapsedString);
m_ui->lbl_connections->setText(tr("%1 (%2 max)", "%1 and %2 are numbers, e.g. 3 (10 max)")
.arg(m_torrent->connectionsCount())
.arg(m_torrent->connectionsLimit() < 0 ? QString::fromUtf8(C_INFINITY) : QString::number(m_torrent->connectionsLimit())));
m_ui->labelConnectionsVal->setText(tr("%1 (%2 max)", "%1 and %2 are numbers, e.g. 3 (10 max)")
.arg(m_torrent->connectionsCount())
.arg(m_torrent->connectionsLimit() < 0 ? QString::fromUtf8(C_INFINITY) : QString::number(m_torrent->connectionsLimit())));
m_ui->label_eta_val->setText(Utils::Misc::userFriendlyDuration(m_torrent->eta()));
m_ui->labelETAVal->setText(Utils::Misc::userFriendlyDuration(m_torrent->eta()));
// Update next announce time
m_ui->reannounce_lbl->setText(Utils::Misc::userFriendlyDuration(m_torrent->nextAnnounce()));
// Update next announce time
m_ui->labelReannounceInVal->setText(Utils::Misc::userFriendlyDuration(m_torrent->nextAnnounce()));
// Update ratio info
const qreal ratio = m_torrent->realRatio();
m_ui->shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8(C_INFINITY) : Utils::String::fromDouble(ratio, 2));
// Update ratio info
const qreal ratio = m_torrent->realRatio();
m_ui->labelShareRatioVal->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8(C_INFINITY) : Utils::String::fromDouble(ratio, 2));
m_ui->label_seeds_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->seedsCount()))
.arg(QString::number(m_torrent->totalSeedsCount())));
m_ui->labelSeedsVal->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->seedsCount()))
.arg(QString::number(m_torrent->totalSeedsCount())));
m_ui->label_peers_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->leechsCount()))
.arg(QString::number(m_torrent->totalLeechersCount())));
m_ui->labelPeersVal->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->leechsCount()))
.arg(QString::number(m_torrent->totalLeechersCount())));
m_ui->label_dl_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->downloadPayloadRate(), true))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalDownload() / (1 + m_torrent->activeTime() - m_torrent->finishedTime()), true)));
m_ui->labelDlSpeedVal->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->downloadPayloadRate(), true))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalDownload() / (1 + m_torrent->activeTime() - m_torrent->finishedTime()), true)));
m_ui->label_upload_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->uploadPayloadRate(), true))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload() / (1 + m_torrent->activeTime()), true)));
m_ui->labelUpSpeedVal->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->uploadPayloadRate(), true))
.arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload() / (1 + m_torrent->activeTime()), true)));
m_ui->label_last_complete_val->setText(m_torrent->lastSeenComplete().isValid() ? m_torrent->lastSeenComplete().toString(Qt::DefaultLocaleShortDate) : tr("Never"));
m_ui->labelLastSeenCompleteVal->setText(m_torrent->lastSeenComplete().isValid() ? m_torrent->lastSeenComplete().toString(Qt::DefaultLocaleShortDate) : tr("Never"));
m_ui->label_completed_on_val->setText(m_torrent->completedTime().isValid() ? m_torrent->completedTime().toString(Qt::DefaultLocaleShortDate) : "");
m_ui->labelCompletedOnVal->setText(m_torrent->completedTime().isValid() ? m_torrent->completedTime().toString(Qt::DefaultLocaleShortDate) : "");
m_ui->label_added_on_val->setText(m_torrent->addedTime().toString(Qt::DefaultLocaleShortDate));
m_ui->labelAddedOnVal->setText(m_torrent->addedTime().toString(Qt::DefaultLocaleShortDate));
if (m_torrent->hasMetadata()) {
m_ui->label_total_pieces_val->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave()));
if (m_torrent->hasMetadata()) {
m_ui->labelTotalPiecesVal->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave()));
if (!m_torrent->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) {
// Pieces availability
showPiecesAvailability(true);
pieces_availability->setAvailability(m_torrent->pieceAvailability());
m_ui->avail_average_lbl->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
if (!m_torrent->isSeed() && !m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) {
// Pieces availability
showPiecesAvailability(true);
m_piecesAvailability->setAvailability(m_torrent->pieceAvailability());
m_ui->labelAverageAvailabilityVal->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
}
else {
showPiecesAvailability(false);
}
// Progress
qreal progress = m_torrent->progress() * 100.;
m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + "%");
m_downloadedPieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
}
else {
showPiecesAvailability(false);
}
// Progress
qreal progress = m_torrent->progress() * 100.;
m_ui->progress_lbl->setText(Utils::String::fromDouble(progress, 1) + "%");
downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
}
else {
showPiecesAvailability(false);
}
break;
}
case PropTabBar::TRACKERS_TAB: {
case PropTabBar::TrackersTab:
// Trackers
trackerList->loadTrackers();
m_trackerList->loadTrackers();
break;
}
case PropTabBar::PEERS_TAB: {
case PropTabBar::PeersTab:
// Load peers
peersList->loadPeers(m_torrent);
m_peerList->loadPeers(m_torrent);
break;
}
case PropTabBar::FILES_TAB: {
case PropTabBar::FilesTab:
// Files progress
if (m_torrent->hasMetadata()) {
qDebug("Updating priorities in files tab");
m_ui->filesList->setUpdatesEnabled(false);
PropListModel->model()->updateFilesProgress(m_torrent->filesProgress());
PropListModel->model()->updateFilesAvailability(m_torrent->availableFileFractions());
m_propListModel->model()->updateFilesProgress(m_torrent->filesProgress());
m_propListModel->model()->updateFilesAvailability(m_torrent->availableFileFractions());
// XXX: We don't update file priorities regularly for performance
// reasons. This means that priorities will not be updated if
// set from the Web UI.
@@ -501,8 +512,6 @@ void PropertiesWidget::loadDynamicData()
m_ui->filesList->setUpdatesEnabled(true);
}
break;
}
default:;
}
}
@@ -511,19 +520,19 @@ void PropertiesWidget::loadUrlSeeds()
{
m_ui->listWebSeeds->clear();
qDebug("Loading URL seeds");
const QList<QUrl> hc_seeds = m_torrent->urlSeeds();
const QList<QUrl> hcSeeds = m_torrent->urlSeeds();
// Add url seeds
foreach (const QUrl &hc_seed, hc_seeds) {
qDebug("Loading URL seed: %s", qUtf8Printable(hc_seed.toString()));
new QListWidgetItem(hc_seed.toString(), m_ui->listWebSeeds);
foreach (const QUrl &hcSeed, hcSeeds) {
qDebug("Loading URL seed: %s", qUtf8Printable(hcSeed.toString()));
new QListWidgetItem(hcSeed.toString(), m_ui->listWebSeeds);
}
}
void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index)
{
if (!index.isValid()) return;
if (!m_torrent || !m_torrent->hasMetadata()) return;
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType)
if (!index.isValid() || !m_torrent || !m_torrent->hasMetadata()) return;
if (m_propListModel->itemType(index) == TorrentContentModelItem::FileType)
openFile(index);
else
openFolder(index, false);
@@ -531,48 +540,52 @@ void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index)
void PropertiesWidget::openFile(const QModelIndex &index)
{
int i = PropListModel->getFileIndex(index);
int i = m_propListModel->getFileIndex(index);
const QDir saveDir(m_torrent->savePath(true));
const QString filename = m_torrent->filePath(i);
const QString file_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(filename));
qDebug("Trying to open file at %s", qUtf8Printable(file_path));
const QString filePath = Utils::Fs::expandPath(saveDir.absoluteFilePath(filename));
qDebug("Trying to open file at %s", qUtf8Printable(filePath));
// Flush data
m_torrent->flushCache();
Utils::Misc::openPath(file_path);
Utils::Misc::openPath(filePath);
}
void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_folder)
void PropertiesWidget::openFolder(const QModelIndex &index, bool containingFolder)
{
QString absolute_path;
QString absolutePath;
// FOLDER
if (PropListModel->itemType(index) == TorrentContentModelItem::FolderType) {
if (m_propListModel->itemType(index) == TorrentContentModelItem::FolderType) {
// Generate relative path to selected folder
QStringList path_items;
path_items << index.data().toString();
QModelIndex parent = PropListModel->parent(index);
QStringList pathItems;
pathItems << index.data().toString();
QModelIndex parent = m_propListModel->parent(index);
while (parent.isValid()) {
path_items.prepend(parent.data().toString());
parent = PropListModel->parent(parent);
pathItems.prepend(parent.data().toString());
parent = m_propListModel->parent(parent);
}
if (path_items.isEmpty())
if (pathItems.isEmpty())
return;
const QDir saveDir(m_torrent->savePath(true));
const QString relative_path = path_items.join("/");
absolute_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(relative_path));
const QString relativePath = pathItems.join("/");
absolutePath = Utils::Fs::expandPath(saveDir.absoluteFilePath(relativePath));
}
else {
int i = PropListModel->getFileIndex(index);
int i = m_propListModel->getFileIndex(index);
const QDir saveDir(m_torrent->savePath(true));
const QString relative_path = m_torrent->filePath(i);
absolute_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(relative_path));
const QString relativePath = m_torrent->filePath(i);
absolutePath = Utils::Fs::expandPath(saveDir.absoluteFilePath(relativePath));
}
// Flush data
m_torrent->flushCache();
if (containing_folder)
Utils::Misc::openFolderSelect(absolute_path);
#ifdef Q_OS_MAC
MacUtils::openFiles(QSet<QString>{absolutePath});
#else
if (containingFolder)
Utils::Misc::openFolderSelect(absolutePath);
else
Utils::Misc::openPath(absolute_path);
Utils::Misc::openPath(absolutePath);
#endif
}
void PropertiesWidget::displayFilesListMenu(const QPoint &)
@@ -580,8 +593,8 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
if (!m_torrent) return;
QModelIndexList selectedRows = m_ui->filesList->selectionModel()->selectedRows(0);
if (selectedRows.empty())
return;
if (selectedRows.empty()) return;
QMenu myFilesLlistMenu;
QAction *actOpen = nullptr;
QAction *actOpenContainingFolder = nullptr;
@@ -606,35 +619,33 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
// The selected torrent might have disappeared during exec()
// from the current view thus leaving invalid indices.
const QModelIndex index = *(selectedRows.begin());
if (!index.isValid())
return;
if (act) {
if (act == actOpen) {
openDoubleClickedFile(index);
}
else if (act == actOpenContainingFolder) {
openFolder(index, true);
}
else if (act == actRename) {
renameSelectedFile();
}
else {
int prio = prio::NORMAL;
if (act == m_ui->actionHigh)
prio = prio::HIGH;
else if (act == m_ui->actionMaximum)
prio = prio::MAXIMUM;
else if (act == m_ui->actionNot_downloaded)
prio = prio::IGNORED;
if (!index.isValid() || !act) return;
qDebug("Setting files priority");
foreach (QModelIndex index, selectedRows) {
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
PropListModel->setData(PropListModel->index(index.row(), PRIORITY, index.parent()), prio);
}
// Save changes
filteredFilesChanged();
if (act == actOpen) {
openDoubleClickedFile(index);
}
else if (act == actOpenContainingFolder) {
openFolder(index, true);
}
else if (act == actRename) {
renameSelectedFile();
}
else {
int prio = prio::NORMAL;
if (act == m_ui->actionHigh)
prio = prio::HIGH;
else if (act == m_ui->actionMaximum)
prio = prio::MAXIMUM;
else if (act == m_ui->actionNot_downloaded)
prio = prio::IGNORED;
qDebug("Setting files priority");
foreach (QModelIndex index, selectedRows) {
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
m_propListModel->setData(m_propListModel->index(index.row(), PRIORITY, index.parent()), prio);
}
// Save changes
filteredFilesChanged();
}
}
@@ -657,16 +668,16 @@ void PropertiesWidget::displayWebSeedListMenu(const QPoint &)
}
const QAction *act = seedMenu.exec(QCursor::pos());
if (act) {
if (act == actAdd)
askWebSeed();
else if (act == actDel)
deleteSelectedUrlSeeds();
else if (act == actCpy)
copySelectedWebSeedsToClipboard();
else if (act == actEdit)
editWebSeed();
}
if (!act) return;
if (act == actAdd)
askWebSeed();
else if (act == actDel)
deleteSelectedUrlSeeds();
else if (act == actCpy)
copySelectedWebSeedsToClipboard();
else if (act == actEdit)
editWebSeed();
}
void PropertiesWidget::renameSelectedFile()
@@ -692,9 +703,9 @@ void PropertiesWidget::renameSelectedFile()
return;
}
if (PropListModel->itemType(modelIndex) == TorrentContentModelItem::FileType) {
if (m_propListModel->itemType(modelIndex) == TorrentContentModelItem::FileType) {
// renaming a file
const int fileIndex = PropListModel->getFileIndex(modelIndex);
const int fileIndex = m_propListModel->getFileIndex(modelIndex);
if (newName.endsWith(QB_EXT))
newName.chop(QB_EXT.size());
@@ -722,16 +733,16 @@ void PropertiesWidget::renameSelectedFile()
qDebug("Renaming %s to %s", qUtf8Printable(oldFilePath), qUtf8Printable(newFilePath));
m_torrent->renameFile(fileIndex, newFilePath);
PropListModel->setData(modelIndex, newName);
m_propListModel->setData(modelIndex, newName);
}
else {
// renaming a folder
QStringList pathItems;
pathItems << modelIndex.data().toString();
QModelIndex parent = PropListModel->parent(modelIndex);
QModelIndex parent = m_propListModel->parent(modelIndex);
while (parent.isValid()) {
pathItems.prepend(parent.data().toString());
parent = PropListModel->parent(parent);
parent = m_propListModel->parent(parent);
}
const QString oldPath = pathItems.join("/");
pathItems.removeLast();
@@ -773,7 +784,7 @@ void PropertiesWidget::renameSelectedFile()
// Force recheck
if (forceRecheck) m_torrent->forceRecheck();
// Rename folder in torrent files model too
PropListModel->setData(modelIndex, newName);
m_propListModel->setData(modelIndex, newName);
// Remove old folder
const QDir oldFolder(m_torrent->savePath(true) + "/" + oldPath);
int timeout = 10;
@@ -830,52 +841,48 @@ void PropertiesWidget::deleteSelectedUrlSeeds()
void PropertiesWidget::copySelectedWebSeedsToClipboard() const
{
const QList<QListWidgetItem *> selected_items = m_ui->listWebSeeds->selectedItems();
if (selected_items.isEmpty())
return;
const QList<QListWidgetItem *> selectedItems = m_ui->listWebSeeds->selectedItems();
if (selectedItems.isEmpty()) return;
QStringList urls_to_copy;
foreach (QListWidgetItem *item, selected_items)
urls_to_copy << item->text();
QStringList urlsToCopy;
foreach (QListWidgetItem *item, selectedItems)
urlsToCopy << item->text();
QApplication::clipboard()->setText(urls_to_copy.join("\n"));
QApplication::clipboard()->setText(urlsToCopy.join("\n"));
}
void PropertiesWidget::editWebSeed()
{
const QList<QListWidgetItem *> selected_items = m_ui->listWebSeeds->selectedItems();
if (selected_items.size() != 1)
return;
const QList<QListWidgetItem *> selectedItems = m_ui->listWebSeeds->selectedItems();
if (selectedItems.size() != 1) return;
const QListWidgetItem *selected_item = selected_items.last();
const QString old_seed = selected_item->text();
const QListWidgetItem *selectedItem = selectedItems.last();
const QString oldSeed = selectedItem->text();
bool result;
const QString new_seed = AutoExpandableDialog::getText(this, tr("Web seed editing"),
const QString newSeed = AutoExpandableDialog::getText(this, tr("Web seed editing"),
tr("Web seed URL:"), QLineEdit::Normal,
old_seed, &result);
if (!result)
return;
oldSeed, &result);
if (!result) return;
if (!m_ui->listWebSeeds->findItems(new_seed, Qt::MatchFixedString).empty()) {
if (!m_ui->listWebSeeds->findItems(newSeed, Qt::MatchFixedString).empty()) {
QMessageBox::warning(this, tr("qBittorrent"),
tr("This URL seed is already in the list."),
QMessageBox::Ok);
return;
}
m_torrent->removeUrlSeeds(QList<QUrl>() << old_seed);
m_torrent->addUrlSeeds(QList<QUrl>() << new_seed);
m_torrent->removeUrlSeeds(QList<QUrl>() << oldSeed);
m_torrent->addUrlSeeds(QList<QUrl>() << newSeed);
loadUrlSeeds();
}
bool PropertiesWidget::applyPriorities()
void PropertiesWidget::applyPriorities()
{
qDebug("Saving files priorities");
const QVector<int> priorities = PropListModel->model()->getFilePriorities();
const QVector<int> priorities = m_propListModel->model()->getFilePriorities();
// Prioritize the files
qDebug("prioritize files: %d", priorities[0]);
m_torrent->prioritizeFiles(priorities);
return true;
}
void PropertiesWidget::filteredFilesChanged()
@@ -886,10 +893,10 @@ void PropertiesWidget::filteredFilesChanged()
void PropertiesWidget::filterText(const QString &filter)
{
PropListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
m_propListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
if (filter.isEmpty()) {
m_ui->filesList->collapseAll();
m_ui->filesList->expand(PropListModel->index(0, 0));
m_ui->filesList->expand(m_propListModel->index(0, 0));
}
else {
m_ui->filesList->expandAll();

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef PROPERTIESWIDGET_H
@@ -36,32 +34,30 @@
#include "base/bittorrent/torrenthandle.h"
class TransferListWidget;
class TorrentContentFilterModel;
class PropListDelegate;
class torrent_file;
class PeerListWidget;
class TrackerList;
class SpeedWidget;
class MainWindow;
class DownloadedPiecesBar;
class PieceAvailabilityBar;
class PropTabBar;
class LineEdit;
QT_BEGIN_NAMESPACE
class QAction;
class QPushButton;
class QTimer;
class QTreeView;
QT_END_NAMESPACE
class DownloadedPiecesBar;
class LineEdit;
class MainWindow;
class PeerListWidget;
class PieceAvailabilityBar;
class PropListDelegate;
class PropTabBar;
class SpeedWidget;
class torrent_file;
class TorrentContentFilterModel;
class TrackerList;
class TransferListWidget;
namespace Ui
{
class PropertiesWidget;
}
class PropertiesWidget: public QWidget
class PropertiesWidget : public QWidget
{
Q_OBJECT
Q_DISABLE_COPY(PropertiesWidget)
@@ -73,13 +69,13 @@ public:
VISIBLE
};
PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList);
PropertiesWidget(QWidget *parent, MainWindow *mainWindow, TransferListWidget *transferList);
~PropertiesWidget();
BitTorrent::TorrentHandle *getCurrentTorrent() const;
TrackerList *getTrackerList() const { return trackerList; }
PeerListWidget *getPeerList() const { return peersList; }
TrackerList *getTrackerList() const;
PeerListWidget *getPeerList() const;
QTreeView *getFilesList() const;
SpeedWidget *getSpeedWidget() const { return speedWidget; }
SpeedWidget *getSpeedWidget() const;
public slots:
void setVisibility(bool visible);
@@ -93,7 +89,7 @@ public slots:
protected:
QPushButton *getButtonFromIndex(int index);
bool applyPriorities();
void applyPriorities();
protected slots:
void loadTorrentInfos(BitTorrent::TorrentHandle *const torrent);
@@ -113,28 +109,28 @@ protected slots:
private:
void openFile(const QModelIndex &index);
void openFolder(const QModelIndex &index, bool containing_folder);
void openFolder(const QModelIndex &index, bool containingFolder);
Ui::PropertiesWidget *m_ui;
TransferListWidget *transferList;
MainWindow *main_window;
TransferListWidget *m_transferList;
MainWindow *m_mainWindow;
BitTorrent::TorrentHandle *m_torrent;
QTimer *refreshTimer;
SlideState state;
TorrentContentFilterModel *PropListModel;
PropListDelegate *PropDelegate;
PeerListWidget *peersList;
TrackerList *trackerList;
SpeedWidget *speedWidget;
QList<int> slideSizes;
DownloadedPiecesBar *downloaded_pieces;
PieceAvailabilityBar *pieces_availability;
QTimer *m_refreshTimer;
SlideState m_state;
TorrentContentFilterModel *m_propListModel;
PropListDelegate *m_propListDelegate;
PeerListWidget *m_peerList;
TrackerList *m_trackerList;
SpeedWidget *m_speedWidget;
QList<int> m_slideSizes;
DownloadedPiecesBar *m_downloadedPieces;
PieceAvailabilityBar *m_piecesAvailability;
PropTabBar *m_tabBar;
LineEdit *m_contentFilterLine;
QShortcut *editHotkeyFile;
QShortcut *editHotkeyWeb;
QShortcut *deleteHotkeyWeb;
QShortcut *openHotkeyFile;
QShortcut *m_editHotkeyFile;
QShortcut *m_editHotkeyWeb;
QShortcut *m_deleteHotkeyWeb;
QShortcut *m_openHotkeyFile;
private slots:
void filterText(const QString &filter);

View File

@@ -67,7 +67,7 @@
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="downloaded_pieces_lbl">
<widget class="QLabel" name="labelDownloadedPieces">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -83,7 +83,7 @@
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="progress_lbl">
<widget class="QLabel" name="labelProgressVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -96,7 +96,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="avail_pieces_lbl">
<widget class="QLabel" name="labelPiecesAvailability">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -112,7 +112,7 @@
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="avail_average_lbl">
<widget class="QLabel" name="labelAverageAvailabilityVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -142,7 +142,7 @@
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<widget class="Line" name="lineBelowBars">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -161,7 +161,7 @@
<number>4</number>
</property>
<item row="2" column="1">
<widget class="QLabel" name="label_dl_speed_val">
<widget class="QLabel" name="labelDlSpeedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -174,7 +174,7 @@
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_upload_speed">
<widget class="QLabel" name="labelUpSpeed">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -190,7 +190,7 @@
</widget>
</item>
<item row="2" column="3" colspan="4">
<widget class="QLabel" name="label_upload_speed_val">
<widget class="QLabel" name="labelUpSpeedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -203,7 +203,7 @@
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_peers">
<widget class="QLabel" name="labelPeers">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -219,7 +219,7 @@
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_4">
<widget class="QLabel" name="labelConnections">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -235,7 +235,7 @@
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="reannounce_lbl">
<widget class="QLabel" name="labelReannounceInVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -248,7 +248,7 @@
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_eta_val">
<widget class="QLabel" name="labelETAVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -261,7 +261,7 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="labelDlLimit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -277,7 +277,7 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbl_ratio">
<widget class="QLabel" name="labelRatio">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -293,7 +293,7 @@
</widget>
</item>
<item row="0" column="5">
<widget class="QLabel" name="lbl_connections">
<widget class="QLabel" name="labelConnectionsVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -306,7 +306,7 @@
</widget>
</item>
<item row="2" column="5">
<widget class="QLabel" name="label_peers_val">
<widget class="QLabel" name="labelPeersVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -319,7 +319,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<widget class="QLabel" name="labelDownloaded">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -335,7 +335,7 @@
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label">
<widget class="QLabel" name="labelUpLimit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -351,7 +351,7 @@
</widget>
</item>
<item row="4" column="4">
<widget class="QLabel" name="label_last_complete">
<widget class="QLabel" name="labelLastSeenComplete">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -367,7 +367,7 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lbl_dllimit">
<widget class="QLabel" name="labelDlLimitVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -380,7 +380,7 @@
</widget>
</item>
<item row="1" column="3" colspan="4">
<widget class="QLabel" name="upTotal">
<widget class="QLabel" name="labelUpTotalVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -393,7 +393,7 @@
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_10">
<widget class="QLabel" name="labelReannounceIn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -409,7 +409,7 @@
</widget>
</item>
<item row="4" column="5">
<widget class="QLabel" name="label_last_complete_val">
<widget class="QLabel" name="labelLastSeenCompleteVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -422,7 +422,7 @@
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_seeds">
<widget class="QLabel" name="labelSeeds">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -438,7 +438,7 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_dl_speed">
<widget class="QLabel" name="labelDlSpeed">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -454,7 +454,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="dlTotal">
<widget class="QLabel" name="labelDlTotalVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -467,7 +467,7 @@
</widget>
</item>
<item row="3" column="5">
<widget class="QLabel" name="wasted">
<widget class="QLabel" name="labelWastedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -480,7 +480,7 @@
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="shareRatio">
<widget class="QLabel" name="labelShareRatioVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -493,7 +493,7 @@
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_5">
<widget class="QLabel" name="labelUploaded">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -509,7 +509,7 @@
</widget>
</item>
<item row="1" column="5">
<widget class="QLabel" name="label_seeds_val">
<widget class="QLabel" name="labelSeedsVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -522,7 +522,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lbl_elapsed">
<widget class="QLabel" name="labelElapsedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -535,7 +535,7 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<widget class="QLabel" name="labelTimeActive">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -551,7 +551,7 @@
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="lbl_uplimit">
<widget class="QLabel" name="labelUpLimitVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -564,7 +564,7 @@
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_eta">
<widget class="QLabel" name="labelETA">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -580,7 +580,7 @@
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label_8">
<widget class="QLabel" name="labelWasted">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -611,7 +611,7 @@
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_total_size">
<widget class="QLabel" name="labelTotalSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -627,7 +627,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_total_size_val">
<widget class="QLabel" name="labelTotalSizeVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -640,7 +640,7 @@
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_total_pieces">
<widget class="QLabel" name="labelTotalPieces">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -656,7 +656,7 @@
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_total_pieces_val">
<widget class="QLabel" name="labelTotalPiecesVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -669,7 +669,7 @@
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_created_by">
<widget class="QLabel" name="labelCreatedBy">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -685,7 +685,7 @@
</widget>
</item>
<item row="0" column="5">
<widget class="QLabel" name="label_created_by_val">
<widget class="QLabel" name="labelCreatedByVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -698,7 +698,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_added_on">
<widget class="QLabel" name="labelAddedOn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -714,7 +714,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_added_on_val">
<widget class="QLabel" name="labelAddedOnVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -727,7 +727,7 @@
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_completed_on">
<widget class="QLabel" name="labelCompletedOn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -743,7 +743,7 @@
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_completed_on_val">
<widget class="QLabel" name="labelCompletedOnVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -756,7 +756,7 @@
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_9">
<widget class="QLabel" name="labelCreatedOn">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -772,7 +772,7 @@
</widget>
</item>
<item row="1" column="5">
<widget class="QLabel" name="lbl_creationDate">
<widget class="QLabel" name="labelCreatedOnVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -785,7 +785,7 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="hash_lbl2">
<widget class="QLabel" name="labelHash">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -801,7 +801,7 @@
</widget>
</item>
<item row="2" column="1" colspan="5">
<widget class="QLabel" name="hash_lbl">
<widget class="QLabel" name="labelHashVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -817,7 +817,7 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="savePath_lbl">
<widget class="QLabel" name="labelSavePath">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -833,7 +833,7 @@
</widget>
</item>
<item row="3" column="1" colspan="5">
<widget class="QLabel" name="save_path">
<widget class="QLabel" name="labelSavePathVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -852,7 +852,7 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="comment_lbl2">
<widget class="QLabel" name="labelComment">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -868,7 +868,7 @@
</widget>
</item>
<item row="4" column="1" colspan="5">
<widget class="QLabel" name="comment_text">
<widget class="QLabel" name="labelCommentVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@@ -1000,6 +1000,9 @@
</widget>
<widget class="QWidget" name="pageContents">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
@@ -1062,7 +1065,7 @@
</layout>
</widget>
<widget class="QWidget" name="pageSpeed">
<layout class="QVBoxLayout" name="speed_layout">
<layout class="QVBoxLayout" name="speedLayout">
<property name="leftMargin">
<number>0</number>
</property>

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "proplistdelegate.h"
@@ -83,53 +81,53 @@ void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case PROGRESS: {
if (index.data().toDouble() < 0)
break;
if (index.data().toDouble() < 0)
break;
QStyleOptionProgressBar newopt;
qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.progress = int(progress);
newopt.maximum = 100;
newopt.minimum = 0;
newopt.textVisible = true;
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
newopt.state &= ~QStyle::State_Enabled;
newopt.palette = progressBarDisabledPalette();
}
else {
newopt.state |= QStyle::State_Enabled;
}
QStyleOptionProgressBar newopt;
qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.progress = int(progress);
newopt.maximum = 100;
newopt.minimum = 0;
newopt.textVisible = true;
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
newopt.state &= ~QStyle::State_Enabled;
newopt.palette = progressBarDisabledPalette();
}
else {
newopt.state |= QStyle::State_Enabled;
}
#ifndef Q_OS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
// XXX: To avoid having the progress text on the right of the bar
QProxyStyle("fusion").drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
// XXX: To avoid having the progress text on the right of the bar
QProxyStyle("fusion").drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif
}
break;
case PRIORITY: {
QString text = "";
switch (index.data().toInt()) {
case prio::MIXED:
text = tr("Mixed", "Mixed (priorities");
break;
case prio::IGNORED:
text = tr("Not downloaded");
break;
case prio::HIGH:
text = tr("High", "High (priority)");
break;
case prio::MAXIMUM:
text = tr("Maximum", "Maximum (priority)");
break;
default:
text = tr("Normal", "Normal (priority)");
break;
}
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
break;
case PRIORITY: {
QString text = "";
switch (index.data().toInt()) {
case prio::MIXED:
text = tr("Mixed", "Mixed (priorities");
break;
case prio::IGNORED:
text = tr("Not downloaded");
break;
case prio::HIGH:
text = tr("High", "High (priority)");
break;
case prio::MAXIMUM:
text = tr("Maximum", "Maximum (priority)");
break;
default:
text = tr("Normal", "Normal (priority)");
break;
}
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
}
break;
case AVAILABILITY: {
@@ -174,16 +172,16 @@ void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{
if (index.column() != PRIORITY) return 0;
if (index.column() != PRIORITY) return nullptr;
if (m_properties) {
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
if (!torrent || !torrent->hasMetadata() || torrent->isSeed())
return 0;
return nullptr;
}
if (index.data().toInt() == prio::MIXED)
return 0;
return nullptr;
QComboBox *editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus);

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef PROPLISTDELEGATE_H
@@ -33,10 +31,10 @@
#include <QItemDelegate>
class QPainter;
class QModelIndex;
class QStyleOptionViewItem;
class QAbstractItemModel;
class QModelIndex;
class QPainter;
class QStyleOptionViewItem;
class PropertiesWidget;
// Defines for properties list columns
@@ -50,7 +48,7 @@ enum PropColumn
AVAILABILITY
};
class PropListDelegate: public QItemDelegate
class PropListDelegate : public QItemDelegate
{
Q_OBJECT
@@ -72,5 +70,4 @@ private:
PropertiesWidget *m_properties;
};
#endif
#endif // PROPLISTDELEGATE_H

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,120 +24,121 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "proptabbar.h"
#include <QButtonGroup>
#include <QKeySequence>
#include <QPushButton>
#include <QSpacerItem>
#include <QKeySequence>
#include "proptabbar.h"
#include "guiiconprovider.h"
PropTabBar::PropTabBar(QWidget *parent) :
QHBoxLayout(parent), m_currentIndex(-1)
PropTabBar::PropTabBar(QWidget *parent)
: QHBoxLayout(parent)
, m_currentIndex(-1)
{
setAlignment(Qt::AlignLeft | Qt::AlignCenter);
setSpacing(3);
m_btnGroup = new QButtonGroup(this);
// General tab
QPushButton *main_infos_button = new QPushButton(
setAlignment(Qt::AlignLeft | Qt::AlignCenter);
setSpacing(3);
m_btnGroup = new QButtonGroup(this);
// General tab
QPushButton *mainInfosButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("document-properties"),
GuiIconProvider::instance()->getIcon("document-properties"),
#endif
tr("General"), parent);
main_infos_button->setShortcut(Qt::ALT + Qt::Key_G);
addWidget(main_infos_button);
m_btnGroup->addButton(main_infos_button, MAIN_TAB);
// Trackers tab
QPushButton *trackers_button = new QPushButton(
tr("General"), parent);
mainInfosButton->setShortcut(Qt::ALT + Qt::Key_G);
addWidget(mainInfosButton);
m_btnGroup->addButton(mainInfosButton, MainTab);
// Trackers tab
QPushButton *trackersButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("network-server"),
GuiIconProvider::instance()->getIcon("network-server"),
#endif
tr("Trackers"), parent);
trackers_button->setShortcut(Qt::ALT + Qt::Key_C);
addWidget(trackers_button);
m_btnGroup->addButton(trackers_button, TRACKERS_TAB);
// Peers tab
QPushButton *peers_button = new QPushButton(
tr("Trackers"), parent);
trackersButton->setShortcut(Qt::ALT + Qt::Key_C);
addWidget(trackersButton);
m_btnGroup->addButton(trackersButton, TrackersTab);
// Peers tab
QPushButton *peersButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("edit-find-user"),
GuiIconProvider::instance()->getIcon("edit-find-user"),
#endif
tr("Peers"), parent);
peers_button->setShortcut(Qt::ALT + Qt::Key_R);
addWidget(peers_button);
m_btnGroup->addButton(peers_button, PEERS_TAB);
// URL seeds tab
QPushButton *urlseeds_button = new QPushButton(
tr("Peers"), parent);
peersButton->setShortcut(Qt::ALT + Qt::Key_R);
addWidget(peersButton);
m_btnGroup->addButton(peersButton, PeersTab);
// URL seeds tab
QPushButton *URLSeedsButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("network-server"),
GuiIconProvider::instance()->getIcon("network-server"),
#endif
tr("HTTP Sources"), parent);
urlseeds_button->setShortcut(Qt::ALT + Qt::Key_B);
addWidget(urlseeds_button);
m_btnGroup->addButton(urlseeds_button, URLSEEDS_TAB);
// Files tab
QPushButton *files_button = new QPushButton(
tr("HTTP Sources"), parent);
URLSeedsButton->setShortcut(Qt::ALT + Qt::Key_B);
addWidget(URLSeedsButton);
m_btnGroup->addButton(URLSeedsButton, URLSeedsTab);
// Files tab
QPushButton *filesButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("inode-directory"),
GuiIconProvider::instance()->getIcon("inode-directory"),
#endif
tr("Content"), parent);
files_button->setShortcut(Qt::ALT + Qt::Key_Z);
addWidget(files_button);
m_btnGroup->addButton(files_button, FILES_TAB);
// Spacer
addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed));
// Speed tab
QPushButton *speed_button = new QPushButton(
tr("Content"), parent);
filesButton->setShortcut(Qt::ALT + Qt::Key_Z);
addWidget(filesButton);
m_btnGroup->addButton(filesButton, FilesTab);
// Spacer
addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed));
// Speed tab
QPushButton *speedButton = new QPushButton(
#ifndef Q_OS_MAC
GuiIconProvider::instance()->getIcon("office-chart-line"),
GuiIconProvider::instance()->getIcon("office-chart-line"),
#endif
tr("Speed"), parent);
speed_button->setShortcut(Qt::ALT + Qt::Key_D);
addWidget(speed_button);
m_btnGroup->addButton(speed_button, SPEED_TAB);
// SIGNAL/SLOT
connect(m_btnGroup, SIGNAL(buttonClicked(int)), SLOT(setCurrentIndex(int)));
// Disable buttons focus
foreach (QAbstractButton *btn, m_btnGroup->buttons()) {
btn->setFocusPolicy(Qt::NoFocus);
}
tr("Speed"), parent);
speedButton->setShortcut(Qt::ALT + Qt::Key_D);
addWidget(speedButton);
m_btnGroup->addButton(speedButton, SpeedTab);
// SIGNAL/SLOT
connect(m_btnGroup, SIGNAL(buttonClicked(int)), SLOT(setCurrentIndex(int)));
// Disable buttons focus
foreach (QAbstractButton *btn, m_btnGroup->buttons())
btn->setFocusPolicy(Qt::NoFocus);
}
PropTabBar::~PropTabBar() {
delete m_btnGroup;
PropTabBar::~PropTabBar()
{
delete m_btnGroup;
}
int PropTabBar::currentIndex() const
{
return m_currentIndex;
return m_currentIndex;
}
void PropTabBar::setCurrentIndex(int index)
{
if (index >= m_btnGroup->buttons().size())
if (index >= m_btnGroup->buttons().size())
index = 0;
// If asked to hide or if the currently selected tab is clicked
if (index < 0 || m_currentIndex == index) {
if (m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setDown(false);
m_currentIndex = -1;
emit visibilityToggled(false);
// If asked to hide or if the currently selected tab is clicked
if (index < 0 || m_currentIndex == index) {
if (m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setDown(false);
m_currentIndex = -1;
emit visibilityToggled(false);
}
return;
}
return;
}
// Unselect previous tab
if (m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setDown(false);
} else {
// Nothing was selected, show!
emit visibilityToggled(true);
}
// Select the new button
m_btnGroup->button(index)->setDown(true);
m_currentIndex = index;
// Emit the signal
emit tabChanged(index);
// Unselect previous tab
if (m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setDown(false);
}
else {
// Nothing was selected, show!
emit visibilityToggled(true);
}
// Select the new button
m_btnGroup->button(index)->setDown(true);
m_currentIndex = index;
// Emit the signal
emit tabChanged(index);
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef PROPTABBAR_H
@@ -33,9 +31,7 @@
#include <QHBoxLayout>
QT_BEGIN_NAMESPACE
class QButtonGroup;
QT_END_NAMESPACE
class PropTabBar : public QHBoxLayout
{
@@ -43,24 +39,30 @@ class PropTabBar : public QHBoxLayout
Q_DISABLE_COPY(PropTabBar)
public:
enum PropertyTab {MAIN_TAB, TRACKERS_TAB, PEERS_TAB, URLSEEDS_TAB, FILES_TAB, SPEED_TAB};
enum PropertyTab
{
MainTab,
TrackersTab,
PeersTab,
URLSeedsTab,
FilesTab,
SpeedTab
};
public:
explicit PropTabBar(QWidget *parent = 0);
~PropTabBar();
int currentIndex() const;
explicit PropTabBar(QWidget *parent = nullptr);
~PropTabBar();
int currentIndex() const;
signals:
void tabChanged(int index);
void visibilityToggled(bool visible);
void tabChanged(int index);
void visibilityToggled(bool visible);
public slots:
void setCurrentIndex(int index);
void setCurrentIndex(int index);
private:
QButtonGroup *m_btnGroup;
int m_currentIndex;
QButtonGroup *m_btnGroup;
int m_currentIndex;
};
#endif // PROPTABBAR_H

View File

@@ -191,20 +191,20 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
Utils::Misc::friendlyUnit(0, true)
};
int yAxeWidth = 0;
int yAxisWidth = 0;
for (const QString &label : speedLabels)
if (fontMetrics.width(label) > yAxeWidth)
yAxeWidth = fontMetrics.width(label);
if (fontMetrics.width(label) > yAxisWidth)
yAxisWidth = fontMetrics.width(label);
int i = 0;
for (const QString &label : speedLabels) {
QRectF labelRect(rect.topLeft() + QPointF(-yAxeWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
QSizeF(2 * yAxeWidth, fontMetrics.height()));
QRectF labelRect(rect.topLeft() + QPointF(-yAxisWidth, (i++) * 0.25 * rect.height() - fontMetrics.height()),
QSizeF(2 * yAxisWidth, fontMetrics.height()));
painter.drawText(labelRect, label, Qt::AlignRight | Qt::AlignTop);
}
// draw grid lines
rect.adjust(yAxeWidth + 4, 0, 0, 0);
rect.adjust(yAxisWidth + 4, 0, 0, 0);
QPen gridPen;
gridPen.setStyle(Qt::DashLine);
@@ -236,18 +236,16 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
boost::circular_buffer<PointData> &queue = getCurrentData();
for (int id = UP; id < NB_GRAPHS; ++id) {
if (!m_properties[static_cast<GraphID>(id)].enable)
continue;
QVector<QPoint> points;
for (int i = int(queue.size()) - 1, j = 0; i >= 0 && j <= m_viewablePointsCount; --i, ++j) {
int new_x = rect.right() - j * xTickSize;
int new_y = rect.bottom() - queue[i].y[id] * yMultiplier;
int newX = rect.right() - j * xTickSize;
int newY = rect.bottom() - queue[i].y[id] * yMultiplier;
points.push_back(QPoint(new_x, new_y));
points.push_back(QPoint(newX, newY));
}
painter.setPen(m_properties[static_cast<GraphID>(id)].pen);
@@ -260,7 +258,6 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
double legendHeight = 0;
int legendWidth = 0;
for (const auto &property : m_properties) {
if (!property.enable)
continue;
@@ -276,7 +273,6 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
i = 0;
for (const auto &property : m_properties) {
if (!property.enable)
continue;
@@ -293,10 +289,12 @@ void SpeedPlotView::paintEvent(QPaintEvent *)
SpeedPlotView::GraphProperties::GraphProperties()
: enable(false)
{}
{
}
SpeedPlotView::GraphProperties::GraphProperties(const QString &name, const QPen &pen, bool enable)
: name(name)
, pen(pen)
, enable(enable)
{}
{
}

View File

@@ -55,6 +55,7 @@ public:
DHT_DOWN,
TRACKER_UP,
TRACKER_DOWN,
NB_GRAPHS
};
@@ -82,7 +83,7 @@ public:
void replot();
protected:
virtual void paintEvent(QPaintEvent *event);
void paintEvent(QPaintEvent *event) override;
private:
enum PeriodInSeconds
@@ -110,6 +111,9 @@ private:
bool enable;
};
int maxYValue();
boost::circular_buffer<PointData> &getCurrentData();
boost::circular_buffer<PointData> m_data5Min;
boost::circular_buffer<PointData> m_data30Min;
boost::circular_buffer<PointData> m_data6Hour;
@@ -120,10 +124,6 @@ private:
int m_counter30Min;
int m_counter6Hour;
int maxYValue();
boost::circular_buffer<PointData> &getCurrentData();
};
#endif // SPEEDPLOTVIEW_H

View File

@@ -38,10 +38,10 @@
#include <libtorrent/session_status.hpp>
#include "propertieswidget.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/sessionstatus.h"
#include "base/preferences.h"
#include "propertieswidget.h"
ComboBoxMenuButton::ComboBoxMenuButton(QWidget *parent, QMenu *menu)
: QComboBox(parent)
@@ -61,6 +61,7 @@ SpeedWidget::SpeedWidget(PropertiesWidget *parent)
{
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(3);
m_hlayout = new QHBoxLayout();
m_hlayout->setContentsMargins(0, 0, 0, 0);

View File

@@ -29,8 +29,8 @@
#ifndef SPEEDWIDGET_H
#define SPEEDWIDGET_H
#include <QWidget>
#include <QComboBox>
#include <QWidget>
#include "speedplotview.h"
@@ -44,6 +44,7 @@ class PropertiesWidget;
class ComboBoxMenuButton : public QComboBox
{
Q_OBJECT
public:
ComboBoxMenuButton(QWidget *parent, QMenu *menu);
virtual void showPopup();
@@ -56,6 +57,7 @@ private:
class SpeedWidget : public QWidget
{
Q_OBJECT
public:
SpeedWidget(PropertiesWidget *parent);
~SpeedWidget();

View File

@@ -399,13 +399,13 @@ void TrackerList::copyTrackerUrl()
QList<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
if (selectedTrackerItems.isEmpty()) return;
QStringList URLsToCopy;
QStringList urlsToCopy;
foreach (QTreeWidgetItem *item, selectedTrackerItems) {
QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
qDebug() << QString("Copy: ") + trackerURL;
URLsToCopy << trackerURL;
urlsToCopy << trackerURL;
}
QApplication::clipboard()->setText(URLsToCopy.join("\n"));
QApplication::clipboard()->setText(urlsToCopy.join("\n"));
}
@@ -420,10 +420,10 @@ void TrackerList::deleteSelectedTrackers()
QList<QTreeWidgetItem *> selectedTrackerItems = getSelectedTrackerItems();
if (selectedTrackerItems.isEmpty()) return;
QStringList URLsToRemove;
QStringList urlsToRemove;
foreach (QTreeWidgetItem *item, selectedTrackerItems) {
QString trackerURL = item->data(COL_URL, Qt::DisplayRole).toString();
URLsToRemove << trackerURL;
urlsToRemove << trackerURL;
m_trackerItems.remove(trackerURL);
delete item;
}
@@ -432,7 +432,7 @@ void TrackerList::deleteSelectedTrackers()
QList<BitTorrent::TrackerEntry> remainingTrackers;
QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
foreach (const BitTorrent::TrackerEntry &entry, trackers) {
if (!URLsToRemove.contains(entry.url()))
if (!urlsToRemove.contains(entry.url()))
remainingTrackers.push_back(entry);
}
@@ -627,9 +627,8 @@ void TrackerList::displayToggleColumnsMenu(const QPoint &)
Q_ASSERT(visibleColumnsCount() > 0);
if (!isColumnHidden(col) && (visibleColumnsCount() == 1))
return;
qDebug("Toggling column %d visibility", col);
setColumnHidden(col, !isColumnHidden(col));
if (!isColumnHidden(col) && (columnWidth(col) <= 5))
setColumnWidth(col, 100);
resizeColumnToContents(col);
saveSettings();
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,22 +24,20 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#include "trackersadditiondlg.h"
#include <QStringList>
#include <QMessageBox>
#include <QFile>
#include <QMessageBox>
#include <QStringList>
#include <QUrl>
#include "base/utils/misc.h"
#include "base/utils/fs.h"
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "base/bittorrent/trackerentry.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/trackerentry.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "guiiconprovider.h"
#include "ui_trackersadditiondlg.h"
@@ -75,14 +73,14 @@ void TrackersAdditionDlg::on_uTorrentListButton_clicked()
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_ui->list_url->text(), true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString)));
//Just to show that it takes times
// Just to show that it takes times
setCursor(Qt::WaitCursor);
}
void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path)
{
QFile list_file(path);
if (!list_file.open(QFile::ReadOnly)) {
QFile listFile(path);
if (!listFile.open(QFile::ReadOnly)) {
QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok);
setCursor(Qt::ArrowCursor);
m_ui->uTorrentListButton->setEnabled(true);
@@ -94,8 +92,8 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
QList<BitTorrent::TrackerEntry> existingTrackers = m_torrent->trackers();
// Load from current user list
QStringList tmp = m_ui->trackers_list->toPlainText().split("\n");
foreach (const QString &user_url, tmp) {
BitTorrent::TrackerEntry userTracker(user_url);
foreach (const QString &userURL, tmp) {
BitTorrent::TrackerEntry userTracker(userURL);
if (!existingTrackers.contains(userTracker))
existingTrackers << userTracker;
}
@@ -104,8 +102,8 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
if (!m_ui->trackers_list->toPlainText().isEmpty() && !m_ui->trackers_list->toPlainText().endsWith("\n"))
m_ui->trackers_list->insertPlainText("\n");
int nb = 0;
while (!list_file.atEnd()) {
const QString line = list_file.readLine().trimmed();
while (!listFile.atEnd()) {
const QString line = listFile.readLine().trimmed();
if (line.isEmpty()) continue;
BitTorrent::TrackerEntry newTracker(line);
if (!existingTrackers.contains(newTracker)) {
@@ -114,7 +112,7 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
}
}
// Clean up
list_file.close();
listFile.close();
Utils::Fs::forceRemove(path);
//To restore the cursor ...
setCursor(Qt::ArrowCursor);

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,6 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef TRACKERSADDITION_H
@@ -67,4 +65,4 @@ private:
BitTorrent::TorrentHandle *const m_torrent;
};
#endif
#endif // TRACKERSADDITION_H

View File

@@ -53,9 +53,15 @@
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
#include "ui_automatedrssdownloader.h"
#include "utils.h"
const QString EXT_JSON {QStringLiteral(".json")};
const QString EXT_LEGACY {QStringLiteral(".rssrules")};
AutomatedRssDownloader::AutomatedRssDownloader(QWidget *parent)
: QDialog(parent)
, m_formatFilterJSON(QString("%1 (*%2)").arg(tr("Rules")).arg(EXT_JSON))
, m_formatFilterLegacy(QString("%1 (*%2)").arg(tr("Rules (legacy)")).arg(EXT_LEGACY))
, m_ui(new Ui::AutomatedRssDownloader)
, m_currentRuleItem(nullptr)
{
@@ -150,14 +156,14 @@ AutomatedRssDownloader::~AutomatedRssDownloader()
void AutomatedRssDownloader::loadSettings()
{
const Preferences *const pref = Preferences::instance();
resize(pref->getRssGeometrySize(this->size()));
Utils::Gui::resize(this, pref->getRssGeometrySize());
m_ui->hsplitter->restoreState(pref->getRssHSplitterSizes());
}
void AutomatedRssDownloader::saveSettings()
{
Preferences *const pref = Preferences::instance();
pref->setRssGeometrySize(this->size());
pref->setRssGeometrySize(size());
pref->setRssHSplitterSizes(m_ui->hsplitter->saveState());
}
@@ -306,7 +312,7 @@ void AutomatedRssDownloader::initCategoryCombobox()
{
// Load torrent categories
QStringList categories = BitTorrent::Session::instance()->categories().keys();
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
std::sort(categories.begin(), categories.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
m_ui->comboCategory->addItem("");
m_ui->comboCategory->addItems(categories);
}
@@ -384,31 +390,73 @@ void AutomatedRssDownloader::on_browseSP_clicked()
void AutomatedRssDownloader::on_exportBtn_clicked()
{
// if (m_editableRuleList->isEmpty()) {
// QMessageBox::warning(this, tr("Invalid action"), tr("The list is empty, there is nothing to export."));
// return;
// }
// // Ask for a save path
// QString save_path = QFileDialog::getSaveFileName(this, tr("Where would you like to save the list?"), QDir::homePath(), tr("Rules list (*.rssrules)"));
// if (save_path.isEmpty()) return;
// if (!save_path.endsWith(".rssrules", Qt::CaseInsensitive))
// save_path += ".rssrules";
// if (!m_editableRuleList->serialize(save_path)) {
// QMessageBox::warning(this, tr("I/O Error"), tr("Failed to create the destination file"));
// return;
// }
if (RSS::AutoDownloader::instance()->rules().isEmpty()) {
QMessageBox::warning(this, tr("Invalid action")
, tr("The list is empty, there is nothing to export."));
return;
}
QString selectedFilter {m_formatFilterJSON};
QString path = QFileDialog::getSaveFileName(
this, tr("Export RSS rules"), QDir::homePath()
, QString("%1;;%2").arg(m_formatFilterJSON).arg(m_formatFilterLegacy), &selectedFilter);
if (path.isEmpty()) return;
const RSS::AutoDownloader::RulesFileFormat format {
(selectedFilter == m_formatFilterJSON)
? RSS::AutoDownloader::RulesFileFormat::JSON
: RSS::AutoDownloader::RulesFileFormat::Legacy
};
if (format == RSS::AutoDownloader::RulesFileFormat::JSON) {
if (!path.endsWith(EXT_JSON, Qt::CaseInsensitive))
path += EXT_JSON;
}
else {
if (!path.endsWith(EXT_LEGACY, Qt::CaseInsensitive))
path += EXT_LEGACY;
}
QFile file {path};
if (!file.open(QFile::WriteOnly)
|| (file.write(RSS::AutoDownloader::instance()->exportRules(format)) == -1)) {
QMessageBox::critical(
this, tr("I/O Error")
, tr("Failed to create the destination file. Reason: %1").arg(file.errorString()));
}
}
void AutomatedRssDownloader::on_importBtn_clicked()
{
// // Ask for filter path
// QString load_path = QFileDialog::getOpenFileName(this, tr("Please point to the RSS download rules file"), QDir::homePath(), tr("Rules list") + QString(" (*.rssrules *.filters)"));
// if (load_path.isEmpty() || !QFile::exists(load_path)) return;
// // Load it
// if (!m_editableRuleList->unserialize(load_path)) {
// QMessageBox::warning(this, tr("Import Error"), tr("Failed to import the selected rules file"));
// return;
// }
QString selectedFilter {m_formatFilterJSON};
QString path = QFileDialog::getOpenFileName(
this, tr("Import RSS rules"), QDir::homePath()
, QString("%1;;%2").arg(m_formatFilterJSON).arg(m_formatFilterLegacy), &selectedFilter);
if (path.isEmpty() || !QFile::exists(path))
return;
QFile file {path};
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(
this, tr("I/O Error")
, tr("Failed to open the file. Reason: %1").arg(file.errorString()));
return;
}
const RSS::AutoDownloader::RulesFileFormat format {
(selectedFilter == m_formatFilterJSON)
? RSS::AutoDownloader::RulesFileFormat::JSON
: RSS::AutoDownloader::RulesFileFormat::Legacy
};
try {
RSS::AutoDownloader::instance()->importRules(file.readAll(),format);
}
catch (const RSS::ParsingError &error) {
QMessageBox::critical(
this, tr("Import Error")
, tr("Failed to import the selected rules file. Reason: %1").arg(error.message()));
}
}
void AutomatedRssDownloader::displayRulesListMenu()
@@ -708,6 +756,7 @@ void AutomatedRssDownloader::handleRuleChanged(const QString &ruleName)
void AutomatedRssDownloader::handleRuleAboutToBeRemoved(const QString &ruleName)
{
m_currentRuleItem = nullptr;
delete m_itemsByRuleName.take(ruleName);
}

View File

@@ -96,6 +96,9 @@ private:
void updateFeedList();
void addFeedArticlesToTree(RSS::Feed *feed, const QStringList &articles);
const QString m_formatFilterJSON;
const QString m_formatFilterLegacy;
Ui::AutomatedRssDownloader *m_ui;
QListWidgetItem *m_currentRuleItem;
QShortcut *m_editHotkey;

View File

@@ -386,7 +386,7 @@
<item>
<widget class="QPushButton" name="importBtn">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Import...</string>
@@ -396,7 +396,7 @@
<item>
<widget class="QPushButton" name="exportBtn">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Export...</string>

View File

@@ -31,25 +31,26 @@
#include "pluginselectdlg.h"
#include <QClipboard>
#include <QDropEvent>
#include <QFileDialog>
#include <QHeaderView>
#include <QImageReader>
#include <QMenu>
#include <QMessageBox>
#include <QFileDialog>
#include <QDropEvent>
#include <QMimeData>
#include <QClipboard>
#include <QTableView>
#include <QImageReader>
#include "autoexpandabledialog.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "searchwidget.h"
#include "pluginsourcedlg.h"
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
#include "pluginsourcedlg.h"
#include "searchwidget.h"
#include "ui_pluginselectdlg.h"
#include "utils.h"
enum PluginColumns
{
@@ -78,9 +79,6 @@ PluginSelectDlg::PluginSelectDlg(SearchEngine *pluginManager, QWidget *parent)
unused.setVerticalHeader(new QHeaderView(Qt::Horizontal));
m_ui->pluginsTree->setRootIsDecorated(false);
m_ui->pluginsTree->header()->resizeSection(0, 160);
m_ui->pluginsTree->header()->resizeSection(1, 80);
m_ui->pluginsTree->header()->resizeSection(2, 200);
m_ui->pluginsTree->hideColumn(PLUGIN_ID);
m_ui->pluginsTree->header()->setSortIndicator(0, Qt::AscendingOrder);
@@ -99,6 +97,7 @@ PluginSelectDlg::PluginSelectDlg(SearchEngine *pluginManager, QWidget *parent)
connect(m_pluginManager, &SearchEngine::checkForUpdatesFinished, this, &PluginSelectDlg::checkForUpdatesFinished);
connect(m_pluginManager, &SearchEngine::checkForUpdatesFailed, this, &PluginSelectDlg::checkForUpdatesFailed);
Utils::Gui::resize(this);
show();
}

View File

@@ -31,6 +31,7 @@
#include "pluginsourcedlg.h"
#include "ui_pluginsourcedlg.h"
#include "utils.h"
PluginSourceDlg::PluginSourceDlg(QWidget *parent)
: QDialog(parent)
@@ -38,6 +39,8 @@ PluginSourceDlg::PluginSourceDlg(QWidget *parent)
{
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
Utils::Gui::resize(this);
show();
}

View File

@@ -28,14 +28,20 @@
* Contact : chris@qbittorrent.org
*/
#include <QStyleOptionViewItem>
#include <QCoreApplication>
#include <QModelIndex>
#include <QPainter>
#include <QProgressBar>
#include <QStyleOptionViewItem>
#include "base/utils/misc.h"
#include "searchsortmodel.h"
#include "searchlistdelegate.h"
#include "searchsortmodel.h"
namespace
{
const char i18nContext[] = "SearchListDelegate";
}
SearchListDelegate::SearchListDelegate(QObject *parent)
: QItemDelegate(parent)
@@ -57,7 +63,8 @@ void SearchListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
case SearchSortModel::SEEDS:
case SearchSortModel::LEECHES:
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown"));
QItemDelegate::drawDisplay(painter, opt, option.rect
, (index.data().toLongLong() >= 0) ? index.data().toString() : QCoreApplication::translate(i18nContext, "Unknown"));
break;
default:
QItemDelegate::paint(painter, option, index);

View File

@@ -110,11 +110,12 @@ bool SearchSortModel::lessThan(const QModelIndex &left, const QModelIndex &right
switch (sortColumn()) {
case NAME:
case ENGINE_URL: {
QString vL = left.data().toString();
QString vR = right.data().toString();
return Utils::String::naturalCompareCaseInsensitive(vL, vR);
const QString strL = left.data().toString();
const QString strR = right.data().toString();
const int result = Utils::String::naturalCompare(strL, strR, Qt::CaseInsensitive);
return (result < 0);
}
break;
default:
return base::lessThan(left, right);
};

View File

@@ -334,10 +334,9 @@ void SearchTab::displayToggleColumnsMenu(const QPoint&)
Q_ASSERT(visibleCols > 0);
if ((!m_ui->resultsBrowser->isColumnHidden(col)) && (visibleCols == 1))
return;
qDebug("Toggling column %d visibility", col);
m_ui->resultsBrowser->setColumnHidden(col, !m_ui->resultsBrowser->isColumnHidden(col));
if ((!m_ui->resultsBrowser->isColumnHidden(col)) && (m_ui->resultsBrowser->columnWidth(col) <= 5))
m_ui->resultsBrowser->setColumnWidth(col, 100);
m_ui->resultsBrowser->resizeColumnToContents(col);
saveSettings();
}
}

Some files were not shown because too many files have changed in this diff Show More