Compare commits

..

101 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
sledgehammer999
fb20f59a96 Bump to 4.0.1 2017-11-22 01:18:04 +02:00
sledgehammer999
a15e3407b0 Bump API_VERSION to 16. 2017-11-22 01:14:58 +02:00
sledgehammer999
e267c2d37a Sync translations from Transifex and run lupdate. 2017-11-22 01:05:19 +02:00
sledgehammer999
ae32edeb26 Correctly check if torrent passed during application start already exists. 2017-11-22 01:05:18 +02:00
Chocobo1
34d38ef466 Fix crash when aborting a torrent creation process. Closes #7783.
The wait time wasn't long enough causing the thread to
terminate prematurely.
Also, to avoid crashing qbt entirely when creating a torrent for a very
big file, I decided to wait indefinitely here.
2017-11-22 01:05:17 +02:00
Chocobo1
120ee6b836 Wrap function into anonymous namespace 2017-11-22 01:05:15 +02:00
Chocobo1
7d25b6fce2 Avoid double delete on close 2017-11-22 01:05:14 +02:00
Chocobo1
068eff9e9f Use Qt5 connect syntax 2017-11-22 01:05:13 +02:00
Chocobo1
31a55f79f1 Remove superfluous QString::fromUtf8() 2017-11-22 01:03:41 +02:00
Chocobo1
bac032e01c Allow drag-n-drop magnet links to mainwindow. Closes #7742. 2017-11-22 01:03:40 +02:00
sledgehammer999
b809941f02 Fix english typo. 2017-11-22 01:03:39 +02:00
Chocobo1
77c3758090 [WebUI] Fix logo missing in login page 2017-11-22 01:03:37 +02:00
luigino
5758817189 qtsingleapplication should always be built statically to avoid dependency problems 2017-11-20 22:48:56 +02:00
Thomas Piccirello
acc9f08a05 Remove duplicate header 2017-11-20 22:48:55 +02:00
Thomas Piccirello
f3b7f17a7c Enable preferences Apply button when ip banlist is modified 2017-11-20 22:48:54 +02:00
Thomas Piccirello
dfc3f047e2 Add ip subnet whitelist for bypassing webui auth 2017-11-20 22:48:53 +02:00
sledgehammer999
223ab7de84 Fix crash on opening torrent/magnet (uninitialized pointer). Closes #7739 #7723. 2017-11-20 22:48:52 +02:00
sledgehammer999
d2a4027347 Improve the github issue template. 2017-11-20 22:48:37 +02:00
226 changed files with 52225 additions and 46959 deletions

View File

@@ -1,14 +1,20 @@
**Please provide the following information** **Please provide the following information**
### qBittorrent version and Operating System: ### qBittorrent version and Operating System
(type here)
### If on linux, libtorrent and Qt version: ### If on linux, libtorrent and Qt version
(type here)
### What is the problem: ### What is the problem
(type here)
### What is the expected behavior: ### What is the expected behavior
(type here)
### Steps to reproduce: ### Steps to reproduce
(type here)
### Extra info(if any): ### Extra info(if any)
(type here)

View File

@@ -76,10 +76,13 @@ before_install:
- shopt -s expand_aliases - shopt -s expand_aliases
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system) - 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" - 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 # options for specific branches
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi - if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
@@ -131,13 +134,13 @@ install:
cp "version" $HOME/hombebrew_cache cp "version" $HOME/hombebrew_cache
cd "$HOME/hombebrew_cache" cd "$HOME/hombebrew_cache"
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb 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 fi
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it # 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 # Also install our custom libtorrent formula by passing the local path to it
# These 2 files are restored from Travis' cache. # 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" brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
if [ "$build_system" = "cmake" ]; then if [ "$build_system" = "cmake" ]; then
@@ -168,11 +171,15 @@ script:
BUILD_TOOL="ninja" BUILD_TOOL="ninja"
fi fi
if [ "$build_system" = "qmake" ]; then if [ "$build_system" = "qmake" ]; then
./bootstrap.sh && ./configure $qbtconf
if [ "$TRAVIS_OS_NAME" = "osx" ]; then 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/^\(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/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
else
./bootstrap.sh && ./configure $qbtconf
fi fi
BUILD_TOOL="make" BUILD_TOOL="make"
fi 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: * Line breaks for long lines with operation:

View File

@@ -1,3 +1,76 @@
* 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)
- BUGFIX: Allow drag-n-drop magnet links to mainwindow. Closes #7742. (Chocobo1)
- BUGFIX: Fix crash when aborting a torrent creation process. Closes #7783. (Chocobo1)
- BUGFIX: Correctly check if torrent passed during application start already exists. (sledgehammer999)
- WEBUI: Add ip subnet whitelist for bypassing webui auth (Thomas Piccirello)
- WEBUI: Fix logo missing in login page (Chocobo1)
- COSMETIC: Fix english typo. (sledgehammer999)
- OTHER: cmake: qtsingleapplication should always be built statically (luigino)
* Mon Nov 20 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.0 * Mon Nov 20 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.0
- FEATURE: Change qbittorrent logo. Issue #6467. (HVS, Atif Afzal, sledgehammer999) - FEATURE: Change qbittorrent logo. Issue #6467. (HVS, Atif Afzal, sledgehammer999)
- FEATURE: New icon theme with SVG source, so we can scale it appropriately in the future. (Bert Verhelst) - FEATURE: New icon theme with SVG source, so we can scale it appropriately in the future. (Bert Verhelst)

View File

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

40
configure vendored
View File

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

4
dist/mac/Info.plist vendored
View File

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

View File

@@ -56,7 +56,7 @@
</image> </image>
</screenshot> </screenshot>
</screenshots> </screenshots>
<url type="homepage">http://www.qbittorrent.org/</url> <url type="homepage">https://www.qbittorrent.org/</url>
<update_contact>sledgehammer999@qbittorrent.org</update_contact> <update_contact>sledgehammer999@qbittorrent.org</update_contact>
<developer_name>The qBittorrent Project</developer_name> <developer_name>The qBittorrent Project</developer_name>
<url type="bugtracker">http://bugs.qbittorrent.org/</url> <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" "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" "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" "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}" 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" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoRepair" 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 !define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific ; Program specific
!define PROG_VERSION "4.0.0" !define PROG_VERSION "4.0.4"
!define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
@@ -50,7 +50,7 @@ XPStyle on
;Installer Version Information ;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent" VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project" 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 "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}" VIAddVersionKey "FileVersion" "${PROG_VERSION}"

View File

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

View File

@@ -114,7 +114,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
AS_CASE([${host_cpu}], AS_CASE([${host_cpu}],
[x86_64],[libsubdirs="lib64 libx32 lib lib64"], [x86_64],[libsubdirs="lib64 libx32 lib lib64"],
[ppc64|s390x|sparc64|aarch64|ppc64le],[libsubdirs="lib64 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 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_PATH = FILELOGGER_SETTINGS_KEY("Path");
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup"); const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld"); 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_AGE = FILELOGGER_SETTINGS_KEY("Age");
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType"); const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
@@ -98,6 +98,10 @@ namespace
const char PARAMS_SEPARATOR[] = "|"; const char PARAMS_SEPARATOR[] = "|";
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile"); 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) Application::Application(const QString &id, int &argc, char **argv)
@@ -105,6 +109,9 @@ Application::Application(const QString &id, int &argc, char **argv)
, m_running(false) , m_running(false)
, m_shutdownAct(ShutdownDialogAction::Exit) , m_shutdownAct(ShutdownDialogAction::Exit)
, m_commandLineArgs(parseCommandLine(this->arguments())) , m_commandLineArgs(parseCommandLine(this->arguments()))
#ifndef DISABLE_WEBUI
, m_webui(nullptr)
#endif
{ {
qRegisterMetaType<Log::Msg>("Log::Msg"); qRegisterMetaType<Log::Msg>("Log::Msg");
@@ -125,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 if (m_commandLineArgs.webUiPort > 0) // it will be -1 when user did not set any value
Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort); Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort);
setApplicationName("qBittorrent");
initializeTranslation(); initializeTranslation();
#if !defined(DISABLE_GUI) #if !defined(DISABLE_GUI)
@@ -218,29 +224,22 @@ void Application::setFileLoggerDeleteOld(bool value)
int Application::fileLoggerMaxSize() const int Application::fileLoggerMaxSize() const
{ {
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt(); int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZEBYTES, DEFAULT_FILELOG_SIZE).toInt();
if (val < 1) return std::min(std::max(val, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
return 1;
if (val > 1000)
return 1000;
return val;
} }
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) if (m_fileLogger)
m_fileLogger->setMaxSize(value); m_fileLogger->setMaxSize(clampedValue);
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000)); settings()->storeValue(KEY_FILELOGGER_MAXSIZEBYTES, clampedValue);
} }
int Application::fileLoggerAge() const int Application::fileLoggerAge() const
{ {
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt(); int val = settings()->loadValue(KEY_FILELOGGER_AGE, 1).toInt();
if (val < 1) return std::min(std::max(val, 1), 365);
return 1;
if (val > 365)
return 365;
return val;
} }
void Application::setFileLoggerAge(const int value) void Application::setFileLoggerAge(const int value)
@@ -288,27 +287,6 @@ void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) c
#if defined(Q_OS_UNIX) #if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program}); 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 #else
QProcess::startDetached(program); QProcess::startDetached(program);
#endif #endif
@@ -518,15 +496,15 @@ int Application::exec(const QStringList &params)
#endif // DISABLE_GUI #endif // DISABLE_GUI
m_running = true; m_running = true;
// Now UI is ready to process signals from Session
BitTorrent::Session::instance()->startUpTorrents();
m_paramsQueue = params + m_paramsQueue; m_paramsQueue = params + m_paramsQueue;
if (!m_paramsQueue.isEmpty()) { if (!m_paramsQueue.isEmpty()) {
processParams(m_paramsQueue); processParams(m_paramsQueue);
m_paramsQueue.clear(); m_paramsQueue.clear();
} }
// Now UI is ready to process signals from Session
BitTorrent::Session::instance()->startUpTorrents();
return BaseApplication::exec(); return BaseApplication::exec();
} }
@@ -599,15 +577,15 @@ void Application::initializeTranslation()
// Load translation // Load translation
QString localeStr = pref->getLocale(); QString localeStr = pref->getLocale();
if (m_qtTranslator.load(QString::fromUtf8("qtbase_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath)) || if (m_qtTranslator.load(QLatin1String("qtbase_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath)) ||
m_qtTranslator.load(QString::fromUtf8("qt_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) m_qtTranslator.load(QLatin1String("qt_") + localeStr, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
qDebug("Qt %s locale recognized, using translation.", qUtf8Printable(localeStr)); qDebug("Qt %s locale recognized, using translation.", qUtf8Printable(localeStr));
else else
qDebug("Qt %s locale unrecognized, using default (en).", qUtf8Printable(localeStr)); qDebug("Qt %s locale unrecognized, using default (en).", qUtf8Printable(localeStr));
installTranslator(&m_qtTranslator); installTranslator(&m_qtTranslator);
if (m_translator.load(QString::fromUtf8(":/lang/qbittorrent_") + localeStr)) if (m_translator.load(QLatin1String(":/lang/qbittorrent_") + localeStr))
qDebug("%s locale recognized, using translation.", qUtf8Printable(localeStr)); qDebug("%s locale recognized, using translation.", qUtf8Printable(localeStr));
else else
qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(localeStr)); qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(localeStr));

View File

@@ -101,7 +101,7 @@ public:
bool isFileLoggerDeleteOld() const; bool isFileLoggerDeleteOld() const;
void setFileLoggerDeleteOld(bool value); void setFileLoggerDeleteOld(bool value);
int fileLoggerMaxSize() const; int fileLoggerMaxSize() const;
void setFileLoggerMaxSize(const int value); void setFileLoggerMaxSize(const int bytes);
int fileLoggerAge() const; int fileLoggerAge() const;
void setFileLoggerAge(const int value); void setFileLoggerAge(const int value);
int fileLoggerAgeType() const; 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; 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(); closeLogFile();
int counter = 0; int counter = 0;
QString backupLogFilename = m_path + ".bak"; 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 // 3. https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); 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 #endif
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)

View File

@@ -16,7 +16,7 @@ else (GUI)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp) list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
endif (GUI) endif (GUI)
add_library(qtsingleapplication ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES}) add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}") target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
target_link_qt_components(qtsingleapplication Network) target_link_qt_components(qtsingleapplication Network)

View File

@@ -1,124 +1,124 @@
HEADERS += \ HEADERS += \
$$PWD/asyncfilestorage.h \ $$PWD/asyncfilestorage.h \
$$PWD/types.h \ $$PWD/bittorrent/addtorrentparams.h \
$$PWD/tristatebool.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/filesystemwatcher.h \
$$PWD/logger.h \ $$PWD/global.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/preferences.h \
$$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/connection.h \ $$PWD/http/connection.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/requestparser.h \ $$PWD/http/requestparser.h \
$$PWD/http/responsebuilder.h \
$$PWD/http/responsegenerator.h \ $$PWD/http/responsegenerator.h \
$$PWD/http/server.h \ $$PWD/http/server.h \
$$PWD/http/types.h \ $$PWD/http/types.h \
$$PWD/http/responsebuilder.h \ $$PWD/iconprovider.h \
$$PWD/indexrange.h \
$$PWD/logger.h \
$$PWD/net/dnsupdater.h \ $$PWD/net/dnsupdater.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/downloadhandler.h \ $$PWD/net/downloadhandler.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/geoipmanager.h \ $$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \ $$PWD/net/portforwarder.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/net/proxyconfigurationmanager.h \ $$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \ $$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \ $$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \ $$PWD/preferences.h \
$$PWD/bittorrent/addtorrentparams.h \ $$PWD/private/profile_p.h \
$$PWD/bittorrent/infohash.h \ $$PWD/profile.h \
$$PWD/bittorrent/session.h \ $$PWD/rss/private/rss_parser.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/rss/rss_article.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_autodownloader.h \
$$PWD/rss/rss_autodownloadrule.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/fs.h \
$$PWD/utils/gzip.h \ $$PWD/utils/gzip.h \
$$PWD/utils/misc.h \ $$PWD/utils/misc.h \
$$PWD/utils/net.h \ $$PWD/utils/net.h \
$$PWD/utils/random.h \ $$PWD/utils/random.h \
$$PWD/utils/string.h \ $$PWD/utils/string.h \
$$PWD/utils/version.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
SOURCES += \ SOURCES += \
$$PWD/asyncfilestorage.cpp \ $$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/filesystemwatcher.cpp \
$$PWD/logger.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/preferences.cpp \
$$PWD/iconprovider.cpp \
$$PWD/http/connection.cpp \ $$PWD/http/connection.cpp \
$$PWD/http/requestparser.cpp \ $$PWD/http/requestparser.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/http/responsegenerator.cpp \ $$PWD/http/responsegenerator.cpp \
$$PWD/http/server.cpp \ $$PWD/http/server.cpp \
$$PWD/http/responsebuilder.cpp \ $$PWD/iconprovider.cpp \
$$PWD/logger.cpp \
$$PWD/net/dnsupdater.cpp \ $$PWD/net/dnsupdater.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/downloadhandler.cpp \ $$PWD/net/downloadhandler.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/geoipmanager.cpp \ $$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \ $$PWD/net/portforwarder.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/net/proxyconfigurationmanager.cpp \ $$PWD/net/proxyconfigurationmanager.cpp \
$$PWD/net/reverseresolution.cpp \ $$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \ $$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \ $$PWD/preferences.cpp \
$$PWD/bittorrent/infohash.cpp \ $$PWD/private/profile_p.cpp \
$$PWD/bittorrent/session.cpp \ $$PWD/profile.cpp \
$$PWD/bittorrent/magneturi.cpp \ $$PWD/rss/private/rss_parser.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/rss/rss_article.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_autodownloader.cpp \
$$PWD/rss/rss_autodownloadrule.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/fs.cpp \
$$PWD/utils/gzip.cpp \ $$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \ $$PWD/utils/misc.cpp \
$$PWD/utils/net.cpp \ $$PWD/utils/net.cpp \
$$PWD/utils/random.cpp \ $$PWD/utils/random.cpp \
$$PWD/utils/string.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

View File

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

View File

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

View File

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

View File

@@ -35,8 +35,8 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <libtorrent/bencode.hpp> #include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp> #include <libtorrent/create_torrent.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/storage.hpp> #include <libtorrent/storage.hpp>
#include <libtorrent/torrent_info.hpp>
#include <QFile> #include <QFile>
@@ -44,120 +44,115 @@
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
namespace
{
// do not include files and folders whose
// name starts with a .
bool fileFilter(const std::string &f)
{
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
}
}
namespace libt = libtorrent; namespace libt = libtorrent;
using namespace BitTorrent; using namespace BitTorrent;
// do not include files and folders whose
// name starts with a .
bool fileFilter(const std::string &f)
{
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
}
TorrentCreatorThread::TorrentCreatorThread(QObject *parent) TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
: QThread(parent) : QThread(parent)
, m_private(false)
, m_pieceSize(0)
{ {
} }
TorrentCreatorThread::~TorrentCreatorThread() TorrentCreatorThread::~TorrentCreatorThread()
{ {
requestInterruption(); requestInterruption();
wait(1000); wait();
} }
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers, void TorrentCreatorThread::create(const TorrentCreatorParams &params)
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
{ {
m_inputPath = Utils::Fs::fromNativePath(inputPath); m_params = params;
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;
start(); 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() void TorrentCreatorThread::run()
{ {
const QString creatorStr("qBittorrent " QBT_VERSION);
emit updateProgress(0); emit updateProgress(0);
QString creator_str("qBittorrent " QBT_VERSION);
try { try {
libt::file_storage fs; libt::file_storage fs;
// Adding files to the torrent // 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; if (isInterruptionRequested()) return;
libt::create_torrent t(fs, m_pieceSize); libt::create_torrent newTorrent(fs, m_params.pieceSize);
// Add url seeds // Add url seeds
foreach (const QString &seed, m_urlSeeds) foreach (QString seed, m_params.urlSeeds) {
t.add_url_seed(seed.trimmed().toStdString()); seed = seed.trimmed();
if (!seed.isEmpty())
newTorrent.add_url_seed(seed.toStdString());
}
int tier = 0; int tier = 0;
bool newline = false; foreach (const QString &tracker, m_params.trackers) {
foreach (const QString &tracker, m_trackers) { if (tracker.isEmpty())
if (tracker.isEmpty()) {
if (newline)
continue;
++tier; ++tier;
newline = true; else
continue; newTorrent.add_tracker(tracker.trimmed().toStdString(), tier);
}
t.add_tracker(tracker.trimmed().toStdString(), tier);
newline = false;
} }
if (isInterruptionRequested()) return; if (isInterruptionRequested()) return;
// calculate the hash for all pieces // calculate the hash for all pieces
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/"; const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
libt::set_piece_hashes(t, Utils::Fs::toNativePath(parentPath).toStdString(), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces())); 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 // Set qBittorrent as creator and add user comment to
// torrent_info structure // torrent_info structure
t.set_creator(creator_str.toUtf8().constData()); newTorrent.set_creator(creatorStr.toUtf8().constData());
t.set_comment(m_comment.toUtf8().constData()); newTorrent.set_comment(m_params.comment.toUtf8().constData());
// Is private ? // Is private ?
t.set_priv(m_private); newTorrent.set_priv(m_params.isPrivate);
if (isInterruptionRequested()) return; if (isInterruptionRequested()) return;
// create the torrent and print it to out libt::entry entry = newTorrent.generate();
qDebug("Saving to %s", qUtf8Printable(m_savePath));
// 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 #ifdef _MSC_VER
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1]; Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str()
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;
#else #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 #endif
, (std::ios_base::out | std::ios_base::binary | std::ios_base::trunc));
if (outfile.fail()) if (outfile.fail())
throw std::exception(); throw std::runtime_error(tr("create new torrent file failed").toStdString());
if (isInterruptionRequested()) return; if (isInterruptionRequested()) return;
libt::bencode(std::ostream_iterator<char>(outfile), t.generate()); libt::bencode(std::ostream_iterator<char>(outfile), entry);
outfile.close(); outfile.close();
emit updateProgress(100); emit updateProgress(100);
emit creationSuccess(m_savePath, parentPath); emit creationSuccess(m_params.savePath, parentPath);
} }
catch (std::exception& e) { catch (const std::exception &e) {
emit creationFailure(QString::fromStdString(e.what())); emit creationFailure(e.what());
} }
} }

View File

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

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. // The following can be removed after one or two libtorrent releases on each branch.
namespace namespace
{ {
const char i18nContext[] = "TorrentHandle";
// new constructor is available // new constructor is available
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0> 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) T makeTorrentCreator(const libtorrent::torrent_info & ti)
@@ -1469,7 +1471,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
return; 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); .arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
m_moveStorageInfo.newPath.clear(); m_moveStorageInfo.newPath.clear();
@@ -1647,13 +1649,13 @@ void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejecte
updateStatus(); updateStatus();
if (p->error.value() == libt::errors::mismatching_file_size) { if (p->error.value() == libt::errors::mismatching_file_size) {
// Mismatching file size (files were probably moved) // 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; m_hasMissingFiles = true;
if (!isPaused()) if (!isPaused())
pause(); pause();
} }
else { 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); .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_JSON[] = "application/json";
const char CONTENT_TYPE_PNG[] = "image/png"; const char CONTENT_TYPE_PNG[] = "image/png";
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8"; 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 // portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'}; const char CRLF[] = {0x0D, 0x0A, '\0'};

View File

@@ -30,9 +30,12 @@
* Contact : hammered999@gmail.com * Contact : hammered999@gmail.com
*/ */
#include "preferences.h"
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDir> #include <QDir>
#include <QLocale> #include <QLocale>
#include <QMutableListIterator>
#include <QPair> #include <QPair>
#include <QSettings> #include <QSettings>
@@ -51,11 +54,10 @@
#include <CoreServices/CoreServices.h> #include <CoreServices/CoreServices.h>
#endif #endif
#include "logger.h"
#include "settingsstorage.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "utils/misc.h" #include "utils/misc.h"
#include "settingsstorage.h"
#include "logger.h"
#include "preferences.h"
Preferences *Preferences::m_instance = 0; Preferences *Preferences::m_instance = 0;
@@ -463,6 +465,42 @@ void Preferences::setWebUiLocalAuthEnabled(bool enabled)
setValue("Preferences/WebUI/LocalHostAuth", enabled); setValue("Preferences/WebUI/LocalHostAuth", enabled);
} }
bool Preferences::isWebUiAuthSubnetWhitelistEnabled() const
{
return value("Preferences/WebUI/AuthSubnetWhitelistEnabled", false).toBool();
}
void Preferences::setWebUiAuthSubnetWhitelistEnabled(bool enabled)
{
setValue("Preferences/WebUI/AuthSubnetWhitelistEnabled", enabled);
}
QList<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
{
QList<Utils::Net::Subnet> subnets;
foreach (const QString &rawSubnet, value("Preferences/WebUI/AuthSubnetWhitelist").toStringList()) {
bool ok = false;
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(rawSubnet.trimmed(), &ok);
if (ok)
subnets.append(subnet);
}
return subnets;
}
void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
{
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", subnets);
}
QString Preferences::getServerDomains() const QString Preferences::getServerDomains() const
{ {
return value("Preferences/WebUI/ServerDomains", "*").toString(); return value("Preferences/WebUI/ServerDomains", "*").toString();
@@ -1183,9 +1221,9 @@ void Preferences::setMainLastDir(const QString &path)
setValue("MainWindowLastDir", 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) void Preferences::setPrefSize(const QSize &size)
@@ -1263,9 +1301,9 @@ void Preferences::setPropTrackerListState(const QByteArray &state)
setValue("TorrentProperties/Trackers/qt5/TrackerListState", 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) void Preferences::setRssGeometrySize(const QSize &geometry)

View File

@@ -33,15 +33,18 @@
#ifndef PREFERENCES_H #ifndef PREFERENCES_H
#define PREFERENCES_H #define PREFERENCES_H
#include <QTime>
#include <QDateTime> #include <QDateTime>
#include <QHostAddress>
#include <QList> #include <QList>
#include <QSize>
#include <QTimer>
#include <QReadWriteLock>
#include <QNetworkCookie> #include <QNetworkCookie>
#include <QReadWriteLock>
#include <QSize>
#include <QStringList>
#include <QTime>
#include <QTimer>
#include <QVariant> #include <QVariant>
#include "base/utils/net.h"
#include "types.h" #include "types.h"
enum scheduler_days enum scheduler_days
@@ -170,10 +173,9 @@ public:
bool isSearchEnabled() const; bool isSearchEnabled() const;
void setSearchEnabled(bool enabled); void setSearchEnabled(bool enabled);
// HTTP Server
bool isWebUiEnabled() const; bool isWebUiEnabled() const;
void setWebUiEnabled(bool enabled); void setWebUiEnabled(bool enabled);
bool isWebUiLocalAuthEnabled() const;
void setWebUiLocalAuthEnabled(bool enabled);
QString getServerDomains() const; QString getServerDomains() const;
void setServerDomains(const QString &str); void setServerDomains(const QString &str);
QString getWebUiAddress() const; QString getWebUiAddress() const;
@@ -182,16 +184,28 @@ public:
void setWebUiPort(quint16 port); void setWebUiPort(quint16 port);
bool useUPnPForWebUIPort() const; bool useUPnPForWebUIPort() const;
void setUPnPForWebUIPort(bool enabled); void setUPnPForWebUIPort(bool enabled);
// Authentication
bool isWebUiLocalAuthEnabled() const;
void setWebUiLocalAuthEnabled(bool enabled);
bool isWebUiAuthSubnetWhitelistEnabled() const;
void setWebUiAuthSubnetWhitelistEnabled(bool enabled);
QList<Utils::Net::Subnet> getWebUiAuthSubnetWhitelist() const;
void setWebUiAuthSubnetWhitelist(QStringList subnets);
QString getWebUiUsername() const; QString getWebUiUsername() const;
void setWebUiUsername(const QString &username); void setWebUiUsername(const QString &username);
QString getWebUiPassword() const; QString getWebUiPassword() const;
void setWebUiPassword(const QString &new_password); void setWebUiPassword(const QString &new_password);
// HTTPS
bool isWebUiHttpsEnabled() const; bool isWebUiHttpsEnabled() const;
void setWebUiHttpsEnabled(bool enabled); void setWebUiHttpsEnabled(bool enabled);
QByteArray getWebUiHttpsCertificate() const; QByteArray getWebUiHttpsCertificate() const;
void setWebUiHttpsCertificate(const QByteArray &data); void setWebUiHttpsCertificate(const QByteArray &data);
QByteArray getWebUiHttpsKey() const; QByteArray getWebUiHttpsKey() const;
void setWebUiHttpsKey(const QByteArray &data); void setWebUiHttpsKey(const QByteArray &data);
// Dynamic DNS
bool isDynDNSEnabled() const; bool isDynDNSEnabled() const;
void setDynDNSEnabled(bool enabled); void setDynDNSEnabled(bool enabled);
DNS::Service getDynDNSService() const; DNS::Service getDynDNSService() const;
@@ -287,7 +301,7 @@ public:
void setMainVSplitterState(const QByteArray &state); void setMainVSplitterState(const QByteArray &state);
QString getMainLastDir() const; QString getMainLastDir() const;
void setMainLastDir(const QString &path); void setMainLastDir(const QString &path);
QSize getPrefSize(const QSize &defaultSize) const; QSize getPrefSize() const;
void setPrefSize(const QSize &size); void setPrefSize(const QSize &size);
QStringList getPrefHSplitterSizes() const; QStringList getPrefHSplitterSizes() const;
void setPrefHSplitterSizes(const QStringList &sizes); void setPrefHSplitterSizes(const QStringList &sizes);
@@ -303,7 +317,7 @@ public:
void setPropVisible(const bool visible); void setPropVisible(const bool visible);
QByteArray getPropTrackerListState() const; QByteArray getPropTrackerListState() const;
void setPropTrackerListState(const QByteArray &state); void setPropTrackerListState(const QByteArray &state);
QSize getRssGeometrySize(const QSize &defaultSize) const; QSize getRssGeometrySize() const;
void setRssGeometrySize(const QSize &geometry); void setRssGeometrySize(const QSize &geometry);
QByteArray getRssHSplitterSizes() const; QByteArray getRssHSplitterSizes() const;
void setRssHSplitterSizes(const QByteArray &sizes); 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 // read and create items from a rss document
void Parser::parse_impl(const QByteArray &feedData) void Parser::parse_impl(const QByteArray &feedData)
{ {
qDebug() << Q_FUNC_INFO;
QXmlStreamReader xml(feedData); QXmlStreamReader xml(feedData);
bool foundChannel = false; bool foundChannel = false;
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "rss") { if (xml.name() == "rss") {
// Find channels // Find channels
@@ -258,11 +257,15 @@ void Parser::parse_impl(const QByteArray &feedData)
} }
} }
if (xml.hasError()) if (!foundChannel) {
m_result.error = xml.errorString();
else if (!foundChannel)
m_result.error = tr("Invalid RSS feed."); 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 // Sort article list chronologically
// NOTE: We don't need to sort it here if articles are always // NOTE: We don't need to sort it here if articles are always
// sorted in fetched XML in reverse chronological order // 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(); return a1["date"].toDateTime() < a2["date"].toDateTime();
}); });
}
emit finished(m_result); emit finished(m_result);
m_result.articles.clear(); // clear articles only m_result.articles.clear(); // clear articles only
@@ -288,35 +292,34 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
break; break;
if (xml.isStartElement()) { if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) { if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text; article[Article::KeyTitle] = xml.readElementText().trimmed();
} }
else if (name == QLatin1String("enclosure")) { else if (name == QLatin1String("enclosure")) {
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent")) if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString(); article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
} }
else if (name == QLatin1String("link")) { else if (name == QLatin1String("link")) {
const QString text {xml.readElementText().trimmed()};
if (text.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive)) if (text.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
article[Article::KeyTorrentURL] = text; // magnet link instead of a news URL article[Article::KeyTorrentURL] = text; // magnet link instead of a news URL
else else
article[Article::KeyLink] = text; article[Article::KeyLink] = text;
} }
else if (name == QLatin1String("description")) { else if (name == QLatin1String("description")) {
article[Article::KeyDescription] = text; article[Article::KeyDescription] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} }
else if (name == QLatin1String("pubDate")) { else if (name == QLatin1String("pubDate")) {
article[Article::KeyDate] = parseDate(text); article[Article::KeyDate] = parseDate(xml.readElementText().trimmed());
} }
else if (name == QLatin1String("author")) { else if (name == QLatin1String("author")) {
article[Article::KeyAuthor] = text; article[Article::KeyAuthor] = xml.readElementText().trimmed();
} }
else if (name == QLatin1String("guid")) { else if (name == QLatin1String("guid")) {
article[Article::KeyId] = text; article[Article::KeyId] = xml.readElementText().trimmed();
} }
else { else {
article[name] = text; article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} }
} }
} }
@@ -326,17 +329,14 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
void Parser::parseRSSChannel(QXmlStreamReader &xml) void Parser::parseRSSChannel(QXmlStreamReader &xml)
{ {
qDebug() << Q_FUNC_INFO;
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
while (!xml.atEnd()) { while (!xml.atEnd()) {
xml.readNext(); xml.readNext();
if (xml.isStartElement()) { if (xml.isStartElement()) {
if (xml.name() == "title") { if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText(); m_result.title = xml.readElementText();
} }
else if (xml.name() == "lastBuildDate") { else if (xml.name() == QLatin1String("lastBuildDate")) {
QString lastBuildDate = xml.readElementText(); QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) { if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) { if (m_result.lastBuildDate == lastBuildDate) {
@@ -346,7 +346,7 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate; m_result.lastBuildDate = lastBuildDate;
} }
} }
else if (xml.name() == "item") { else if (xml.name() == QLatin1String("item")) {
parseRssArticle(xml); parseRssArticle(xml);
} }
} }
@@ -366,14 +366,12 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
break; break;
if (xml.isStartElement()) { if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) { if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text; article[Article::KeyTitle] = xml.readElementText().trimmed();
} }
else if (name == QLatin1String("link")) { else if (name == QLatin1String("link")) {
QString link = (xml.attributes().isEmpty() QString link = (xml.attributes().isEmpty()
? text ? xml.readElementText().trimmed()
: xml.attributes().value(QLatin1String("href")).toString()); : xml.attributes().value(QLatin1String("href")).toString());
if (link.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive)) 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); 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 if (doubleContent) { // Duplicate content -> ignore
xml.readNext(); xml.skipCurrentElement();
while ((xml.name() != QLatin1String("summary")) && (xml.name() != QLatin1String("content")))
xml.readNext();
continue; continue;
} }
// Try to also parse broken articles, which don't use html '&' escapes // Try to also parse broken articles, which don't use html '&' escapes
// Actually works great for non-broken content too // Actually works great for non-broken content too
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements); QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements).trimmed();
if (!feedText.isEmpty()) if (!feedText.isEmpty()) {
article[Article::KeyDescription] = feedText.trimmed(); article[Article::KeyDescription] = feedText;
doubleContent = true;
doubleContent = true; }
} }
else if (name == QLatin1String("updated")) { else if (name == QLatin1String("updated")) {
// ATOM uses standard compliant date, don't do fancy stuff // 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()); article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime());
} }
else if (name == QLatin1String("author")) { else if (name == QLatin1String("author")) {
xml.readNext(); while (xml.readNextStartElement()) {
while (xml.name() != QLatin1String("author")) {
if (xml.name() == QLatin1String("name")) if (xml.name() == QLatin1String("name"))
article[Article::KeyAuthor] = xml.readElementText().trimmed(); article[Article::KeyAuthor] = xml.readElementText().trimmed();
xml.readNext(); else
xml.skipCurrentElement();
} }
} }
else if (name == QLatin1String("id")) { else if (name == QLatin1String("id")) {
article[Article::KeyId] = text; article[Article::KeyId] = xml.readElementText().trimmed();
} }
else { else {
article[name] = text; article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
} }
} }
} }
@@ -430,19 +424,16 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
void Parser::parseAtomChannel(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(); m_baseUrl = xml.attributes().value("xml:base").toString();
while (!xml.atEnd()) { while (!xml.atEnd()) {
xml.readNext(); xml.readNext();
if (xml.isStartElement()) { if (xml.isStartElement()) {
if (xml.name() == "title") { if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText(); m_result.title = xml.readElementText();
} }
else if (xml.name() == "updated") { else if (xml.name() == QLatin1String("updated")) {
QString lastBuildDate = xml.readElementText(); QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) { if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) { if (m_result.lastBuildDate == lastBuildDate) {
@@ -452,7 +443,7 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate; m_result.lastBuildDate = lastBuildDate;
} }
} }
else if (xml.name() == "entry") { else if (xml.name() == QLatin1String("entry")) {
parseAtomArticle(xml); parseAtomArticle(xml);
} }
} }

View File

@@ -28,6 +28,7 @@
#include "rss_autodownloader.h" #include "rss_autodownloader.h"
#include <QDataStream>
#include <QDebug> #include <QDebug>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
@@ -37,6 +38,7 @@
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QVariant> #include <QVariant>
#include <QVector>
#include "../bittorrent/magneturi.h" #include "../bittorrent/magneturi.h"
#include "../bittorrent/session.h" #include "../bittorrent/session.h"
@@ -63,6 +65,32 @@ const QString RulesFileName(QStringLiteral("download_rules.json"));
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing")); 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; using namespace RSS;
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr; QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
@@ -84,8 +112,8 @@ AutoDownloader::AutoDownloader()
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater); connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
connect(m_fileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString) 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") LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
.arg(fileName).arg(errorString), Log::WARNING); .arg(fileName).arg(errorString), Log::CRITICAL);
}); });
m_ioThread->start(); 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() void AutoDownloader::process()
{ {
if (m_processingQueue.isEmpty()) return; // processing was disabled if (m_processingQueue.isEmpty()) return; // processing was disabled
@@ -276,39 +368,20 @@ void AutoDownloader::load()
else if (rulesFile.open(QFile::ReadOnly)) else if (rulesFile.open(QFile::ReadOnly))
loadRules(rulesFile.readAll()); loadRules(rulesFile.readAll());
else else
Logger::instance()->addMessage( LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
QString("Couldn't read RSS AutoDownloader rules from %1. Error: %2") .arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::CRITICAL);
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::WARNING);
} }
void AutoDownloader::loadRules(const QByteArray &data) void AutoDownloader::loadRules(const QByteArray &data)
{ {
QJsonParseError jsonError; try {
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); const auto rules = rulesFromJSON(data);
if (jsonError.error != QJsonParseError::NoError) { for (const auto &rule : rules)
Logger::instance()->addMessage( setRule_impl(rule);
QString("Couldn't parse RSS AutoDownloader rules. Error: %1")
.arg(jsonError.errorString()), Log::WARNING);
return;
} }
catch (const ParsingError &error) {
if (!jsonDoc.isObject()) { LogMsg(tr("Couldn't load RSS AutoDownloader rules. Reason: %1")
Logger::instance()->addMessage( .arg(error.message()), Log::CRITICAL);
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));
} }
} }
@@ -317,7 +390,7 @@ void AutoDownloader::loadRulesLegacy()
SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss")); SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss"));
QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash(); QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
foreach (const QVariant &ruleVar, rules) { foreach (const QVariant &ruleVar, rules) {
auto rule = AutoDownloadRule::fromVariantHash(ruleVar.toHash()); auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
if (!rule.name().isEmpty()) if (!rule.name().isEmpty())
insertRule(rule); insertRule(rule);
} }
@@ -351,6 +424,8 @@ bool AutoDownloader::isProcessingEnabled() const
void AutoDownloader::resetProcessingQueue() void AutoDownloader::resetProcessingQueue()
{ {
m_processingQueue.clear(); m_processingQueue.clear();
if (!m_processingEnabled) return;
foreach (Article *article, Session::instance()->rootFolder()->articles()) { foreach (Article *article, Session::instance()->rootFolder()->articles()) {
if (!article->isRead() && !article->torrentUrl().isEmpty()) if (!article->isRead() && !article->torrentUrl().isEmpty())
addJobForArticle(article); addJobForArticle(article);
@@ -385,3 +460,13 @@ void AutoDownloader::timerEvent(QTimerEvent *event)
Q_UNUSED(event); Q_UNUSED(event);
store(); 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 #pragma once
#include <stdexcept>
#include <QBasicTimer> #include <QBasicTimer>
#include <QHash> #include <QHash>
#include <QList> #include <QList>
@@ -49,6 +51,13 @@ namespace RSS
class AutoDownloadRule; class AutoDownloadRule;
class ParsingError : public std::runtime_error
{
public:
explicit ParsingError(const QString &message);
QString message() const;
};
class AutoDownloader final: public QObject class AutoDownloader final: public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -60,6 +69,12 @@ namespace RSS
~AutoDownloader() override; ~AutoDownloader() override;
public: public:
enum class RulesFileFormat
{
Legacy,
JSON
};
static AutoDownloader *instance(); static AutoDownloader *instance();
bool isProcessingEnabled() const; bool isProcessingEnabled() const;
@@ -73,6 +88,9 @@ namespace RSS
bool renameRule(const QString &ruleName, const QString &newRuleName); bool renameRule(const QString &ruleName, const QString &newRuleName);
void removeRule(const QString &ruleName); void removeRule(const QString &ruleName);
QByteArray exportRules(RulesFileFormat format = RulesFileFormat::JSON) const;
void importRules(const QByteArray &data, RulesFileFormat format = RulesFileFormat::JSON);
signals: signals:
void processingStateChanged(bool enabled); void processingStateChanged(bool enabled);
void ruleAdded(const QString &ruleName); void ruleAdded(const QString &ruleName);
@@ -98,6 +116,10 @@ namespace RSS
void loadRulesLegacy(); void loadRulesLegacy();
void store(); void store();
void storeDeferred(); void storeDeferred();
QByteArray exportRulesToJSONFormat() const;
void importRulesFromJSONFormat(const QByteArray &data);
QByteArray exportRulesToLegacyFormat() const;
void importRulesFromLegacyFormat(const QByteArray &data);
static QPointer<AutoDownloader> m_instance; static QPointer<AutoDownloader> m_instance;

View File

@@ -63,11 +63,29 @@ namespace
QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool) QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool)
{ {
switch (static_cast<int>(triStateBool)) { switch (static_cast<int>(triStateBool)) {
case 0: return false; break; case 0: return false;
case 1: return true; break; case 1: return true;
default: return QJsonValue(); 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")); const QString Str_Name(QStringLiteral("name"));
@@ -378,21 +396,37 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
return rule; 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()); AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
rule.setMustContain(varHash.value("must_contain").toString()); {
rule.setMustNotContain(varHash.value("must_not_contain").toString()); AutoDownloadRule rule(dict.value("name").toString());
rule.setEpisodeFilter(varHash.value("episode_filter").toString());
rule.setFeedURLs(varHash.value("affected_feeds").toStringList()); rule.setUseRegex(dict.value("use_regex", false).toBool());
rule.setEnabled(varHash.value("enabled", false).toBool()); rule.setMustContain(dict.value("must_contain").toString());
rule.setSavePath(varHash.value("save_path").toString()); rule.setMustNotContain(dict.value("must_not_contain").toString());
rule.setCategory(varHash.value("category_assigned").toString()); rule.setEpisodeFilter(dict.value("episode_filter").toString());
rule.setAddPaused(TriStateBool(varHash.value("add_paused").toInt() - 1)); rule.setFeedURLs(dict.value("affected_feeds").toStringList());
rule.setLastMatch(varHash.value("last_match").toDateTime()); rule.setEnabled(dict.value("enabled", false).toBool());
rule.setIgnoreDays(varHash.value("ignore_days").toInt()); 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; return rule;
} }

View File

@@ -84,7 +84,9 @@ namespace RSS
QJsonObject toJsonObject() const; QJsonObject toJsonObject() const;
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = ""); 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: private:
bool matches(const QString &articleTitle, const QString &expression) const; bool matches(const QString &articleTitle, const QString &expression) const;

View File

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

View File

@@ -102,6 +102,27 @@ Session::Session()
m_refreshTimer.start(m_refreshInterval * MsecsPerMin); m_refreshTimer.start(m_refreshInterval * MsecsPerMin);
refresh(); 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() Session::~Session()
@@ -163,7 +184,7 @@ bool Session::moveItem(const QString &itemPath, const QString &destPath, QString
auto item = m_itemsByPath.value(itemPath); auto item = m_itemsByPath.value(itemPath);
if (!item) { if (!item) {
if (error) if (error)
*error = tr("Item doesn't exists: %1.").arg(itemPath); *error = tr("Item doesn't exist: %1.").arg(itemPath);
return false; return false;
} }
@@ -201,7 +222,7 @@ bool Session::removeItem(const QString &itemPath, QString *error)
auto item = m_itemsByPath.value(itemPath); auto item = m_itemsByPath.value(itemPath);
if (!item) { if (!item) {
if (error) if (error)
*error = tr("Item doesn't exists: %1.").arg(itemPath); *error = tr("Item doesn't exist: %1.").arg(itemPath);
return false; return false;
} }
@@ -295,20 +316,6 @@ void Session::loadFolder(const QJsonObject &jsonObj, Folder *folder)
void Session::loadLegacy() 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 legacyFeedPaths = SettingsStorage::instance()->loadValue("Rss/streamList").toStringList();
const QStringList feedAliases = SettingsStorage::instance()->loadValue("Rss/streamAlias").toStringList(); const QStringList feedAliases = SettingsStorage::instance()->loadValue("Rss/streamAlias").toStringList();
if (legacyFeedPaths.size() != feedAliases.size()) { if (legacyFeedPaths.size() != feedAliases.size()) {

View File

@@ -632,19 +632,6 @@ void Utils::Misc::openFolderSelect(const QString &absolutePath)
#endif #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 #endif // DISABLE_GUI
QString Utils::Misc::osName() QString Utils::Misc::osName()

View File

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

View File

@@ -29,6 +29,7 @@
#include "net.h" #include "net.h"
#include <QHostAddress> #include <QHostAddress>
#include <QString> #include <QString>
#include <QStringList>
namespace Utils namespace Utils
{ {
@@ -38,5 +39,55 @@ namespace Utils
{ {
return !QHostAddress(ip).isNull(); return !QHostAddress(ip).isNull();
} }
Subnet parseSubnet(const QString &subnetStr, bool *ok)
{
const Subnet invalid = qMakePair(QHostAddress(), -1);
const Subnet subnet = QHostAddress::parseSubnet(subnetStr);
if (ok)
*ok = (subnet != invalid);
return subnet;
}
bool canParseSubnet(const QString &subnetStr)
{
bool ok = false;
parseSubnet(subnetStr, &ok);
return ok;
}
bool isLoopbackAddress(const QHostAddress &addr)
{
return (addr == QHostAddress::LocalHost)
|| (addr == QHostAddress::LocalHostIPv6)
|| (addr == QHostAddress(QLatin1String("::ffff:127.0.0.1")));
}
bool isIPInRange(const QHostAddress &addr, const QList<Subnet> &subnets)
{
QHostAddress protocolEquivalentAddress;
bool addrConversionOk = false;
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
// always succeeds
protocolEquivalentAddress = QHostAddress(addr.toIPv6Address());
addrConversionOk = true;
}
else {
// only succeeds when addr is an ipv4-mapped ipv6 address
protocolEquivalentAddress = QHostAddress(addr.toIPv4Address(&addrConversionOk));
}
for (const Subnet &subnet : subnets)
if (addr.isInSubnet(subnet) || (addrConversionOk && protocolEquivalentAddress.isInSubnet(subnet)))
return true;
return false;
}
QString subnetToString(const Subnet &subnet)
{
return subnet.first.toString() + '/' + QString::number(subnet.second);
}
} }
} }

View File

@@ -28,13 +28,26 @@
#ifndef BASE_UTILS_NET_H #ifndef BASE_UTILS_NET_H
#define BASE_UTILS_NET_H #define BASE_UTILS_NET_H
#include <QList>
#include <QPair>
class QHostAddress;
class QString; class QString;
class QStringList;
namespace Utils namespace Utils
{ {
namespace Net namespace Net
{ {
using Subnet = QPair<QHostAddress, int>;
bool isValidIP(const QString &ip); bool isValidIP(const QString &ip);
Subnet parseSubnet(const QString &subnetStr, bool *ok = nullptr);
bool canParseSubnet(const QString &subnetStr);
bool isLoopbackAddress(const QHostAddress &addr);
bool isIPInRange(const QHostAddress &addr, const QList<Subnet> &subnets);
QString subnetToString(const Subnet &subnet);
} }
} }

View File

@@ -33,9 +33,9 @@
#include <QByteArray> #include <QByteArray>
#include <QCollator> #include <QCollator>
#include <QtGlobal>
#include <QLocale> #include <QLocale>
#include <QRegExp> #include <QRegExp>
#include <QtGlobal>
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
#include <QThreadStorage> #include <QThreadStorage>
#endif #endif
@@ -45,110 +45,106 @@ namespace
class NaturalCompare class NaturalCompare
{ {
public: public:
explicit NaturalCompare(const bool caseSensitive = true) explicit NaturalCompare(const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
: m_caseSensitive(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 // 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 // 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. // 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead. // So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240 // See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7 // Without ICU library, QCollator doesn't support `setNumericMode(true)` on an OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7) #else
return;
#endif
m_collator.setNumericMode(true); m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); m_collator.setCaseSensitivity(caseSensitivity);
}
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);
#endif #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 #ifdef Q_OS_WIN
int posL = 0; return compare(left, right);
int posR = 0; #else
while (true) { return m_collator.compare(left, right);
while (true) { #endif
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;
} }
private: 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; 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 // provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html // 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 #ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp; static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(true)); if (!nCmp.hasLocalData())
return (nCmp.localData())(left, right); nCmp.setLocalData(NaturalCompare(Qt::CaseSensitive));
return (nCmp.localData())(left, right);
#else #else
thread_local NaturalCompare nCmp(true); thread_local NaturalCompare nCmp(Qt::CaseSensitive);
return nCmp(left, right); return nCmp(left, right);
#endif #endif
} }
bool Utils::String::naturalCompareCaseInsensitive(const QString &left, const QString &right) #ifdef Q_OS_MAC
{
// 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
static QThreadStorage<NaturalCompare> nCmp; static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(false)); if (!nCmp.hasLocalData())
nCmp.setLocalData(NaturalCompare(Qt::CaseInsensitive));
return (nCmp.localData())(left, right); return (nCmp.localData())(left, right);
#else #else
thread_local NaturalCompare nCmp(false); thread_local NaturalCompare nCmp(Qt::CaseInsensitive);
return nCmp(left, right); return nCmp(left, right);
#endif #endif
} }
@@ -188,4 +184,3 @@ QString Utils::String::wildcardToRegex(const QString &pattern)
{ {
return qt_regexp_toCanonical(pattern, QRegExp::Wildcard); return qt_regexp_toCanonical(pattern, QRegExp::Wildcard);
} }

View File

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

View File

@@ -30,7 +30,6 @@ set(QBT_GUI_HEADERS
about_imp.h about_imp.h
addnewtorrentdialog.h addnewtorrentdialog.h
advancedsettings.h advancedsettings.h
advancedsettings.h
autoexpandabledialog.h autoexpandabledialog.h
banlistoptions.h banlistoptions.h
categoryfiltermodel.h categoryfiltermodel.h
@@ -45,6 +44,7 @@ fspathedit.h
fspathedit_p.h fspathedit_p.h
guiiconprovider.h guiiconprovider.h
hidabletabwidget.h hidabletabwidget.h
ipsubnetwhitelistoptionsdialog.h
loglistwidget.h loglistwidget.h
mainwindow.h mainwindow.h
messageboxraised.h messageboxraised.h
@@ -74,6 +74,7 @@ transferlistfilterswidget.h
transferlistsortmodel.h transferlistsortmodel.h
transferlistwidget.h transferlistwidget.h
updownratiodlg.h updownratiodlg.h
utils.h
) )
set(QBT_GUI_SOURCES set(QBT_GUI_SOURCES
@@ -90,6 +91,7 @@ executionlog.cpp
fspathedit.cpp fspathedit.cpp
fspathedit_p.cpp fspathedit_p.cpp
guiiconprovider.cpp guiiconprovider.cpp
ipsubnetwhitelistoptionsdialog.cpp
loglistwidget.cpp loglistwidget.cpp
mainwindow.cpp mainwindow.cpp
messageboxraised.cpp messageboxraised.cpp
@@ -118,6 +120,7 @@ transferlistfilterswidget.cpp
transferlistsortmodel.cpp transferlistsortmodel.cpp
transferlistwidget.cpp transferlistwidget.cpp
updownratiodlg.cpp updownratiodlg.cpp
utils.cpp
) )
if (APPLE) if (APPLE)
@@ -135,6 +138,7 @@ mainwindow.ui
about.ui about.ui
banlistoptions.ui banlistoptions.ui
cookiesdialog.ui cookiesdialog.ui
ipsubnetwhitelistoptionsdialog.ui
previewselectdialog.ui previewselectdialog.ui
login.ui login.ui
downloadfromurldlg.ui downloadfromurldlg.ui

View File

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

View File

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

View File

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

View File

@@ -32,6 +32,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_autoexpandabledialog.h" #include "ui_autoexpandabledialog.h"
#include "utils.h"
AutoExpandableDialog::AutoExpandableDialog(QWidget *parent) AutoExpandableDialog::AutoExpandableDialog(QWidget *parent)
: QDialog(parent) : QDialog(parent)
@@ -68,30 +69,29 @@ QString AutoExpandableDialog::getText(QWidget *parent, const QString &title, con
void AutoExpandableDialog::showEvent(QShowEvent *e) void AutoExpandableDialog::showEvent(QShowEvent *e)
{ {
// Overriding showEvent is required for consistent UI with fixed size under custom DPI // Overriding showEvent is required for consistent UI with fixed size under custom DPI
// Show dialog
QDialog::showEvent(e); QDialog::showEvent(e);
// and resize textbox to fit the text
// NOTE: For some strange reason QFontMetrics gets more accurate // Show dialog and resize textbox to fit the text
// when called from showEvent. Only 6 symbols off instead of 11 symbols off. // NOTE: For unknown reason QFontMetrics gets more accurate when called from showEvent.
int textW = m_ui->textEdit->fontMetrics().width(m_ui->textEdit->text()) + 4; int wd = m_ui->textEdit->fontMetrics().width(m_ui->textEdit->text()) + 4;
int wd = textW;
if (!windowTitle().isEmpty()) { if (!windowTitle().isEmpty()) {
int w = fontMetrics().width(windowTitle()); // not really the font metrics in window title, so we enlarge it a bit,
if (w > wd) // including the small icon and close button width
wd = w; int w = fontMetrics().width(windowTitle()) * 1.8;
wd = std::max(wd, w);
} }
if (!m_ui->textLabel->text().isEmpty()) { if (!m_ui->textLabel->text().isEmpty()) {
int w = m_ui->textLabel->fontMetrics().width(m_ui->textLabel->text()); int w = m_ui->textLabel->fontMetrics().width(m_ui->textLabel->text());
if (w > wd) wd = std::max(wd, w);
wd = w;
} }
// Now resize the dialog to fit the contents // Now resize the dialog to fit the contents
// max width of text from either of: label, title, textedit // max width of text from either of: label, title, textedit
// If the value is less than dialog default size, default size is used // If the value is less than dialog default size, default size is used
if (wd > width()) if (wd > width()) {
resize(width() - m_ui->verticalLayout->sizeHint().width() + wd, height()); 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/bittorrent/session.h"
#include "base/utils/net.h" #include "base/utils/net.h"
#include "ui_banlistoptions.h" #include "ui_banlistoptions.h"
#include "utils.h"
BanListOptions::BanListOptions(QWidget *parent) BanListOptions::BanListOptions(QWidget *parent)
: QDialog(parent) : QDialog(parent)
@@ -52,6 +53,8 @@ BanListOptions::BanListOptions(QWidget *parent)
m_ui->bannedIPList->setModel(m_sortFilter); m_ui->bannedIPList->setModel(m_sortFilter);
m_ui->bannedIPList->sortByColumn(0, Qt::AscendingOrder); m_ui->bannedIPList->sortByColumn(0, Qt::AscendingOrder);
m_ui->buttonBanIP->setEnabled(false); m_ui->buttonBanIP->setEnabled(false);
Utils::Gui::resize(this);
} }
BanListOptions::~BanListOptions() BanListOptions::~BanListOptions()
@@ -70,8 +73,11 @@ void BanListOptions::on_buttonBox_accepted()
IPList << index.data().toString(); IPList << index.data().toString();
} }
BitTorrent::Session::instance()->setBannedIPs(IPList); BitTorrent::Session::instance()->setBannedIPs(IPList);
QDialog::accept();
}
else {
QDialog::reject();
} }
QDialog::accept();
} }
void BanListOptions::on_buttonBanIP_clicked() void BanListOptions::on_buttonBanIP_clicked()

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,11 +33,13 @@
#include <QDialog> #include <QDialog>
#include <QPushButton> #include <QPushButton>
#include "ui_confirmdeletiondlg.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "ui_confirmdeletiondlg.h"
#include "utils.h"
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
Q_OBJECT Q_OBJECT
@@ -50,13 +52,17 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
else 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))); 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 // Icons
lbl_warn->setPixmap(GuiIconProvider::instance()->getIcon("dialog-warning").pixmap(lbl_warn->height())); const QSize iconSize = Utils::Gui::largeIconSize();
lbl_warn->setFixedWidth(lbl_warn->height()); lbl_warn->setPixmap(GuiIconProvider::instance()->getIcon("dialog-warning").pixmap(iconSize));
lbl_warn->setFixedWidth(iconSize.width());
rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked")); rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked"));
rememberBtn->setIconSize(Utils::Gui::mediumIconSize());
checkPermDelete->setChecked(defaultDeleteFiles || Preferences::instance()->deleteTorrentFilesAsDefault()); checkPermDelete->setChecked(defaultDeleteFiles || Preferences::instance()->deleteTorrentFilesAsDefault());
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState())); connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
buttonBox->button(QDialogButtonBox::Cancel)->setFocus(); buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
Utils::Gui::resize(this);
} }
bool shouldDeleteLocalFiles() const { bool shouldDeleteLocalFiles() const {

View File

@@ -40,6 +40,7 @@
#include <QStringList> #include <QStringList>
#include "ui_downloadfromurldlg.h" #include "ui_downloadfromurldlg.h"
#include "utils.h"
class downloadFromURL : public QDialog, private Ui::downloadFromURL class downloadFromURL : public QDialog, private Ui::downloadFromURL
{ {
@@ -60,7 +61,7 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL
// Paste clipboard if there is an URL in it // Paste clipboard if there is an URL in it
QString clip_txt = qApp->clipboard()->text(); QString clip_txt = qApp->clipboard()->text();
QStringList clip_txt_list = clip_txt.split(QString::fromUtf8("\n")); QStringList clip_txt_list = clip_txt.split(QLatin1Char('\n'));
clip_txt.clear(); clip_txt.clear();
QStringList clip_txt_list_cleaned; QStringList clip_txt_list_cleaned;
foreach (clip_txt, clip_txt_list) { foreach (clip_txt, clip_txt_list) {
@@ -82,6 +83,7 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL
if (clip_txt_list_cleaned.size() > 0) if (clip_txt_list_cleaned.size() > 0)
textUrls->setText(clip_txt_list_cleaned.join("\n")); textUrls->setText(clip_txt_list_cleaned.join("\n"));
Utils::Gui::resize(this);
show(); show();
} }
@@ -94,7 +96,7 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL
void downloadButtonClicked() void downloadButtonClicked()
{ {
QString urls = textUrls->toPlainText(); QString urls = textUrls->toPlainText();
QStringList url_list = urls.split(QString::fromUtf8("\n")); QStringList url_list = urls.split(QLatin1Char('\n'));
QString url; QString url;
QStringList url_list_cleaned; QStringList url_list_cleaned;
foreach (url, url_list) { foreach (url, url_list) {

View File

@@ -30,6 +30,7 @@
#include <QAction> #include <QAction>
#include <QApplication> #include <QApplication>
#include <QCoreApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QStyle> #include <QStyle>
@@ -40,6 +41,7 @@
namespace namespace
{ {
const char i18nContext[] = "FileSystemPathEdit";
struct TrStringWithComment struct TrStringWithComment
{ {
const char *source; const char *source;
@@ -47,18 +49,18 @@ namespace
QString tr() const QString tr() const
{ {
return QObject::tr(source, comment); return QCoreApplication::translate(i18nContext, source, comment);
} }
}; };
constexpr TrStringWithComment browseButtonBriefText = 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 = 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 = 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 = 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 class FileSystemPathEdit::FileSystemPathEditPrivate
@@ -154,7 +156,9 @@ void FileSystemPathEdit::FileSystemPathEditPrivate::modeChanged()
switch (m_mode) { switch (m_mode) {
case FileSystemPathEdit::Mode::FileOpen: case FileSystemPathEdit::Mode::FileOpen:
case FileSystemPathEdit::Mode::FileSave: case FileSystemPathEdit::Mode::FileSave:
pixmap = QStyle::SP_DialogOpenButton; #ifdef Q_OS_WIN
pixmap = QStyle::SP_DirOpenIcon;
#endif
showDirsOnly = false; showDirsOnly = false;
break; break;
case FileSystemPathEdit::Mode::DirectoryOpen: case FileSystemPathEdit::Mode::DirectoryOpen:

View File

@@ -1,122 +1,126 @@
INCLUDEPATH += $$PWD INCLUDEPATH += $$PWD
include(lineedit/lineedit.pri) include(lineedit/lineedit.pri)
include(properties/properties.pri)
include(powermanagement/powermanagement.pri) include(powermanagement/powermanagement.pri)
include(properties/properties.pri)
unix:!macx:dbus: include(qtnotify/qtnotify.pri) unix:!macx:dbus: include(qtnotify/qtnotify.pri)
HEADERS += \ 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/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/addnewtorrentdialog.h \
$$PWD/advancedsettings.h \
$$PWD/autoexpandabledialog.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/messageboxraised.h \
$$PWD/optionsdlg.h \ $$PWD/optionsdlg.h \
$$PWD/advancedsettings.h \ $$PWD/previewlistdelegate.h \
$$PWD/shutdownconfirmdlg.h \ $$PWD/previewselectdialog.h \
$$PWD/torrentmodel.h \ $$PWD/rss/articlelistwidget.h \
$$PWD/torrentcreatordlg.h \ $$PWD/rss/automatedrssdownloader.h \
$$PWD/rss/feedlistwidget.h \
$$PWD/rss/htmlbrowser.h \
$$PWD/rss/rsswidget.h \
$$PWD/scanfoldersdelegate.h \ $$PWD/scanfoldersdelegate.h \
$$PWD/search/searchwidget.h \
$$PWD/search/searchtab.h \
$$PWD/search/pluginselectdlg.h \ $$PWD/search/pluginselectdlg.h \
$$PWD/search/pluginsourcedlg.h \ $$PWD/search/pluginsourcedlg.h \
$$PWD/search/searchlistdelegate.h \ $$PWD/search/searchlistdelegate.h \
$$PWD/search/searchsortmodel.h \ $$PWD/search/searchsortmodel.h \
$$PWD/cookiesmodel.h \ $$PWD/search/searchtab.h \
$$PWD/cookiesdialog.h \ $$PWD/search/searchwidget.h \
$$PWD/categoryfiltermodel.h \ $$PWD/shutdownconfirmdlg.h \
$$PWD/categoryfilterproxymodel.h \ $$PWD/speedlimitdlg.h \
$$PWD/categoryfilterwidget.h \ $$PWD/statsdialog.h \
$$PWD/statusbar.h \
$$PWD/tagfiltermodel.h \ $$PWD/tagfiltermodel.h \
$$PWD/tagfilterproxymodel.h \ $$PWD/tagfilterproxymodel.h \
$$PWD/tagfilterwidget.h \ $$PWD/tagfilterwidget.h \
$$PWD/banlistoptions.h \ $$PWD/torrentcategorydialog.h \
$$PWD/rss/rsswidget.h \ $$PWD/torrentcontentfiltermodel.h \
$$PWD/rss/articlelistwidget.h \ $$PWD/torrentcontentmodel.h \
$$PWD/rss/feedlistwidget.h \ $$PWD/torrentcontentmodelfile.h \
$$PWD/rss/automatedrssdownloader.h \ $$PWD/torrentcontentmodelfolder.h \
$$PWD/rss/htmlbrowser.h \ $$PWD/torrentcontentmodelitem.h \
$$PWD/fspathedit.h \ $$PWD/torrentcontenttreeview.h \
$$PWD/fspathedit_p.h \ $$PWD/torrentcreatordlg.h \
$$PWD/previewselectdialog.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 += \ 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/addnewtorrentdialog.cpp \
$$PWD/autoexpandabledialog.cpp \
$$PWD/statsdialog.cpp \
$$PWD/messageboxraised.cpp \
$$PWD/statusbar.cpp \
$$PWD/advancedsettings.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/optionsdlg.cpp \
$$PWD/shutdownconfirmdlg.cpp \ $$PWD/previewselectdialog.cpp \
$$PWD/torrentmodel.cpp \ $$PWD/rss/articlelistwidget.cpp \
$$PWD/torrentcreatordlg.cpp \ $$PWD/rss/automatedrssdownloader.cpp \
$$PWD/rss/feedlistwidget.cpp \
$$PWD/rss/htmlbrowser.cpp \
$$PWD/rss/rsswidget.cpp \
$$PWD/scanfoldersdelegate.cpp \ $$PWD/scanfoldersdelegate.cpp \
$$PWD/search/searchwidget.cpp \
$$PWD/search/searchtab.cpp \
$$PWD/search/pluginselectdlg.cpp \ $$PWD/search/pluginselectdlg.cpp \
$$PWD/search/pluginsourcedlg.cpp \ $$PWD/search/pluginsourcedlg.cpp \
$$PWD/search/searchlistdelegate.cpp \ $$PWD/search/searchlistdelegate.cpp \
$$PWD/search/searchsortmodel.cpp \ $$PWD/search/searchsortmodel.cpp \
$$PWD/cookiesmodel.cpp \ $$PWD/search/searchtab.cpp \
$$PWD/cookiesdialog.cpp \ $$PWD/search/searchwidget.cpp \
$$PWD/categoryfiltermodel.cpp \ $$PWD/shutdownconfirmdlg.cpp \
$$PWD/categoryfilterproxymodel.cpp \ $$PWD/speedlimitdlg.cpp \
$$PWD/categoryfilterwidget.cpp \ $$PWD/statsdialog.cpp \
$$PWD/statusbar.cpp \
$$PWD/tagfiltermodel.cpp \ $$PWD/tagfiltermodel.cpp \
$$PWD/tagfilterproxymodel.cpp \ $$PWD/tagfilterproxymodel.cpp \
$$PWD/tagfilterwidget.cpp \ $$PWD/tagfilterwidget.cpp \
$$PWD/banlistoptions.cpp \ $$PWD/torrentcategorydialog.cpp \
$$PWD/rss/rsswidget.cpp \ $$PWD/torrentcontentfiltermodel.cpp \
$$PWD/rss/articlelistwidget.cpp \ $$PWD/torrentcontentmodel.cpp \
$$PWD/rss/feedlistwidget.cpp \ $$PWD/torrentcontentmodelfile.cpp \
$$PWD/rss/automatedrssdownloader.cpp \ $$PWD/torrentcontentmodelfolder.cpp \
$$PWD/rss/htmlbrowser.cpp \ $$PWD/torrentcontentmodelitem.cpp \
$$PWD/fspathedit.cpp \ $$PWD/torrentcontenttreeview.cpp \
$$PWD/fspathedit_p.cpp \ $$PWD/torrentcreatordlg.cpp \
$$PWD/previewselectdialog.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 { win32|macx {
HEADERS += $$PWD/programupdater.h HEADERS += $$PWD/programupdater.h
@@ -129,29 +133,30 @@ macx {
} }
FORMS += \ FORMS += \
$$PWD/mainwindow.ui \
$$PWD/about.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/addnewtorrentdialog.ui \
$$PWD/autoexpandabledialog.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/optionsdlg.ui \
$$PWD/torrentcreatordlg.ui \ $$PWD/previewselectdialog.ui \
$$PWD/search/searchwidget.ui \ $$PWD/rss/automatedrssdownloader.ui \
$$PWD/rss/rsswidget.ui \
$$PWD/search/pluginselectdlg.ui \ $$PWD/search/pluginselectdlg.ui \
$$PWD/search/pluginsourcedlg.ui \ $$PWD/search/pluginsourcedlg.ui \
$$PWD/search/searchtab.ui \ $$PWD/search/searchtab.ui \
$$PWD/cookiesdialog.ui \ $$PWD/search/searchwidget.ui \
$$PWD/banlistoptions.ui \ $$PWD/shutdownconfirmdlg.ui \
$$PWD/rss/rsswidget.ui \ $$PWD/statsdialog.ui \
$$PWD/rss/automatedrssdownloader.ui \ $$PWD/torrentcategorydialog.ui \
$$PWD/torrentcategorydialog.ui $$PWD/torrentcreatordlg.ui \
$$PWD/updownratiodlg.ui
RESOURCES += $$PWD/about.qrc RESOURCES += $$PWD/about.qrc

View File

@@ -0,0 +1,112 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Thomas Piccirello <thomas@piccirello.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "ipsubnetwhitelistoptionsdialog.h"
#include <QHostAddress>
#include <QMessageBox>
#include <QPair>
#include <QSortFilterProxyModel>
#include <QStringListModel>
#include "base/preferences.h"
#include "base/utils/net.h"
#include "ui_ipsubnetwhitelistoptionsdialog.h"
#include "utils.h"
IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::IPSubnetWhitelistOptionsDialog)
, m_modified(false)
{
m_ui->setupUi(this);
QStringList authSubnetWhitelistStringList;
for (const Utils::Net::Subnet &subnet : Preferences::instance()->getWebUiAuthSubnetWhitelist())
authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet);
m_model = new QStringListModel(authSubnetWhitelistStringList, this);
m_sortFilter = new QSortFilterProxyModel(this);
m_sortFilter->setDynamicSortFilter(true);
m_sortFilter->setSourceModel(m_model);
m_ui->whitelistedIPSubnetList->setModel(m_sortFilter);
m_ui->whitelistedIPSubnetList->sortByColumn(0, Qt::AscendingOrder);
m_ui->buttonWhitelistIPSubnet->setEnabled(false);
Utils::Gui::resize(this);
}
IPSubnetWhitelistOptionsDialog::~IPSubnetWhitelistOptionsDialog()
{
delete m_ui;
}
void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted()
{
if (m_modified) {
// save to session
QStringList subnets;
// Operate on the m_sortFilter to grab the strings in sorted order
for (int i = 0; i < m_sortFilter->rowCount(); ++i)
subnets.append(m_sortFilter->index(i, 0).data().toString());
Preferences::instance()->setWebUiAuthSubnetWhitelist(subnets);
QDialog::accept();
}
else {
QDialog::reject();
}
}
void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked()
{
bool ok = false;
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text(), &ok);
if (!ok) {
QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid."));
return;
}
m_model->insertRow(m_model->rowCount());
m_model->setData(m_model->index(m_model->rowCount() - 1, 0), Utils::Net::subnetToString(subnet));
m_ui->txtIPSubnet->clear();
m_modified = true;
}
void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked()
{
for (const auto &i : m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes())
m_sortFilter->removeRow(i.row());
m_modified = true;
}
void IPSubnetWhitelistOptionsDialog::on_txtIPSubnet_textChanged(const QString &subnetStr)
{
m_ui->buttonWhitelistIPSubnet->setEnabled(Utils::Net::canParseSubnet(subnetStr));
}

View File

@@ -0,0 +1,64 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Thomas Piccirello <thomas@piccirello.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef OPTIONS_IPSUBNETWHITELIST_H
#define OPTIONS_IPSUBNETWHITELIST_H
#include <QDialog>
class QSortFilterProxyModel;
class QStringListModel;
namespace Ui
{
class IPSubnetWhitelistOptionsDialog;
}
class IPSubnetWhitelistOptionsDialog : public QDialog
{
Q_OBJECT
Q_DISABLE_COPY(IPSubnetWhitelistOptionsDialog)
public:
explicit IPSubnetWhitelistOptionsDialog(QWidget *parent = nullptr);
~IPSubnetWhitelistOptionsDialog();
private slots:
void on_buttonBox_accepted();
void on_buttonWhitelistIPSubnet_clicked();
void on_buttonDeleteIPSubnet_clicked();
void on_txtIPSubnet_textChanged(const QString &subnetStr);
private:
Ui::IPSubnetWhitelistOptionsDialog *m_ui;
QStringListModel *m_model;
QSortFilterProxyModel *m_sortFilter;
bool m_modified;
};
#endif // OPTIONS_IPSUBNETWHITELIST_H

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>IPSubnetWhitelistOptionsDialog</class>
<widget class="QDialog" name="IPSubnetWhitelistOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>360</width>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string>List of whitelisted IP subnets</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="whitelistedIPSubnetBox">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_31">
<item>
<widget class="QTreeView" name="whitelistedIPSubnetList">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="txtIPSubnet">
<property name="placeholderText">
<string>Example: 172.17.32.0/24, fdff:ffff:c8::/40</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QPushButton" name="buttonWhitelistIPSubnet">
<property name="text">
<string>Add subnet</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDeleteIPSubnet">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>whitelistedIPSubnetList</tabstop>
<tabstop>txtIPSubnet</tabstop>
<tabstop>buttonWhitelistIPSubnet</tabstop>
<tabstop>buttonDeleteIPSubnet</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>IPSubnetWhitelistOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>179</x>
<y>427</y>
</hint>
<hint type="destinationlabel">
<x>179</x>
<y>224</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

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

View File

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

View File

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

View File

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

View File

@@ -28,56 +28,72 @@
#include "macutilities.h" #include "macutilities.h"
#include <QSet>
#include <QtMac> #include <QtMac>
#include <objc/message.h> #include <objc/message.h>
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
QPixmap pixmapForExtension(const QString &ext, const QSize &size) namespace MacUtils
{ {
@autoreleasepool { QPixmap pixmapForExtension(const QString &ext, const QSize &size)
NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()]; {
if (image) { @autoreleasepool {
NSRect rect = NSMakeRect(0, 0, size.width(), size.height()); NSImage *image = [[NSWorkspace sharedWorkspace] iconForFileType:ext.toNSString()];
CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil]; if (image) {
return QtMac::fromCGImageRef(cgImage); 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" #include "mainwindow.h"
#ifdef Q_OS_MAC #include <QClipboard>
#include <QtMacExtras> #include <QCloseEvent>
#include <QtMac> #include <QCryptographicHash>
#endif #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 <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) #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
#include <QDBusConnection> #include <QDBusConnection>
#include "notifications.h" #include "notifications.h"
#endif #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 "about_imp.h"
#include "base/settingsstorage.h" #include "addnewtorrentdialog.h"
#include "base/logger.h" #include "application.h"
#include "base/utils/misc.h" #include "autoexpandabledialog.h"
#include "base/utils/fs.h"
#ifdef Q_OS_WIN
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#endif
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/sessionstatus.h" #include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "base/global.h" #include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/rss/rss_folder.h" #include "base/rss/rss_folder.h"
#include "base/rss/rss_session.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) #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
#include "programupdater.h" #include "programupdater.h"
#endif #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 #if LIBTORRENT_VERSION_NUM < 10100
#include "trackerlogin.h" #include "trackerlogin.h"
#endif #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 #ifdef Q_OS_MAC
void qt_mac_set_dock_menu(QMenu *menu); void qt_mac_set_dock_menu(QMenu *menu);
@@ -231,7 +231,7 @@ MainWindow::MainWindow(QWidget *parent)
m_searchFilter = new LineEdit(this); m_searchFilter = new LineEdit(this);
m_searchFilterAction = m_ui->toolBar->insertWidget(m_ui->actionLock, m_searchFilter); m_searchFilterAction = m_ui->toolBar->insertWidget(m_ui->actionLock, m_searchFilter);
m_searchFilter->setPlaceholderText(tr("Filter torrent list...")); m_searchFilter->setPlaceholderText(tr("Filter torrent list..."));
m_searchFilter->setFixedWidth(200); m_searchFilter->setFixedWidth(Utils::Gui::scaledSize(this, 200));
QWidget *spacer = new QWidget(this); QWidget *spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 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->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->actionDownloadFromURL->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_O);
m_ui->actionExit->setShortcut(Qt::CTRL + Qt::Key_Q); 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); QShortcut *switchTransferShortcut = new QShortcut(Qt::ALT + Qt::Key_1, this);
connect(switchTransferShortcut, &QShortcut::activated, this, &MainWindow::displayTransferTab); connect(switchTransferShortcut, &QShortcut::activated, this, &MainWindow::displayTransferTab);
@@ -978,6 +983,16 @@ void MainWindow::on_actionExit_triggered()
close(); 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 QWidget *MainWindow::currentTabWidget() const
{ {
if (isMinimized() || !isVisible()) if (isMinimized() || !isVisible())
@@ -1225,24 +1240,26 @@ void MainWindow::dropEvent(QDropEvent *event)
// remove scheme // remove scheme
QStringList files; QStringList files;
if (event->mimeData()->hasUrls()) { if (event->mimeData()->hasUrls()) {
const QList<QUrl> urls = event->mimeData()->urls(); foreach (const QUrl &url, event->mimeData()->urls()) {
foreach (const QUrl &url, urls) { if (url.isEmpty())
if (!url.isEmpty()) { continue;
if (url.scheme().compare("file", Qt::CaseInsensitive) == 0)
files << url.toLocalFile(); files << ((url.scheme().compare("file", Qt::CaseInsensitive) == 0)
else ? url.toLocalFile()
files << url.toString(); : url.toString());
}
} }
} }
else { else {
files = event->mimeData()->text().split('\n'); files = event->mimeData()->text().split('\n');
} }
// differentiate ".torrent" files and others // differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles, otherFiles; QStringList torrentFiles, otherFiles;
foreach (const QString &file, files) { foreach (const QString &file, files) {
if (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; torrentFiles << file;
else else
otherFiles << file; otherFiles << file;
@@ -1251,7 +1268,6 @@ void MainWindow::dropEvent(QDropEvent *event)
// Download torrents // Download torrents
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled(); const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
foreach (const QString &file, torrentFiles) { foreach (const QString &file, torrentFiles) {
qDebug("Dropped file %s on download list", qUtf8Printable(file));
if (useTorrentAdditionDialog) if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(file, this); AddNewTorrentDialog::show(file, this);
else else
@@ -1297,7 +1313,7 @@ static bool dockClickHandler(id self, SEL cmd, ...)
void MainWindow::setupDockClickHandler() void MainWindow::setupDockClickHandler()
{ {
dockMainWindowHandle = this; dockMainWindowHandle = this;
overrideDockClickHandler(dockClickHandler); MacUtils::overrideDockClickHandler(dockClickHandler);
} }
#endif #endif
@@ -1556,7 +1572,7 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const
if (!reply.isError()) if (!reply.isError())
return; return;
#elif defined(Q_OS_MAC) #elif defined(Q_OS_MAC)
displayNotification(title, msg); MacUtils::displayNotification(title, msg);
#else #else
if (m_systrayIcon && QSystemTrayIcon::supportsMessages()) if (m_systrayIcon && QSystemTrayIcon::supportsMessages())
m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, TIME_TRAY_BALLOON); m_systrayIcon->showMessage(title, msg, QSystemTrayIcon::Information, TIME_TRAY_BALLOON);
@@ -1838,7 +1854,7 @@ void MainWindow::toggleAlternativeSpeeds()
void MainWindow::on_actionDonateMoney_triggered() void MainWindow::on_actionDonateMoney_triggered()
{ {
QDesktopServices::openUrl(QUrl("http://www.qbittorrent.org/donate")); QDesktopServices::openUrl(QUrl("https://www.qbittorrent.org/donate"));
} }
void MainWindow::showConnectionSettings() void MainWindow::showConnectionSettings()

View File

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

View File

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

View File

@@ -65,8 +65,10 @@
#include "advancedsettings.h" #include "advancedsettings.h"
#include "rss/automatedrssdownloader.h" #include "rss/automatedrssdownloader.h"
#include "banlistoptions.h" #include "banlistoptions.h"
#include "ipsubnetwhitelistoptionsdialog.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "scanfoldersdelegate.h" #include "scanfoldersdelegate.h"
#include "utils.h"
#include "ui_optionsdlg.h" #include "ui_optionsdlg.h"
@@ -98,13 +100,21 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true); m_ui->tabSelection->item(TAB_WEBUI)->setHidden(true);
#endif #endif
m_ui->tabSelection->item(TAB_ADVANCED)->setIcon(GuiIconProvider::instance()->getIcon("preferences-other")); 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) { for (int i = 0; i < m_ui->tabSelection->count(); ++i) {
// uniform size for all icons const QSize size(std::numeric_limits<int>::max(), static_cast<int>(maxHeight * 1.2));
m_ui->tabSelection->item(i)->setSizeHint(QSize(std::numeric_limits<int>::max(), 62)); m_ui->tabSelection->item(i)->setSizeHint(size);
} }
m_ui->IpFilterRefreshBtn->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); 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->setPixmap(QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical).pixmap(16, 16));
m_ui->deleteTorrentWarningIcon->hide(); m_ui->deleteTorrentWarningIcon->hide();
m_ui->deleteTorrentWarningLabel->hide(); m_ui->deleteTorrentWarningLabel->hide();
@@ -138,7 +148,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(ScanFoldersModel::instance(), &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton); connect(ScanFoldersModel::instance(), &QAbstractListModel::dataChanged, this, &ThisType::enableApplyButton);
connect(m_ui->scanFoldersView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ThisType::handleScanFolderViewSelectionChanged); 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 // Languages supported
initializeLanguageCombo(); initializeLanguageCombo();
@@ -258,7 +268,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->autoRunBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->autoRunBox, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->autoRun_txt, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->autoRun_txt, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
const QString autoRunStr = QString::fromUtf8("%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10\n%11") const QString autoRunStr = QString("%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10\n%11")
.arg(tr("Supported parameters (case sensitive):")) .arg(tr("Supported parameters (case sensitive):"))
.arg(tr("%N: Torrent name")) .arg(tr("%N: Torrent name"))
.arg(tr("%L: Category")) .arg(tr("%L: Category"))
@@ -350,6 +360,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled);
connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
@@ -442,25 +454,19 @@ void OptionsDialog::changePage(QListWidgetItem *current, QListWidgetItem *previo
void OptionsDialog::loadWindowState() void OptionsDialog::loadWindowState()
{ {
const Preferences* const pref = Preferences::instance(); Utils::Gui::resize(this, Preferences::instance()->getPrefSize());
resize(pref->getPrefSize(this->size()));
} }
void OptionsDialog::loadSplitterState() void OptionsDialog::loadSplitterState()
{ {
const Preferences* const pref = Preferences::instance(); const QStringList sizesStr = Preferences::instance()->getPrefHSplitterSizes();
const QStringList sizes_str = pref->getPrefHSplitterSizes(); // width has been modified, use height as width reference instead
QList<int> sizes; const int width = Utils::Gui::scaledSize(this
if (sizes_str.size() == 2) { , (m_ui->tabSelection->item(TAB_UI)->sizeHint().height() * 2));
sizes << sizes_str.first().toInt(); QList<int> sizes {width, (m_ui->hsplitter->width() - width)};
sizes << sizes_str.last().toInt(); if (sizesStr.size() == 2)
} sizes = {sizesStr.first().toInt(), sizesStr.last().toInt()};
else {
sizes << 116;
sizes << m_ui->hsplitter->width() - 116;
}
m_ui->hsplitter->setSizes(sizes); m_ui->hsplitter->setSizes(sizes);
} }
@@ -486,7 +492,7 @@ void OptionsDialog::saveOptions()
QString locale = getLocale(); QString locale = getLocale();
if (pref->getLocale() != locale) { if (pref->getLocale() != locale) {
QTranslator *translator = new QTranslator; QTranslator *translator = new QTranslator;
if (translator->load(QString::fromUtf8(":/lang/qbittorrent_") + locale)) if (translator->load(QLatin1String(":/lang/qbittorrent_") + locale))
qDebug("%s locale recognized, using translation.", qUtf8Printable(locale)); qDebug("%s locale recognized, using translation.", qUtf8Printable(locale));
else else
qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale)); qDebug("%s locale unrecognized, using default (en).", qUtf8Printable(locale));
@@ -531,7 +537,7 @@ void OptionsDialog::saveOptions()
Application * const app = static_cast<Application*>(QCoreApplication::instance()); Application * const app = static_cast<Application*>(QCoreApplication::instance());
app->setFileLoggerPath(m_ui->textFileLogPath->selectedPath()); app->setFileLoggerPath(m_ui->textFileLogPath->selectedPath());
app->setFileLoggerBackup(m_ui->checkFileLogBackup->isChecked()); 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->setFileLoggerAge(m_ui->spinFileLogAge->value());
app->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex()); app->setFileLoggerAgeType(m_ui->comboFileLogAgeType->currentIndex());
app->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked()); app->setFileLoggerDeleteOld(m_ui->checkFileLogDelete->isChecked());
@@ -655,13 +661,16 @@ void OptionsDialog::saveOptions()
pref->setWebUiPort(m_ui->spinWebUiPort->value()); pref->setWebUiPort(m_ui->spinWebUiPort->value());
pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked()); pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked());
pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked()); pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked());
// HTTPS
if (m_ui->checkWebUiHttps->isChecked()) { if (m_ui->checkWebUiHttps->isChecked()) {
pref->setWebUiHttpsCertificate(m_sslCert); pref->setWebUiHttpsCertificate(m_sslCert);
pref->setWebUiHttpsKey(m_sslKey); pref->setWebUiHttpsKey(m_sslKey);
} }
// Authentication
pref->setWebUiUsername(webUiUsername()); pref->setWebUiUsername(webUiUsername());
pref->setWebUiPassword(webUiPassword()); pref->setWebUiPassword(webUiPassword());
pref->setWebUiLocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked()); pref->setWebUiLocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked());
pref->setWebUiAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
// DynDNS // DynDNS
pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked());
pref->setDynDNSService(m_ui->comboDNSService->currentIndex()); pref->setDynDNSService(m_ui->comboDNSService->currentIndex());
@@ -756,7 +765,7 @@ void OptionsDialog::loadOptions()
m_ui->checkFileLogDelete->setChecked(fileLogDelete); m_ui->checkFileLogDelete->setChecked(fileLogDelete);
m_ui->spinFileLogAge->setEnabled(fileLogDelete); m_ui->spinFileLogAge->setEnabled(fileLogDelete);
m_ui->comboFileLogAgeType->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->spinFileLogAge->setValue(app->fileLoggerAge());
m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType()); m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType());
// End General preferences // End General preferences
@@ -1052,6 +1061,8 @@ void OptionsDialog::loadOptions()
m_ui->textWebUiUsername->setText(pref->getWebUiUsername()); m_ui->textWebUiUsername->setText(pref->getWebUiUsername());
m_ui->textWebUiPassword->setText(pref->getWebUiPassword()); m_ui->textWebUiPassword->setText(pref->getWebUiPassword());
m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled()); m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled());
m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled());
m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked());
m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled());
m_ui->comboDNSService->setCurrentIndex(static_cast<int>(pref->getDynDNSService())); m_ui->comboDNSService->setCurrentIndex(static_cast<int>(pref->getDynDNSService()));
@@ -1570,7 +1581,7 @@ void OptionsDialog::on_IpFilterRefreshBtn_clicked()
session->setIPFilteringEnabled(true); session->setIPFilteringEnabled(true);
session->setIPFilterFile(""); // forcing Session reload filter file session->setIPFilterFile(""); // forcing Session reload filter file
session->setIPFilterFile(getFilter()); 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)); setCursor(QCursor(Qt::WaitCursor));
} }
@@ -1582,7 +1593,7 @@ void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
else else
QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); 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; 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) QString OptionsDialog::languageToLocalizedString(const QLocale &locale)
@@ -1667,11 +1678,11 @@ bool OptionsDialog::setSslKey(const QByteArray &key)
// try different formats // try different formats
const bool isKeyValid = (!QSslKey(key, QSsl::Rsa).isNull() || !QSslKey(key, QSsl::Ec).isNull()); const bool isKeyValid = (!QSslKey(key, QSsl::Rsa).isNull() || !QSslKey(key, QSsl::Ec).isNull());
if (isKeyValid) { 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; m_sslKey = key;
} }
else { 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(); m_sslKey.clear();
} }
return isKeyValid; return isKeyValid;
@@ -1686,11 +1697,11 @@ bool OptionsDialog::setSslCertificate(const QByteArray &cert)
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
const bool isCertValid = !QSslCertificate(cert).isNull(); const bool isCertValid = !QSslCertificate(cert).isNull();
if (isCertValid) { 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; m_sslCert = cert;
} }
else { 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(); m_sslCert.clear();
} }
return isCertValid; return isCertValid;
@@ -1724,7 +1735,14 @@ bool OptionsDialog::webUIAuthenticationOk()
void OptionsDialog::on_banListButton_clicked() void OptionsDialog::on_banListButton_clicked()
{ {
//have to call dialog window // call dialog window
BanListOptions bl(this); if (BanListOptions(this).exec() == QDialog::Accepted)
bl.exec(); enableApplyButton();
}
void OptionsDialog::on_IPSubnetWhitelistButton_clicked()
{
// call dialog window
if (IPSubnetWhitelistOptionsDialog(this).exec() == QDialog::Accepted)
enableApplyButton();
} }

View File

@@ -99,6 +99,7 @@ private slots:
void on_IpFilterRefreshBtn_clicked(); void on_IpFilterRefreshBtn_clicked();
void handleIPFilterParsed(bool error, int ruleCount); void handleIPFilterParsed(bool error, int ruleCount);
void on_banListButton_clicked(); void on_banListButton_clicked();
void on_IPSubnetWhitelistButton_clicked();
void on_randomButton_clicked(); void on_randomButton_clicked();
void on_addScanFolderButton_clicked(); void on_addScanFolderButton_clicked();
void on_removeScanFolderButton_clicked(); void on_removeScanFolderButton_clicked();

View File

@@ -535,16 +535,16 @@
<item> <item>
<widget class="QSpinBox" name="spinFileLogSize"> <widget class="QSpinBox" name="spinFileLogSize">
<property name="suffix"> <property name="suffix">
<string> MB</string> <string> KiB</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>1000</number> <number>1024000</number>
</property> </property>
<property name="value"> <property name="value">
<number>10</number> <number>65</number>
</property> </property>
</widget> </widget>
</item> </item>
@@ -1823,11 +1823,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="0" rowspan="2"> <item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="labelGlobalRate"/>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/slow_off.png</pixmap>
</property>
</widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="checkDownloadLimit"> <widget class="QCheckBox" name="checkDownloadLimit">
@@ -1970,11 +1966,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="0" rowspan="2"> <item row="0" column="0" rowspan="2">
<widget class="QLabel" name="label_16"> <widget class="QLabel" name="labelAltRate"/>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/slow.png</pixmap>
</property>
</widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QCheckBox" name="checkUploadLimitAlt"> <widget class="QCheckBox" name="checkUploadLimitAlt">
@@ -2853,20 +2845,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<layout class="QGridLayout" name="gridLayout_11"> <layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="lblSslCertStatus"> <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"> <property name="text">
<string/> <string notr="true"/>
</property> </property>
</widget> </widget>
</item> </item>
@@ -2906,20 +2886,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="lblSslKeyStatus"> <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"> <property name="text">
<string/> <string notr="true"/>
</property> </property>
</widget> </widget>
</item> </item>
@@ -3005,7 +2973,27 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<item row="4" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="checkBypassLocalAuth"> <widget class="QCheckBox" name="checkBypassLocalAuth">
<property name="text"> <property name="text">
<string>Bypass authentication for localhost</string> <string>Bypass authentication for clients on localhost</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="checkBypassAuthSubnetWhitelist">
<property name="text">
<string>Bypass authentication for clients in whitelisted IP subnets</string>
</property>
</widget>
</item>
<item row="6" column="1" colspan="1">
<widget class="QPushButton" name="IPSubnetWhitelistButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>IP subnet whitelist...</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -3289,6 +3277,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
<tabstop>btnWebUiCrt</tabstop> <tabstop>btnWebUiCrt</tabstop>
<tabstop>btnWebUiKey</tabstop> <tabstop>btnWebUiKey</tabstop>
<tabstop>checkBypassLocalAuth</tabstop> <tabstop>checkBypassLocalAuth</tabstop>
<tabstop>checkBypassAuthSubnetWhitelist</tabstop>
<tabstop>IPSubnetWhitelistButton</tabstop>
<tabstop>checkDynDNS</tabstop> <tabstop>checkDynDNS</tabstop>
<tabstop>comboDNSService</tabstop> <tabstop>comboDNSService</tabstop>
<tabstop>registerDNSBtn</tabstop> <tabstop>registerDNSBtn</tabstop>

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,13 +50,15 @@ protected:
switch (sortColumn()) { switch (sortColumn()) {
case PeerListDelegate::IP: case PeerListDelegate::IP:
case PeerListDelegate::CLIENT: { case PeerListDelegate::CLIENT: {
QString vL = left.data().toString(); const QString strL = left.data().toString();
QString vR = right.data().toString(); const QString strR = right.data().toString();
return Utils::String::naturalCompareCaseInsensitive(vL, vR); 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); Q_ASSERT(visibleCols > 0);
if (!isColumnHidden(col) && (visibleCols == 1)) if (!isColumnHidden(col) && (visibleCols == 1))
return; return;
qDebug("Toggling column %d visibility", col);
setColumnHidden(col, !isColumnHidden(col)); setColumnHidden(col, !isColumnHidden(col));
if (!isColumnHidden(col) && (columnWidth(col) <= 5)) if (!isColumnHidden(col) && (columnWidth(col) <= 5))
setColumnWidth(col, 100); resizeColumnToContents(col);
saveSettings(); saveSettings();
} }
} }
@@ -231,8 +230,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer...")); addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
emptyMenu = false; emptyMenu = false;
} }
QAction *banAct = 0; QAction *banAct = nullptr;
QAction *copyPeerAct = 0; QAction *copyPeerAct = nullptr;
if (!selectionModel()->selectedRows().isEmpty()) { if (!selectionModel()->selectedRows().isEmpty()) {
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy IP:port")); copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy IP:port"));
menu.addSeparator(); menu.addSeparator();
@@ -241,7 +240,8 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
} }
if (emptyMenu) return; if (emptyMenu) return;
QAction *act = menu.exec(QCursor::pos()); QAction *act = menu.exec(QCursor::pos());
if (act == 0) return; if (!act) return;
if (act == addPeerAct) { if (act == addPeerAct) {
QList<BitTorrent::PeerAddress> peersList = PeersAdditionDlg::askForPeers(this); QList<BitTorrent::PeerAddress> peersList = PeersAdditionDlg::askForPeers(this);
int peerCount = 0; int peerCount = 0;
@@ -249,7 +249,7 @@ void PeerListWidget::showPeerListMenu(const QPoint &)
if (torrent->connectPeer(addr)) { if (torrent->connectPeer(addr)) {
qDebug("Adding peer %s...", qUtf8Printable(addr.ip.toString())); qDebug("Adding peer %s...", qUtf8Printable(addr.ip.toString()));
Logger::instance()->addMessage(tr("Manually adding peer '%1'...").arg(addr.ip.toString())); Logger::instance()->addMessage(tr("Manually adding peer '%1'...").arg(addr.ip.toString()));
peerCount++; ++peerCount;
} }
else { else {
Logger::instance()->addMessage(tr("The peer '%1' could not be added to this torrent.").arg(addr.ip.toString()), Log::WARNING); 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?"), 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"), tr("&Yes"), tr("&No"),
QString(), 0, 1); QString(), 0, 1);
if (ret) if (ret) return;
return;
QModelIndexList selectedIndexes = selectionModel()->selectedRows(); QModelIndexList selectedIndexes = selectionModel()->selectedRows();
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {

View File

@@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt4 and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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), * 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 * but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version. * exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/ */
#include "peersadditiondlg.h" #include "peersadditiondlg.h"
#include <QMessageBox>
#include <QHostAddress> #include <QHostAddress>
#include <QMessageBox>
#include "ui_peersadditiondlg.h" #include "ui_peersadditiondlg.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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), * 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 * but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version. * exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/ */
#include "proplistdelegate.h" #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())); QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break; break;
case PROGRESS: { case PROGRESS: {
if (index.data().toDouble() < 0) if (index.data().toDouble() < 0)
break; break;
QStyleOptionProgressBar newopt; QStyleOptionProgressBar newopt;
qreal progress = index.data().toDouble() * 100.; qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect; newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.progress = int(progress); newopt.progress = int(progress);
newopt.maximum = 100; newopt.maximum = 100;
newopt.minimum = 0; newopt.minimum = 0;
newopt.textVisible = true; newopt.textVisible = true;
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) { if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
newopt.state &= ~QStyle::State_Enabled; newopt.state &= ~QStyle::State_Enabled;
newopt.palette = progressBarDisabledPalette(); newopt.palette = progressBarDisabledPalette();
} }
else { else {
newopt.state |= QStyle::State_Enabled; newopt.state |= QStyle::State_Enabled;
} }
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter); QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else #else
// XXX: To avoid having the progress text on the right of the bar // XXX: To avoid having the progress text on the right of the bar
QProxyStyle("fusion").drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0); QProxyStyle("fusion").drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif #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; break;
case AVAILABILITY: { 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 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) { if (m_properties) {
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent(); BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
if (!torrent || !torrent->hasMetadata() || torrent->isSeed()) if (!torrent || !torrent->hasMetadata() || torrent->isSeed())
return 0; return nullptr;
} }
if (index.data().toInt() == prio::MIXED) if (index.data().toInt() == prio::MIXED)
return 0; return nullptr;
QComboBox *editor = new QComboBox(parent); QComboBox *editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus); editor->setFocusPolicy(Qt::StrongFocus);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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), * 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 * but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version. * exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/ */
#include "trackersadditiondlg.h" #include "trackersadditiondlg.h"
#include <QStringList>
#include <QMessageBox>
#include <QFile> #include <QFile>
#include <QMessageBox>
#include <QStringList>
#include <QUrl> #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/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 "guiiconprovider.h"
#include "ui_trackersadditiondlg.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); 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(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(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); setCursor(Qt::WaitCursor);
} }
void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path) void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path)
{ {
QFile list_file(path); QFile listFile(path);
if (!list_file.open(QFile::ReadOnly)) { if (!listFile.open(QFile::ReadOnly)) {
QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok); QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok);
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
m_ui->uTorrentListButton->setEnabled(true); m_ui->uTorrentListButton->setEnabled(true);
@@ -94,8 +92,8 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
QList<BitTorrent::TrackerEntry> existingTrackers = m_torrent->trackers(); QList<BitTorrent::TrackerEntry> existingTrackers = m_torrent->trackers();
// Load from current user list // Load from current user list
QStringList tmp = m_ui->trackers_list->toPlainText().split("\n"); QStringList tmp = m_ui->trackers_list->toPlainText().split("\n");
foreach (const QString &user_url, tmp) { foreach (const QString &userURL, tmp) {
BitTorrent::TrackerEntry userTracker(user_url); BitTorrent::TrackerEntry userTracker(userURL);
if (!existingTrackers.contains(userTracker)) if (!existingTrackers.contains(userTracker))
existingTrackers << 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")) if (!m_ui->trackers_list->toPlainText().isEmpty() && !m_ui->trackers_list->toPlainText().endsWith("\n"))
m_ui->trackers_list->insertPlainText("\n"); m_ui->trackers_list->insertPlainText("\n");
int nb = 0; int nb = 0;
while (!list_file.atEnd()) { while (!listFile.atEnd()) {
const QString line = list_file.readLine().trimmed(); const QString line = listFile.readLine().trimmed();
if (line.isEmpty()) continue; if (line.isEmpty()) continue;
BitTorrent::TrackerEntry newTracker(line); BitTorrent::TrackerEntry newTracker(line);
if (!existingTrackers.contains(newTracker)) { if (!existingTrackers.contains(newTracker)) {
@@ -114,7 +112,7 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
} }
} }
// Clean up // Clean up
list_file.close(); listFile.close();
Utils::Fs::forceRemove(path); Utils::Fs::forceRemove(path);
//To restore the cursor ... //To restore the cursor ...
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);

View File

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

View File

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

View File

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

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