Compare commits

...

95 Commits

Author SHA1 Message Date
sledgehammer999
15e7726233 Bump to 3.3.13 2017-06-01 02:32:05 +03:00
sledgehammer999
9258a9ae78 Update Changelog. 2017-06-01 02:29:53 +03:00
Chocobo1
7c2437e5f2 [WebUI]: Implement CSRF defense
Bump API version
2017-06-01 02:28:07 +03:00
sledgehammer999
dc315c080b Revert "Remove workaround". Required since we also support Qt4 in this branch.
This reverts commit 2ba3104337.
2017-06-01 02:28:06 +03:00
sledgehammer999
971c340b53 Fix format-truncation warning. 2017-06-01 01:19:55 +03:00
Chocobo1
76b9b02177 Fix focusing on the previously opened dialog didn't work
Placing modeless dialog A on top of modeless dialog B, then open dialog B again,
the expected behavior is B on top of A.
2017-06-01 01:19:47 +03:00
sledgehammer999
375c2dfd0b Merge pull request #6876 from evsh/fix-cmake-qt4
cmake: fix compilation with Qt 4
2017-05-31 22:41:11 +03:00
Eugene Shalygin
6a3e7a12d8 cmake: fix compilation with Qt 4
In qmake we define QStringLiteral=QLatin1String, so define it in cmake
builds too.
2017-05-31 19:28:32 +02:00
sledgehammer999
d92ef3fa4d Fix Qt4 build. 2017-05-31 00:34:21 +03:00
Eugene Shalygin
89d4cb257f Fix crash in download piece bar
When torrent size is smaller than the image width, bytes per pixel was
set to zero and code was crashing. Set it to -1 instead, as we do when
image is empty. This will disable highliting, but our algorithm does not
work in this case anyway.
2017-05-31 00:34:21 +03:00
Chocobo1
47ebb0df58 Add new webUI API. Closes #6457.
Now getting piece information for a specific torrent is possible via:

* Returns an array of states (integers) of pieces in order. Defined as:
  "0=not downloaded", "1=downloading", "2=downloaded".
  GET /query/getPieceStates/<torrent_hash>

* Returns an array of hashes (strings) of pieces in order:
  GET /query/getPieceHashes/<torrent_hash>
2017-05-31 00:34:20 +03:00
Chocobo1
71169abaa6 [WebUI] Make cookie parsing robust
Previously cookie name such as "<any string>SID" will be mistakenly
accepted as "SID" session ID, this commit fixes it.

Use QString::isEmpty()
2017-05-31 00:34:19 +03:00
Chocobo1
83dd0ae86e Refactor out helper function Utils::String::unquote
Remove redundant include
2017-05-31 00:34:18 +03:00
Chocobo1
c9500d933b Fix renaming files is not case sensitive on Windows platform. Closes #5128. 2017-05-31 00:34:17 +03:00
Naikel Aparicio
b8787460a4 Fix slow filtering in WebUI. Fixup 7aadf644e0. 2017-05-31 00:34:17 +03:00
Naikel Aparicio
d031264d62 Cosmetic fixes for WebUI upload and download windows 2017-05-31 00:34:16 +03:00
KingLucius
63b408c743 remove ExtraTorrent plugin 2017-05-31 00:34:15 +03:00
Eugene Shalygin
1ac4458d13 cmake: fix FindSystemd.cmake
Apparently, recent version of CMake made PkgConfig stuff scooped and we
need to call find_package(PkgConfig) in FindSystemd.cmake too, the call
in FindLibtorrentRasterbar.cmake, that is always used first, is not
enough now.
2017-05-31 00:34:14 +03:00
Chocobo1
6b91510d71 Set icon height in html instead of js
Fixup of d302e4fb92
2017-05-31 00:34:14 +03:00
Chocobo1
f1a7e8921a [WebUI] Fix connection status icon too large. Closes #6804. 2017-05-31 00:34:13 +03:00
Eugene Shalygin
78c34b948e Fix build error. 2017-05-31 00:34:12 +03:00
Frédéric Brière
690db070c7 Properly sort categories case-insensitively in filter widget. Closes #6708. 2017-05-31 00:34:11 +03:00
Frédéric Brière
0979936266 Turn isSpecialItem() into a CategoryFilterModel static method. 2017-05-31 00:34:11 +03:00
Frédéric Brière
ba23b41616 Set "category" column as case-insensitive in TransferListSortModel.
(From what I can tell, it would appear that the sorting was already
case-insensitive by default.  This makes it explicit.)
2017-05-31 00:34:10 +03:00
Frédéric Brière
412584e9c3 Leave categories order intact in "Add new torrent" dialog.
The categories are already properly sorted in AddNewTorrentDialog; this
second case-sensitive sort undid all that.

This partially addresses #6708.
2017-05-31 00:34:09 +03:00
Thomas Piccirello
13aa3c3088 Use less permissive Content Security Policy
Adjust content order
2017-05-31 00:34:08 +03:00
thalieht
067c406c94 webUI: increase the number of digits after the decimal point 2017-05-31 00:34:08 +03:00
Eugene Shalygin
8536e79f2a cmake: include searchengine resources 2017-05-31 00:34:07 +03:00
Thomas Piccirello
70b98d903e Return status indicating if at least one torrent was successfully added 2017-05-31 00:34:06 +03:00
Chocobo1
1997a29769 Tweak CachedSettingValue
* Add another constructor to save a call to proxyFunc when its not needed
  note that this call is a virtual function call
* Pass in proxyFunc by const reference
* Move get methods together
2017-05-31 00:34:05 +03:00
Chocobo1
f6e4f48386 Fix double click on system tray icon causing program to open and
minimize immediately

Closes #5826.
Simply ignore DoubleClick event, as it always come after Trigger
event
2017-05-31 00:34:05 +03:00
Eugene Shalygin
10002984e6 Follow project coding style. Issue #2192. 2017-05-31 00:34:04 +03:00
Frédéric Brière
2fa2d36c3b findIncompleteFiles(): Replace dir listing with individual checks. Closes #6265.
Looking for incomplete files in a new torrent by using a pre-generated
directory listing presents several disadvantages:

  - It requires us to extract the top-level directory name (in case it
    was renamed).
  - It requires us to know whether the top-level directory was stripped.
  - In the latter case, it may result in recursively traversing the
    entire contents of all downloaded torrents.

Calling QFile::exists() individually for each file solves all these
issues.

In so doing, the handling of single-file and multiple-file torrents are
rendered pretty much identical, and can therefore be merged.
2017-05-31 00:34:03 +03:00
Eugene Shalygin
ceed9b468e Do not attempt to show detailed tooltips without torrent metadata. Closes #6768.
Computations for detailed tooltips are not protected against unavailable
metadata, that leads to asserts or crashes. But since those tooltips are
useless in this case, do not show them at all. Inform user that detailed
tooltip becomes available once torrent metadata are fetched.
2017-05-31 00:34:02 +03:00
sledgehammer999
f19854c054 [WebUI]Use the same layout in the Speed tab in preferences as the GUI. 2017-05-31 00:34:02 +03:00
sledgehammer999
989cbda29c Add missing unit sizes in misc.js 2017-05-31 00:34:01 +03:00
sledgehammer999
881f79b76d [WebUI]Use translatable strings in Statistics dialog. 2017-05-31 00:34:00 +03:00
sledgehammer999
51986f3ac8 [WebUI]Make the context obligatory for translatable strings. Also delete duplicate strings from extra translations. 2017-05-31 00:33:59 +03:00
Eugene Shalygin
943a837570 Fix release CMake build
write() is declared with __attribute__ ((__warn_unused_result__)) and as
such we shall check its return value. Took opportunity and adjusted
error reporting a bit: if writing to stderr fails, try to write to
stdout.
2017-05-31 00:33:59 +03:00
Chocobo1
a6cf386073 .gitignore: ignore moc_*.h 2017-05-31 00:33:58 +03:00
Vladimir Golovnev
b6e1b6e501 Revert "Guard the flag used for deferred session configure." (#6733)
This reverts commit 5cbc7b16c0.
2017-05-31 00:33:57 +03:00
Chocobo1
cee308a517 Fix stack overflow in Utils::Gzip::decompress
Anyway, use std::vector to allocate memory on the heap (in compress() too)
2017-05-31 00:33:56 +03:00
Brian Kendall
390d22bc66 Fixed macOS-specific bug in AddNewTorrentDialog
Because AddNewTorrentDialog is a sheet in macOS, repositioning it causes bad things to happen, particularly if the main dialog is on a secondary monitor.
2017-05-31 00:33:56 +03:00
Chocobo1
e4a5b8d352 Setup DPI at startup 2017-05-31 00:33:55 +03:00
Chocobo1
2ba3104337 Remove workaround
Upstream confirmed bug fixed in Qt 5.2
2017-05-31 00:33:54 +03:00
Chocobo1
d1dfdd1306 Refactor 2017-05-31 00:33:53 +03:00
Chocobo1
8a02a69924 Temporary revert to the old behavior. 2017-05-31 00:33:53 +03:00
sledgehammer999
8ffc72b626 TravisCI: Install latest zlib and switch to container-based infrastructure. 2017-05-31 00:33:52 +03:00
Chocobo1
e85479dfd7 Specify lib requirement: zlib >= 1.2.5.2 2017-05-31 00:33:51 +03:00
Chocobo1
104bed7cc8 Revise Utils::Gzip::decompress
Rename from uncompress to decompress
Change signature
Use proper casting
Use larger buffer for the output of inflate()
Reserve 1 MBytes for output buffer
Change function signature
2017-05-31 00:33:50 +03:00
Chocobo1
75f3dd6d1d Revise Utils::Gzip::compress code
Change signature
Add ZLIB_CONST define to make  z_stream.next_in const
Cast to zlib defined type Bytef*
Set memLevel to 9 in deflateInit2() for maximum performance
Revise compression loop
On returning false, free memory correctly by calling deflateEnd()
Reserve space by the estimation of deflateBound()
2017-05-31 00:33:50 +03:00
Chocobo1
751f64c98b Rewrite rules for gzipping http response content 2017-05-31 00:33:49 +03:00
Chocobo1
6353c2ca3c Implement robust acceptsGzipEncoding()
Adhere more to http/1.1 standard
2017-05-31 00:33:48 +03:00
Chocobo1
f51e467ce3 Fix "Content-Encoding" header is always created.
Was side effect of operator[]
2017-05-31 00:33:47 +03:00
Chocobo1
358d182c82 Cleanup Http::responseGenerator()
Add CRLF definition
Rewrite loop using iterator, slightly more efficient
Rename variables
2017-05-31 00:33:47 +03:00
Chocobo1
7d2802cf2c Demote to helper function
Rename function
2017-05-31 00:33:46 +03:00
Chocobo1
a739d86e3d Convert Qstring to char arrays
Cleanup header
Sort constants
2017-05-31 00:33:45 +03:00
Chocobo1
6ff614ebea Send Date http header
It's not strict required but often expected.
change class to namespace
cleanup header
2017-05-31 00:33:44 +03:00
Chocobo1
d9f4141221 Always send Content-Length header.
Because without it, HTTP/1.1 (with persistence connection) clients will
keep waiting for more data.
2017-05-31 00:33:44 +03:00
Chocobo1
415a805818 Implement http persistence connection
Max simultaneous connection limit set to 500
This also release allocated memory of Connection instances at runtime instead of at program shutdown.
2017-05-31 00:33:43 +03:00
Chocobo1
b8fff68230 WebUI: Fix checkbox hidden. Closes #6642. 2017-05-31 00:33:42 +03:00
Eugene Shalygin
44f81a2d2b cmake: set warning and error options
The set is far from perfect, but guards against common errors with GCC.
2017-05-31 00:33:41 +03:00
Eugene Shalygin
b98ef9905e cmake: use environment variable LIB on Windows
Append its value to CMAKE_LIBRARY_PATH to simplify life of on Windows.
2017-05-31 00:33:41 +03:00
Eugene Shalygin
e4f472e0f8 Replace variable length array with std::vector in print_stacktrace()
The function does memory allocation from heap anyway, so should not be
a problem to use STL container.
2017-05-31 00:33:40 +03:00
Vladimir Golovnev (Glassez)
91a38193f5 Remove torrent temp folder when torrent is deleted 2017-05-31 00:33:39 +03:00
Vladimir Golovnev (Glassez)
66b92f3bb4 Remove torrent temp folder if it becomes unneeded 2017-05-31 00:33:38 +03:00
Eugene Shalygin
a911dc57f3 Fix formatting in CONTRIBUTING.md (#6704)
Fix formatting in CONTRIBUTING.md
2017-05-31 00:33:38 +03:00
Chocobo1
c43c473105 Disable Qt embedding manifest automatically
Embedding manifest fails for me after upgrading to VS2017, this fixes it.
2017-05-31 00:33:37 +03:00
wevsty
d079b71f63 update chinese windows installer translation 2017-05-31 00:33:36 +03:00
ngosang
8b0b398a5f [Search engine] Add btdb plugin 2017-05-31 00:33:35 +03:00
ngosang
9400aac003 [Search engine] Update legittorrents plugin 2017-05-31 00:33:35 +03:00
ngosang
7856863b3e [Search engine] Remove mininova plugin 2017-05-31 00:33:34 +03:00
ngosang
b1f598a1d3 [Search engine] Update demonoid plugin 2017-05-31 00:33:33 +03:00
Eugene Shalygin
9c7ed80292 cmake: use import libraries for Boost and OpenSSL in Libtorrent find module 2017-05-31 00:33:32 +03:00
Eugene Shalygin
1835ec6086 cmake: fix typo in the manifest file name 2017-05-31 00:33:32 +03:00
Chocobo1
3d4cead200 Uncrustify 2017-05-31 00:33:31 +03:00
Chocobo1
112a24f9b6 Relax comparsion for floating point 2017-05-31 00:33:30 +03:00
Chocobo1
cdcafecb44 Cleanup & refactor 2017-05-31 00:33:29 +03:00
Chocobo1
4e173d34d3 Setup parent pointer
Rely on Qt to do the delete, since the parent ownership is setup
correctly.
2017-05-31 00:33:28 +03:00
Chocobo1
df5d31b52b Always draw background 2017-05-31 00:33:28 +03:00
Chocobo1
be745551e6 Fix downloaded/uploaded columns were not highlighted properly when selected.
Refactor
2017-05-31 00:33:27 +03:00
opengg
a3e4bcd1dd [WebUI] Add skip_checking and paused to /command/download and /command/upload 2017-05-31 00:33:26 +03:00
opengg
36cc6909f8 [WebUI] bugfix: RequestParser::splitMultipartData drop extra trailing newline. 2017-05-31 00:33:25 +03:00
Chocobo1
3987e677d5 Refactor: move methods under the same #if section. 2017-05-31 00:33:24 +03:00
Chocobo1
13f27c6d3b Refactor: move the validation of certificates & key functions under Server class
Rename method
Add log messages
2017-05-31 00:33:24 +03:00
Chocobo1
68f88f7907 Rename class variables 2017-05-31 00:33:23 +03:00
Chocobo1
6c2c08c6dd Refactor: group port forwarding code together
Unify log message
2017-05-31 00:33:22 +03:00
Eugene Shalygin
b8eee9e1b8 Start up torrents after UI was created. Fixes #6454.
Commit dd0537d changed torrents startup code adding alerts processing
into it. Therefore alerts were processed before UI code subscribed to
signals and therefore part of alerts was not reflected in the UI.

Thus here we do not start torrents in Session constructor, but do that
from Application::exec() after UI was constructed and is ready to process
signals.
2017-05-31 00:33:21 +03:00
Eugene Shalygin
ef08b4269d Rename .desktop and appdata files to match executable name. Fixes #6625. 2017-05-31 00:33:21 +03:00
schnurlos
b5a67aefdb Update of german.nsi
Translation of 64-bit text done.
2017-05-31 00:33:20 +03:00
Eugene Shalygin
a302c995f8 Create Ukrainian translation for the Windows installer. 2017-05-31 00:33:19 +03:00
ngosang
f2877cbec4 Update Spanish translation for the installer 2017-05-31 00:33:06 +03:00
sledgehammer999
4a95291fcc Merge pull request #6648 from evsh/v3_3_x
CMake 3.8 fixes for v3_3_x branch. Closes #6634.
2017-04-17 20:59:23 +03:00
Eugene Shalygin
6717e3d30c Remove generated include from headers. Closes #6634.
Not only fixes compilation with CMake 3.8 (without messing with include
paths) but makes sources cleaner.
2017-04-17 19:30:38 +02:00
Eugene Shalygin
778209ae49 cmake: fixes for cmake 3.8
AUTOUIC seems to became stricter.
2017-04-17 18:26:11 +02:00
154 changed files with 2828 additions and 2133 deletions

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ Makefile*
# Generated MOC, resource and UI files # Generated MOC, resource and UI files
moc_*.cpp moc_*.cpp
moc_*.h
qrc_*.cpp qrc_*.cpp
ui_*.h ui_*.h
*.moc *.moc

View File

@@ -34,16 +34,15 @@ notifications:
on_success: change on_success: change
on_failure: change on_failure: change
# container-based builds
#sudo: false
cache: cache:
ccache: true ccache: true
directories: directories:
- $HOME/hombebrew_cache - $HOME/hombebrew_cache
# opt-in Ubuntu Trusty # opt-in Ubuntu Trusty
sudo: required
dist: trusty dist: trusty
# container-based builds
sudo: false
addons: addons:
coverity_scan: coverity_scan:
@@ -55,20 +54,27 @@ addons:
branch_pattern: $coverity_branch branch_pattern: $coverity_branch
notification_email: sledgehammer999@qbittorrent.org notification_email: sledgehammer999@qbittorrent.org
apt: apt:
#sources: sources:
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json # sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
#- ubuntu-toolchain-r-test #- ubuntu-toolchain-r-test
#- boost-latest #- boost-latest
- sourceline: 'ppa:qbittorrent-team/qbittorrent-stable'
- sourceline: 'ppa:beineri/opt-qt551-trusty'
packages: packages:
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise # packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
- autoconf - autoconf
- automake - automake
- colormake - colormake
- libssl-dev - libssl-dev
- libboost-dev - libboost-dev
- libboost-system-dev - libboost-system-dev
# uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package - libtorrent-rasterbar-dev
#- libtorrent-rasterbar6 # Qt 5.5.1
- qt55base
- qt55tools
# Qt 4.8
- qt4-default
- libqt4-dev
before_install: before_install:
# only allow specific build for coverity scan, others will stop # only allow specific build for coverity scan, others will stop
@@ -79,7 +85,7 @@ before_install:
#- libt_path="$HOME/libt_install" #- libt_path="$HOME/libt_install"
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip" #- 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":$PKG_CONFIG_PATH" - qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
# options for specific branches # options for specific branches
- if [ "$qt" = 4 ]; then qbtconf="$qbtconf --with-qt4" ; fi - if [ "$qt" = 4 ]; then qbtconf="$qbtconf --with-qt4" ; fi
@@ -88,6 +94,9 @@ before_install:
if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# setup virtual display for after_success target # setup virtual display for after_success target
if [ "$gui" = true ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ; if [ "$gui" = true ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
# Qt 5
PATH=/opt/qt55/bin:${PATH}
fi fi
# print settings # print settings
@@ -97,31 +106,23 @@ before_install:
- echo $qbtconf - echo $qbtconf
install: install:
- | #- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then #if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# libtorrent
sudo add-apt-repository --yes ppa:qbittorrent-team/qbittorrent-stable ;
sudo apt-get update -qq ;
sudo apt-get install -qq libtorrent-rasterbar-dev ;
# build libtorrent from source # build libtorrent from source
#if [ "$lt_branch" != "dist" ]; then #if [ "$lt_branch" != "dist" ]; then
#cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ; #cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ;
#cd libtorrent && ./autotool.sh && ./configure $ltconf && make install ; #cd libtorrent && ./autotool.sh && ./configure $ltconf && make install ;
#fi ; #fi ;
#fi
# Qt
if [ "$qt" = 4 ]; then sudo apt-get -qq install qt4-default libqt4-dev ; fi ;
if [ "$qt" = 5 ]; then sudo apt-get -qq install qt5-default qtbase5-dev qttools5-dev-tools ; fi ;
# ccache
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
dpkg-query -L ccache && export use_ccache=true ;
ccache -V && ccache --show-stats && ccache --zero-stats ;
fi ;
fi
- | - |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# dependencies
brew update > /dev/null
brew install colormake ccache zlib
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew link --force zlib
brew outdated "pkg-config" || brew upgrade "pkg-config"
wget https://builds.shiki.hu/homebrew/version ; wget https://builds.shiki.hu/homebrew/version ;
if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then
echo "Cached files are different from server. Downloading new ones." ; echo "Cached files are different from server. Downloading new ones." ;
@@ -136,10 +137,6 @@ install:
wget https://builds.shiki.hu/homebrew/qt5-5.7.1_1.el_capitan.bottle.tar.gz ; wget https://builds.shiki.hu/homebrew/qt5-5.7.1_1.el_capitan.bottle.tar.gz ;
fi fi
# dependencies
brew update > /dev/null ;
brew install colormake ccache ;
brew outdated "pkg-config" || brew upgrade "pkg-config" ;
# 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.
@@ -156,12 +153,11 @@ install:
brew install "$HOME/hombebrew_cache/qt5.rb" ; brew install "$HOME/hombebrew_cache/qt5.rb" ;
brew link --force qt5 ; brew link --force qt5 ;
fi fi
fi
# ccache - |
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH" && export use_ccache=true ; export use_ccache=true
ccache -V && ccache --show-stats && ccache --zero-stats ; ccache -V && ccache --show-stats && ccache --zero-stats
fi ;
fi fi
script: script:

View File

@@ -30,7 +30,6 @@ add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}") add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
# } # }
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og")
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
include(GNUInstallDirs) include(GNUInstallDirs)
endif (UNIX AND NOT APPLE) endif (UNIX AND NOT APPLE)

View File

@@ -1,4 +1,4 @@
# Filing an issue # Filing an issue
### Must read ### Must read
* If you aren't sure, you can ask on the [**forum**](http://forum.qbittorrent.org) or read our [**wiki**](http://wiki.qbittorrent.org) first. * If you aren't sure, you can ask on the [**forum**](http://forum.qbittorrent.org) or read our [**wiki**](http://wiki.qbittorrent.org) first.
@@ -6,12 +6,11 @@
* Write in **English**! * Write in **English**!
* Provide **version** information: (You can find version numbers at menu `Help -> About -> Libraries`) * Provide **version** information: (You can find version numbers at menu `Help -> About -> Libraries`)
``` ```
qBittorrent: qBittorrent:
Qt: Qt:
libtorrent: libtorrent:
boost: boost:
OS version: OS version:
``` ```
* Provide **steps** to reproduce the problem, it will be easier to pinpoint the fault. * Provide **steps** to reproduce the problem, it will be easier to pinpoint the fault.
* **Screenshots**! A screenshot is worth a thousand words. just upload it. [(How?)](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests) * **Screenshots**! A screenshot is worth a thousand words. just upload it. [(How?)](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests)

View File

@@ -1,3 +1,47 @@
* Thu Jun 01 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.13
- BUGFIX: Fixed UI glitch about torrent numbers in the sidepanel. Fixes #6454. (evsh)
- BUGFIX: Fix downloaded/uploaded columns were not highlighted properly when selected. (Chocobo1)
- BUGFIX: Always draw background in files list and search result list (Chocobo1)
- BUGFIX: Remove torrent temp folder if it becomes unneeded (glassez)
- BUGFIX: Remove torrent temp folder when torrent is deleted (glassez)
- BUGFIX: Setup DPI at startup (Chocobo1)
- BUGFIX: Do not attempt to show detailed tooltips without torrent metadata. Closes #6768. (evsh)
- BUGFIX: Better detection of already present files when adding a torrent. (fbriere)
- BUGFIX: Fix double click on system tray icon causing program to open and minimize immediately. Closes #5826. (Chocobo1)
- BUGIFX: Fix categories sorting in AddNewTorrentDialog. Partially fixes #6708. (fbriere)
- BUGFIX: Set "category" column as case-insensitive in transfer list. (fbriere)
- BUGFIX: Properly sort categories case-insensitively in filter widget. Closes #6708. (fbriere)
- BUGFIX: Fix renaming files is not case sensitive on Windows platform. Closes #5128. (Chocobo1)
- BUGFIX: Fix crash in download piece bar (evsh)
- BUGFIX: Fix focusing on the previously opened dialog didn't work (Chocobo1)
- WEBUI: Bugfix: `RequestParser::splitMultipartData` drop extra trailing newline. (OpenGG)
- WEBUI: Add `skip_checking` and `paused` to `/command/download` and `/command/upload` (OpenGG)
- WEBUI: Fix checkbox hidden. Closes #6642. (Chocobo1)
- WEBUI: Implement http persistence connection. Max simultaneous connection limit set to 500. This also release allocated memory of Connection instances at runtime instead of at program shutdown. (Chocobo1)
- WEBUI: Always send Content-Length header. (Chocobo1)
- WEBUI: Send Date http header (Chocobo1)
- WEBUI: Fix "Content-Encoding" header is always created. (Chocobo1)
- WEBUI: Implement robust checking for gzip encoding and revise gzip compressing/decompressing code. (Chocobo1)
- WEBUI: Make the context obligatory for translatable strings. Also delete duplicate strings from extra translations. (sledgehammer999)
- WEBUI: Use translatable strings in Statistics dialog. (sledgehammer999)
- WEBUI: Add missing unit sizes in misc.js (sledgehammer999)
- WEBUI: Use the same layout in the Speed tab in preferences as the GUI. (sledgehammer999)
- WEBUI: Return status indicating if at least one torrent was successfully added (Thomas Piccirello)
- WEBUI: Increase the number of digits after the decimal point (thalieht)
- WEBUI: Use less permissive Content Security Policy (Thomas Piccirello)
- WEBUI: Fix connection status icon too large. Closes #6804. (Chocobo1)
- WEBUI: Cosmetic fixes for WebUI upload and download windows (naikel)
- WEBUI: Fix slow filtering in WebUI. (naikel)
- WEBUI: Make cookie parsing robust (Chocobo1)
- WEBUI: New API for getting torrent piece info (Chocobo1)
- WEBUI: Implement Cross-Site Request Forgery defense. Due to this the HTTP referer header is now expected in (almost) all HTTP requests. qBittorrent will drop the request sent without the referer header. That's why we bump the API_VERSION_MIN too. (reported by OpenGG, fixed by Chocobo1)
- SEARCH: Update demonoid, legittorrents plugins (ngosang)
- SEARCH: Remove mininova, ExtraTorrent plugins (ngosang, KingLucius)
- SEARCH: Add btdb plugin (ngosang)
- WINDOWS: Updated Spanish, Ukrainian, German, Chinese languages of the installer. (ngosang, evsh, schnurlos, wevsty)
- LINUX: Rename .desktop and appdata files to match executable name. Fixes #6625. (evsh)
- MACOS: Fix UI responsiveness after AddNewTorrentDialog received metadata. (Brian Kendall)
* Thu Apr 06 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.12 * Thu Apr 06 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.12
- FEATURE: Indicate bitness in stackstrace and about dialog. Closes #6172. (sledgehammer999) - FEATURE: Indicate bitness in stackstrace and about dialog. Closes #6172. (sledgehammer999)
- BUGFIX: Fix incomplete type compile error with Qt4 (Chocobo1) - BUGFIX: Fix incomplete type compile error with Qt4 (Chocobo1)

View File

@@ -93,13 +93,12 @@ foreach(_boost_cmpnt IN LISTS _boost_components)
list(APPEND LibtorrentRasterbar_LIBRARIES "Boost::${_boost_cmpnt}") list(APPEND LibtorrentRasterbar_LIBRARIES "Boost::${_boost_cmpnt}")
endforeach(_boost_cmpnt) endforeach(_boost_cmpnt)
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS})
list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRasterbar_ENCRYPTION_INDEX) list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRasterbar_ENCRYPTION_INDEX)
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1) if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} ${OPENSSL_LIBRARIES}) set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS})
set(LibtorrentRasterbar_OPENSSL_ENABLED ON) set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
endif() endif()

View File

@@ -3,6 +3,9 @@
# sets variables # sets variables
# SYSTEMD_FOUND # SYSTEMD_FOUND
# SYSTEMD_SERVICES_INSTALL_DIR # SYSTEMD_SERVICES_INSTALL_DIR
find_package(PkgConfig QUIET REQUIRED)
if (NOT SYSTEMD_FOUND) if (NOT SYSTEMD_FOUND)
pkg_check_modules(SYSTEMD "systemd") pkg_check_modules(SYSTEMD "systemd")
endif(NOT SYSTEMD_FOUND) endif(NOT SYSTEMD_FOUND)

View File

@@ -0,0 +1,49 @@
###############################################################
#
# Copyright 2011 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License. You may
# obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###############################################################
MACRO (GLIBC_DETECT _VERSION)
# there are multiple ways to detect glibc, but given nmi's
# cons'd up paths I will trust only gcc. I guess I could also use
# ldd --version to detect.
set(_GLIB_SOURCE_DETECT "
#include <limits.h>
#include <stdio.h>
int main()
{
printf(\"%d%d\",__GLIBC__, __GLIBC_MINOR__);
return 0;
}
")
file (WRITE ${CMAKE_CURRENT_BINARY_DIR}/build/cmake/glibc.cpp "${_GLIB_SOURCE_DETECT}\n")
try_run(POST26_GLIBC_DETECTED
POST26_GLIBC_COMPILE
${CMAKE_CURRENT_BINARY_DIR}/build/cmake
${CMAKE_CURRENT_BINARY_DIR}/build/cmake/glibc.cpp
RUN_OUTPUT_VARIABLE GLIBC_VERSION )
if (GLIBC_VERSION AND POST26_GLIBC_COMPILE )
set(${_VERSION} ${GLIBC_VERSION})
else()
message(STATUS "NOTE: Could not detect GLIBC_VERSION from compiler")
endif()
ENDMACRO (GLIBC_DETECT)

View File

@@ -0,0 +1,89 @@
# Sets cache variable QBT_ADDITONAL_FLAGS and QBT_ADDITONAL_CXX_FLAGS to list of additional
# compiler flags for C and C++ (QBT_ADDITONAL_FLAGS) and for C++ only (QBT_ADDITONAL_CXX_FLAGS)
# and appends them to CMAKE_XXX_FLAGS variables.
# It could use add_compile_options(), but then it is needed to use generator expressions,
# and most interesting of them are not compatible with Visual Studio :(
macro(qbt_set_compiler_options)
# if (NOT QBT_ADDITONAL_FLAGS)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
#-Wshadow -Wconversion ?
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
"-Wfloat-equal -Wcast-qual -Wcast-align"
"-Wsign-conversion -Winvalid-pch -Werror=return-type -Wno-long-long"
# -fstack-protector-all
"-Werror -Wno-error=deprecated-declarations"
)
set (_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
"-Woverloaded-virtual -Wold-style-cast -Wstrict-null-sentinel"
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
"-Werror=overloaded-virtual"
# "-Weffc++"
"-Werror -Wno-error=cpp"
# we should modify code to make these ones obsolete
"-Wno-error=old-style-cast -Wno-error=sign-conversion -Wno-error=float-equal"
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
# GCC 4.8 has problems with std::array and its initialization
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wno-error=missing-field-initializers")
endif()
include(CheckCXXCompilerFlag)
# check for -pedantic
check_cxx_compiler_flag(-pedantic _PEDANTIC_IS_SUPPORTED)
if (_PEDANTIC_IS_SUPPORTED)
list(APPEND _GCC_COMMON_CXX_FLAGS "-pedantic -pedantic-errors")
else (_PEDANTIC_IS_SUPPORTED)
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wpedantic")
endif (_PEDANTIC_IS_SUPPORTED)
if (CMAKE_SYSTEM_NAME MATCHES Linux)
# if Glibc version is 2.20 or higher, set -D_DEFAULT_SOURCE
include(MacroGlibcDetect)
message(STATUS "Detecting Glibc version...")
glibc_detect(GLIBC_VERSION)
if(${GLIBC_VERSION})
if(GLIBC_VERSION LESS "220")
message(STATUS "Glibc version is ${GLIBC_VERSION}")
else(GLIBC_VERSION LESS "220")
message(STATUS "Glibc version is ${GLIBC_VERSION}, adding -D_DEFAULT_SOURCE")
add_definitions(-D_DEFAULT_SOURCE)
endif(GLIBC_VERSION LESS "220")
endif(${GLIBC_VERSION})
endif (CMAKE_SYSTEM_NAME MATCHES Linux)
string(REPLACE ";" " " _GCC_COMMON_C_AND_CXX_FLAGS_STRING "${_GCC_COMMON_C_AND_CXX_FLAGS}")
string(REPLACE ";" " " _GCC_COMMON_CXX_FLAGS_STRING "${_GCC_COMMON_CXX_FLAGS}")
string(APPEND CMAKE_C_FLAGS " ${_GCC_COMMON_C_AND_CXX_FLAGS_STRING}")
string(APPEND CMAKE_CXX_FLAGS " ${_GCC_COMMON_C_AND_CXX_FLAGS_STRING} ${_GCC_COMMON_CXX_FLAGS_STRING}")
set(QBT_ADDITONAL_FLAGS "${_GCC_COMMON_C_AND_CXX_FLAGS_STRING}" CACHE STRING
"Additional qBittorent compile flags" FORCE)
set(QBT_ADDITONAL_CXX_FLAGS "${_GCC_COMMON_CXX_FLAGS_STRING}" CACHE STRING
"Additional qBittorent C++ compile flags" FORCE)
# check whether we can enable -Og optimization for debug build
# also let's enable -march=native for debug builds
check_cxx_compiler_flag(-Og _DEBUG_OPTIMIZATION_LEVEL_IS_SUPPORTED)
if (_DEBUG_OPTIMIZATION_LEVEL_IS_SUPPORTED)
string(APPEND CMAKE_C_FLAGS_DEBUG " -Og -g3 -march=native -pipe" )
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og -g3 -march=native -pipe" )
else(_DEBUG_OPTIMIZATION_LEVEL_IS_SUPPORTED)
string(APPEND CMAKE_C_FLAGS_DEBUG " -O0 -g3 -march=native -pipe" )
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O0 -g3 -march=native -pipe" )
endif (_DEBUG_OPTIMIZATION_LEVEL_IS_SUPPORTED)
endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(QBT_ADDITONAL_FLAGS "-wd4290 -wd4275 -wd4251 /W4" CACHE STRING "Additional qBittorent compile flags")
string(APPEND CMAKE_C_FLAGS " ${QBT_ADDITONAL_FLAGS}")
string(APPEND CMAKE_CXX_FLAGS " ${QBT_ADDITONAL_FLAGS}")
endif ()
# endif (NOT QBT_ADDITONAL_FLAGS)
endmacro(qbt_set_compiler_options)

View File

@@ -1,5 +1,7 @@
# Settings for compiling qBittorrent on Windows # Settings for compiling qBittorrent on Windows
list(APPEND CMAKE_LIBRARY_PATH "$ENV{LIB}")
# We want to link with static version of # We want to link with static version of
# libtorrent # libtorrent
set(LibtorrentRasterbar_USE_STATIC_LIBS True) set(LibtorrentRasterbar_USE_STATIC_LIBS True)

18
configure vendored
View File

@@ -5461,12 +5461,12 @@ if test -n "$zlib_CFLAGS"; then
pkg_cv_zlib_CFLAGS="$zlib_CFLAGS" pkg_cv_zlib_CFLAGS="$zlib_CFLAGS"
elif test -n "$PKG_CONFIG"; then elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \ if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.5.2\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.5.2") 2>&5
ac_status=$? ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then test $ac_status = 0; }; then
pkg_cv_zlib_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null` pkg_cv_zlib_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.5.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes test "x$?" != "x0" && pkg_failed=yes
else else
pkg_failed=yes pkg_failed=yes
@@ -5478,12 +5478,12 @@ if test -n "$zlib_LIBS"; then
pkg_cv_zlib_LIBS="$zlib_LIBS" pkg_cv_zlib_LIBS="$zlib_LIBS"
elif test -n "$PKG_CONFIG"; then elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \ if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.5.2\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib") 2>&5 ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.5.2") 2>&5
ac_status=$? ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then test $ac_status = 0; }; then
pkg_cv_zlib_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null` pkg_cv_zlib_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.5.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes test "x$?" != "x0" && pkg_failed=yes
else else
pkg_failed=yes pkg_failed=yes
@@ -5504,14 +5504,14 @@ else
_pkg_short_errors_supported=no _pkg_short_errors_supported=no
fi fi
if test $_pkg_short_errors_supported = yes; then if test $_pkg_short_errors_supported = yes; then
zlib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1` zlib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.5.2" 2>&1`
else else
zlib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1` zlib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.5.2" 2>&1`
fi fi
# Put the nasty error message in config.log where it belongs # Put the nasty error message in config.log where it belongs
echo "$zlib_PKG_ERRORS" >&5 echo "$zlib_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (zlib) were not met: as_fn_error $? "Package requirements (zlib >= 1.2.5.2) were not met:
$zlib_PKG_ERRORS $zlib_PKG_ERRORS

View File

@@ -208,7 +208,7 @@ PKG_CHECK_MODULES(libtorrent,
LIBS="$libtorrent_LIBS $LIBS"]) LIBS="$libtorrent_LIBS $LIBS"])
PKG_CHECK_MODULES(zlib, PKG_CHECK_MODULES(zlib,
[zlib], [zlib >= 1.2.5.2],
[CPPFLAGS="$zlib_CFLAGS $CPPFLAGS" [CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
LIBS="$zlib_LIBS $LIBS"]) LIBS="$zlib_LIBS $LIBS"])

2
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>3.3.12</string> <string>3.3.13</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>qBit</string> <string>qBit</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>

View File

@@ -20,18 +20,20 @@ install(FILES ${MAN_FILES}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
COMPONENT doc) COMPONENT doc)
install(DIRECTORY menuicons/ if (GUI)
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor install(DIRECTORY menuicons/
FILES_MATCHING PATTERN "*.png") DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
FILES_MATCHING PATTERN "*.png")
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.png install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/
COMPONENT data) COMPONENT data)
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qBittorrent.desktop install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
COMPONENT data) COMPONENT data)
install(FILES qBittorrent.appdata.xml install(FILES qbittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/
COMPONENT data) COMPONENT data)
endif()

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2014 sledgehammer999 <sledgehammer999@qbittorrent.org> --> <!-- Copyright 2014 sledgehammer999 <sledgehammer999@qbittorrent.org> -->
<component type="desktop"> <component type="desktop">
<id>qBittorrent.desktop</id> <id>qbittorrent.desktop</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0 and OpenSSL</project_license> <project_license>GPL-2.0 and OpenSSL</project_license>
<name>qBittorrent</name> <name>qBittorrent</name>

View File

@@ -23,7 +23,7 @@ LangString inst_unist ${LANG_GERMAN} "Vorherige Version wird deinstalliert."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_GERMAN} "Starte qBittorrent." LangString launch_qbt ${LANG_GERMAN} "Starte qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_GERMAN} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_GERMAN} "Diese Installation funktioniert nur mit einer 64-bit Version von Windows."
;------------------------------------ ;------------------------------------

View File

@@ -1,53 +1,53 @@
;Installer strings ;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)" ;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_SIMPCHINESE} "qBittorrent (required)" LangString inst_qbt_req ${LANG_SIMPCHINESE} "qBittorrent (必要)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut" ;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_SIMPCHINESE} "Create Desktop Shortcut" LangString inst_dekstop ${LANG_SIMPCHINESE} "创建桌面快捷方式"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_SIMPCHINESE} "Create Start Menu Shortcut" LangString inst_startmenu ${LANG_SIMPCHINESE} "创建开始菜单快捷方式"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_SIMPCHINESE} "Open .torrent files with qBittorrent" LangString inst_torrent ${LANG_SIMPCHINESE} "用 qBittorrent 打开.torrent文件"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_SIMPCHINESE} "Open magnet links with qBittorrent" LangString inst_magnet ${LANG_SIMPCHINESE} " qBittorrent 打开磁力链接"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_SIMPCHINESE} "Add Windows Firewall rule" LangString inst_firewall ${LANG_SIMPCHINESE} "添加Windows防火墙规则"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_SIMPCHINESE} "Adding Windows Firewall rule" LangString inst_firewallinfo ${LANG_SIMPCHINESE} "正在添加Windows防火墙规则"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_SIMPCHINESE} "qBittorrent is running. Please close the application before installing." LangString inst_warning ${LANG_SIMPCHINESE} "qBittorrent 正在运行。 安装前请关闭应用程序。"
;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings." ;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
LangString inst_uninstall_question ${LANG_SIMPCHINESE} "A previous installation was detected. It will be uninstalled without deleting user settings." LangString inst_uninstall_question ${LANG_SIMPCHINESE} "检测到以前的安装。 它将被卸载但不删除用户设置。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SIMPCHINESE} "Uninstalling previous version." LangString inst_unist ${LANG_SIMPCHINESE} "卸载以前的版本。"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SIMPCHINESE} "Launch qBittorrent." LangString launch_qbt ${LANG_SIMPCHINESE} "启动 qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SIMPCHINESE} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_SIMPCHINESE} "此安装程序只能在64位的Windows上工作。"
;------------------------------------ ;------------------------------------
;Uninstaller strings ;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files" ;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_SIMPCHINESE} "Remove files" LangString remove_files ${LANG_SIMPCHINESE} "删除文件"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts" ;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_SIMPCHINESE} "Remove shortcuts" LangString remove_shortcuts ${LANG_SIMPCHINESE} "删除快捷方式"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations" ;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_SIMPCHINESE} "Remove file associations" LangString remove_associations ${LANG_SIMPCHINESE} "删除文件关联"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys" ;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_SIMPCHINESE} "Remove registry keys" LangString remove_registry ${LANG_SIMPCHINESE} "删除注册表键"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files" ;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_SIMPCHINESE} "Remove configuration files" LangString remove_conf ${LANG_SIMPCHINESE} "删除配置文件"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule" ;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_SIMPCHINESE} "Remove Windows Firewall rule" LangString remove_firewall ${LANG_SIMPCHINESE} "删除Windows防火墙规则"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule" ;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_SIMPCHINESE} "Removing Windows Firewall rule" LangString remove_firewallinfo ${LANG_SIMPCHINESE} "正在删除Windows防火墙规则"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_SIMPCHINESE} "Remove torrents and cached data" LangString remove_cache ${LANG_SIMPCHINESE} "删除种子和缓存数据"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_SIMPCHINESE} "qBittorrent is running. Please close the application before uninstalling." LangString uninst_warning ${LANG_SIMPCHINESE} "qBittorrent 正在运行。 卸载前请关闭程序。"
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_SIMPCHINESE} "Not removing .torrent association. It is associated with:" LangString uninst_tor_warn ${LANG_SIMPCHINESE} "不删除 .torrent 关联。 关联的是:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_SIMPCHINESE} "Not removing magnet association. It is associated with:" LangString uninst_mag_warn ${LANG_SIMPCHINESE} "不删除磁力关联。 关联的是:"

View File

@@ -23,7 +23,7 @@ LangString inst_unist ${LANG_SPANISH} "Desinstalando la versión anterior."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SPANISH} "Iniciar qBittorrent." LangString launch_qbt ${LANG_SPANISH} "Iniciar qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SPANISH} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_SPANISH} "Este instalador solo funciona en versiones de 64-bit de Windows."
;------------------------------------ ;------------------------------------

View File

@@ -23,7 +23,7 @@ LangString inst_unist ${LANG_SPANISHINTERNATIONAL} "Desinstalando la versión an
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SPANISHINTERNATIONAL} "Iniciar qBittorrent." LangString launch_qbt ${LANG_SPANISHINTERNATIONAL} "Iniciar qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SPANISHINTERNATIONAL} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_SPANISHINTERNATIONAL} "Este instalador solo funciona en versiones de 64-bit de Windows."
;------------------------------------ ;------------------------------------

View File

@@ -1,53 +1,53 @@
;Installer strings ;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)" ;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_UKRAINIAN} "qBittorrent (required)" LangString inst_qbt_req ${LANG_UKRAINIAN} "qBittorrent (необхідний)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut" ;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_UKRAINIAN} "Create Desktop Shortcut" LangString inst_dekstop ${LANG_UKRAINIAN} "Створити ярлик на стільниці"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_UKRAINIAN} "Create Start Menu Shortcut" LangString inst_startmenu ${LANG_UKRAINIAN} "Створити ярлик в меню Пуск"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_UKRAINIAN} "Open .torrent files with qBittorrent" LangString inst_torrent ${LANG_UKRAINIAN} "Відкривати .torrent файли за допомогою qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_UKRAINIAN} "Open magnet links with qBittorrent" LangString inst_magnet ${LANG_UKRAINIAN} "Відкривати посилання magnet за допомогою qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_UKRAINIAN} "Add Windows Firewall rule" LangString inst_firewall ${LANG_UKRAINIAN} "Додати правило в Windows брандмауер"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_UKRAINIAN} "Adding Windows Firewall rule" LangString inst_firewallinfo ${LANG_UKRAINIAN} "Додаємо правило до брандмауера"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_UKRAINIAN} "qBittorrent is running. Please close the application before installing." LangString inst_warning ${LANG_UKRAINIAN} "qBittorrent вже виконується. Будь ласка, закрийте застосунок перед запуском інсталятору."
;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings." ;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings."
LangString inst_uninstall_question ${LANG_UKRAINIAN} "A previous installation was detected. It will be uninstalled without deleting user settings." LangString inst_uninstall_question ${LANG_UKRAINIAN} "Виявлено попередню інсталяцію. Її буде видалено за виключенням користувацьких налаштувань."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_UKRAINIAN} "Uninstalling previous version." LangString inst_unist ${LANG_UKRAINIAN} "Видалення попередньої версії."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_UKRAINIAN} "Launch qBittorrent." LangString launch_qbt ${LANG_UKRAINIAN} "Запустити qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_UKRAINIAN} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_UKRAINIAN} "Ця програма установки працює тільки в 64-розрядних версіях Windows."
;------------------------------------ ;------------------------------------
;Uninstaller strings ;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files" ;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_UKRAINIAN} "Remove files" LangString remove_files ${LANG_UKRAINIAN} "Видалити файли"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts" ;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_UKRAINIAN} "Remove shortcuts" LangString remove_shortcuts ${LANG_UKRAINIAN} "Видалити ярлики"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations" ;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_UKRAINIAN} "Remove file associations" LangString remove_associations ${LANG_UKRAINIAN} "Видалити файлові асоціації"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys" ;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_UKRAINIAN} "Remove registry keys" LangString remove_registry ${LANG_UKRAINIAN} "Видалити ключі реєстру"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files" ;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_UKRAINIAN} "Remove configuration files" LangString remove_conf ${LANG_UKRAINIAN} "Видалити користувацькі налаштування"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule" ;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_UKRAINIAN} "Remove Windows Firewall rule" LangString remove_firewall ${LANG_UKRAINIAN} "Видалити правило брандмауера Windows"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule" ;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_UKRAINIAN} "Removing Windows Firewall rule" LangString remove_firewallinfo ${LANG_UKRAINIAN} "Видаляємо правило брандмауера Windows"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_UKRAINIAN} "Remove torrents and cached data" LangString remove_cache ${LANG_UKRAINIAN} "Видалити торренти та кешовані дані"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_UKRAINIAN} "qBittorrent is running. Please close the application before uninstalling." LangString uninst_warning ${LANG_UKRAINIAN} "qBittorrent виконується. Будь ласка, закрийте застосунок перед деінсталляцією."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_UKRAINIAN} "Not removing .torrent association. It is associated with:" LangString uninst_tor_warn ${LANG_UKRAINIAN} "Не видаляємо асоціацію .torrent файлів. Вони асоційовані з:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_UKRAINIAN} "Not removing magnet association. It is associated with:" LangString uninst_mag_warn ${LANG_UKRAINIAN} "Не видаляємо асоціацію magnet-посилань. Вони асоційовані з:"

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 "3.3.12" !define PROG_VERSION "3.3.13"
!define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun

View File

@@ -2,6 +2,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD "11") set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES) add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroQbtCompilerSettings)
qbt_set_compiler_options()
include(MacroLinkQtComponents) include(MacroLinkQtComponents)
include(QbtTargetSources) include(QbtTargetSources)
@@ -28,6 +31,7 @@ else (QT5)
endif (DBUS) endif (DBUS)
find_package(Qt4 4.8.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED) find_package(Qt4 4.8.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
include(${QT_USE_FILE}) include(${QT_USE_FILE})
add_definitions(-DQStringLiteral=QLatin1String)
endif (QT5) endif (QT5)
set(CMAKE_AUTOMOC True) set(CMAKE_AUTOMOC True)

View File

@@ -38,6 +38,7 @@ endforeach()
set(QBT_APP_RESOURCES set(QBT_APP_RESOURCES
../icons.qrc ../icons.qrc
../searchengine.qrc
"${_lang_qrc_dst}" "${_lang_qrc_dst}"
) )
@@ -55,7 +56,7 @@ if (WIN32)
else (MINGW) else (MINGW)
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc) list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
endif (MINGW) endif (MINGW)
list(APPEND QBT_APP_SOURCES ../qbittorent.exe.manifest) list(APPEND QBT_APP_SOURCES ../qbittorrent.exe.manifest)
endif (WIN32) endif (WIN32)
if (UNIX) if (UNIX)

View File

@@ -111,17 +111,20 @@ Application::Application(const QString &id, int &argc, char **argv)
QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
} }
#endif #endif
setApplicationName("qBittorrent"); setApplicationName("qBittorrent");
initializeTranslation(); initializeTranslation();
#ifndef DISABLE_GUI
#if !defined(DISABLE_GUI)
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
#endif // QBT_USES_QT5 #endif // QBT_USES_QT5
setQuitOnLastWindowClosed(false); setQuitOnLastWindowClosed(false);
#ifdef Q_OS_WIN #endif
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection); connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
#endif // Q_OS_WIN #endif
#endif // DISABLE_GUI
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &))); connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup())); connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
@@ -441,6 +444,9 @@ int Application::exec(const QStringList &params)
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();
} }

View File

@@ -40,6 +40,7 @@
#include <QPen> #include <QPen>
#include <QPushButton> #include <QPushButton>
#include <QSplashScreen> #include <QSplashScreen>
#ifdef QBT_STATIC_QT #ifdef QBT_STATIC_QT
#include <QtPlugin> #include <QtPlugin>
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
@@ -120,15 +121,24 @@ struct QBtCommandLineParameters
} }
}; };
#ifndef DISABLE_GUI #if !defined Q_OS_WIN && !defined Q_OS_HAIKU
void showSplashScreen(); void reportToUser(const char* str);
#endif #endif
void displayVersion(); void displayVersion();
void displayUsage(const QString &prg_name); void displayUsage(const QString &prg_name);
bool userAgreesWithLegalNotice(); bool userAgreesWithLegalNotice();
void displayBadArgMessage(const QString &message); void displayBadArgMessage(const QString &message);
QBtCommandLineParameters parseCommandLine(); QBtCommandLineParameters parseCommandLine();
#if !defined(DISABLE_GUI)
void showSplashScreen();
#if defined(Q_OS_UNIX)
void setupDpi();
#endif // Q_OS_UNIX
#endif // DISABLE_GUI
// Main // Main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@@ -141,6 +151,11 @@ int main(int argc, char *argv[])
macMigratePlists(); macMigratePlists();
#endif #endif
#if !defined(DISABLE_GUI) && defined(Q_OS_UNIX)
setupDpi();
#endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
migrateRSS(); migrateRSS();
#endif #endif
@@ -342,6 +357,17 @@ QBtCommandLineParameters parseCommandLine()
return result; return result;
} }
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
void reportToUser(const char* str)
{
const size_t strLen = strlen(str);
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
auto dummy = write(STDOUT_FILENO, str, strLen);
Q_UNUSED(dummy);
}
}
#endif
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) #if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
void sigNormalHandler(int signum) void sigNormalHandler(int signum)
{ {
@@ -349,9 +375,9 @@ void sigNormalHandler(int signum)
const char str1[] = "Catching signal: "; const char str1[] = "Catching signal: ";
const char *sigName = sysSigName[signum]; const char *sigName = sysSigName[signum];
const char str2[] = "\nExiting cleanly\n"; const char str2[] = "\nExiting cleanly\n";
write(STDERR_FILENO, str1, strlen(str1)); reportToUser(str1);
write(STDERR_FILENO, sigName, strlen(sigName)); reportToUser(sigName);
write(STDERR_FILENO, str2, strlen(str2)); reportToUser(str2);
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU #endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
signal(signum, SIG_DFL); signal(signum, SIG_DFL);
qApp->exit(); // unsafe, but exit anyway qApp->exit(); // unsafe, but exit anyway
@@ -364,9 +390,9 @@ void sigAbnormalHandler(int signum)
const char *sigName = sysSigName[signum]; const char *sigName = sysSigName[signum];
const char str2[] = "\nPlease file a bug report at http://bug.qbittorrent.org and provide the following information:\n\n" const char str2[] = "\nPlease file a bug report at http://bug.qbittorrent.org and provide the following information:\n\n"
"qBittorrent version: " QBT_VERSION "\n"; "qBittorrent version: " QBT_VERSION "\n";
write(STDERR_FILENO, str1, strlen(str1)); reportToUser(str1);
write(STDERR_FILENO, sigName, strlen(sigName)); reportToUser(sigName);
write(STDERR_FILENO, str2, strlen(str2)); reportToUser(str2);
print_stacktrace(); // unsafe print_stacktrace(); // unsafe
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU #endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
#ifdef STACKTRACE_WIN #ifdef STACKTRACE_WIN
@@ -379,7 +405,7 @@ void sigAbnormalHandler(int signum)
} }
#endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) #endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
#ifndef DISABLE_GUI #if !defined(DISABLE_GUI)
void showSplashScreen() void showSplashScreen()
{ {
QPixmap splash_img(":/icons/skin/splash.png"); QPixmap splash_img(":/icons/skin/splash.png");
@@ -393,7 +419,15 @@ void showSplashScreen()
QTimer::singleShot(1500, splash, SLOT(deleteLater())); QTimer::singleShot(1500, splash, SLOT(deleteLater()));
qApp->processEvents(); qApp->processEvents();
} }
#endif
#if defined(Q_OS_UNIX)
void setupDpi()
{
if (qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
}
#endif // Q_OS_UNIX
#endif // DISABLE_GUI
void displayVersion() void displayVersion()
{ {

View File

@@ -9,16 +9,18 @@
#include <execinfo.h> #include <execinfo.h>
#include <cxxabi.h> #include <cxxabi.h>
#include <vector>
/** Print a demangled stack backtrace of the caller function to FILE* out. */ /** Print a demangled stack backtrace of the caller function to FILE* out. */
static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames = 63) static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames = 63)
{ {
fprintf(out, "stack trace:\n"); fprintf(out, "stack trace:\n");
// storage array for stack trace address data // storage array for stack trace address data
void *addrlist[max_frames + 1]; std::vector<void *> addrlist(max_frames + 1);
// retrieve current stack addresses // retrieve current stack addresses
int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void *)); int addrlen = backtrace(addrlist.data(), addrlist.size());
if (addrlen == 0) { if (addrlen == 0) {
fprintf(out, " <empty, possibly corrupt>\n"); fprintf(out, " <empty, possibly corrupt>\n");
@@ -27,7 +29,7 @@ static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames
// resolve addresses into strings containing "filename(function+address)", // resolve addresses into strings containing "filename(function+address)",
// this array must be free()-ed // this array must be free()-ed
char * *symbollist = backtrace_symbols(addrlist, addrlen); char * *symbollist = backtrace_symbols(addrlist.data(), addrlen);
// allocate string which will be filled with the demangled function name // allocate string which will be filled with the demangled function name
size_t funcnamesize = 256; size_t funcnamesize = 256;

View File

@@ -1,4 +1,4 @@
find_package(ZLIB REQUIRED) find_package(ZLIB 1.2.5.2 REQUIRED)
set(QBT_BASE_HEADERS set(QBT_BASE_HEADERS
bittorrent/cachestatus.h bittorrent/cachestatus.h

View File

@@ -34,7 +34,6 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QDirIterator>
#include <QHostAddress> #include <QHostAddress>
#include <QNetworkAddressEntry> #include <QNetworkAddressEntry>
#include <QNetworkInterface> #include <QNetworkInterface>
@@ -155,16 +154,6 @@ namespace
return expanded; return expanded;
} }
QStringList findAllFiles(const QString &dirPath)
{
QStringList files;
QDirIterator it(dirPath, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
files << it.next();
return files;
}
template <typename T> template <typename T>
struct LowerLimited struct LowerLimited
{ {
@@ -425,7 +414,6 @@ Session::Session(QObject *parent)
Net::PortForwarder::initInstance(m_nativeSession); Net::PortForwarder::initInstance(m_nativeSession);
qDebug("* BitTorrent Session constructed"); qDebug("* BitTorrent Session constructed");
startUpTorrents();
} }
bool Session::isDHTEnabled() const bool Session::isDHTEnabled() const
@@ -858,9 +846,6 @@ void Session::adjustLimits()
void Session::configure() void Session::configure()
{ {
qDebug("Configuring session"); qDebug("Configuring session");
if (!m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early
QWriteLocker locker(&m_lock);
if (!m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock
#if LIBTORRENT_VERSION_NUM < 10100 #if LIBTORRENT_VERSION_NUM < 10100
libt::session_settings sessionSettings = m_nativeSession->settings(); libt::session_settings sessionSettings = m_nativeSession->settings();
configure(sessionSettings); configure(sessionSettings);
@@ -1392,7 +1377,14 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles)
// Remove it from session // Remove it from session
if (deleteLocalFiles) { if (deleteLocalFiles) {
m_savePathsToRemove[torrent->hash()] = torrent->rootPath(true); if (torrent->savePath(true) == torrentTempPath(torrent->hash())) {
m_savePathsToRemove[torrent->hash()] = torrent->savePath(true);
}
else {
QString rootPath = torrent->rootPath(true);
if (!rootPath.isEmpty())
m_savePathsToRemove[torrent->hash()] = rootPath;
}
m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files); m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files);
} }
else { else {
@@ -1711,35 +1703,19 @@ bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) c
{ {
auto findInDir = [](const QString &dirPath, TorrentInfo &torrentInfo) -> bool auto findInDir = [](const QString &dirPath, TorrentInfo &torrentInfo) -> bool
{ {
const QDir dir(dirPath);
bool found = false; bool found = false;
if (torrentInfo.filesCount() == 1) { for (int i = 0; i < torrentInfo.filesCount(); ++i) {
const QString filePath = dirPath + torrentInfo.filePath(0); const QString filePath = torrentInfo.filePath(i);
if (QFile(filePath).exists()) { if (dir.exists(filePath)) {
found = true; found = true;
} }
else if (QFile(filePath + QB_EXT).exists()) { else if (dir.exists(filePath + QB_EXT)) {
found = true; found = true;
torrentInfo.renameFile(0, torrentInfo.filePath(0) + QB_EXT); torrentInfo.renameFile(i, filePath + QB_EXT);
}
}
else {
QSet<QString> allFiles;
int dirPathSize = dirPath.size();
foreach (const QString &file, findAllFiles(dirPath + torrentInfo.name()))
allFiles << file.mid(dirPathSize);
for (int i = 0; i < torrentInfo.filesCount(); ++i) {
QString filePath = torrentInfo.filePath(i);
if (allFiles.contains(filePath)) {
found = true;
}
else {
filePath += QB_EXT;
if (allFiles.contains(filePath)) {
found = true;
torrentInfo.renameFile(i, filePath);
}
}
} }
if ((i % 100) == 0)
qApp->processEvents();
} }
return found; return found;
@@ -2967,12 +2943,10 @@ void Session::initResumeFolder()
void Session::configureDeferred() void Session::configureDeferred()
{ {
if (m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early if (!m_deferredConfigureScheduled) {
QWriteLocker locker(&m_lock); QMetaObject::invokeMethod(this, "configure", Qt::QueuedConnection);
if (m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock m_deferredConfigureScheduled = true;
}
QMetaObject::invokeMethod(this, "configure", Qt::QueuedConnection);
m_deferredConfigureScheduled = true;
} }
// Enable IP Filtering // Enable IP Filtering
@@ -3364,17 +3338,16 @@ void Session::handleTorrentRemovedAlert(libt::torrent_removed_alert *p)
void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p) void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p)
{ {
m_savePathsToRemove.remove(p->info_hash); const QString path = m_savePathsToRemove.take(p->info_hash);
if (path == torrentTempPath(p->info_hash))
Utils::Fs::smartRemoveEmptyFolderTree(path);
} }
void Session::handleTorrentDeleteFailedAlert(libt::torrent_delete_failed_alert *p) void Session::handleTorrentDeleteFailedAlert(libt::torrent_delete_failed_alert *p)
{ {
// libtorrent won't delete the directory if it contains files not listed in the torrent, // libtorrent won't delete the directory if it contains files not listed in the torrent,
// so we remove the directory ourselves // so we remove the directory ourselves
if (m_savePathsToRemove.contains(p->info_hash)) { Utils::Fs::smartRemoveEmptyFolderTree(m_savePathsToRemove.take(p->info_hash));
QString path = m_savePathsToRemove.take(p->info_hash);
Utils::Fs::smartRemoveEmptyFolderTree(path);
}
} }
void Session::handleMetadataReceivedAlert(libt::metadata_received_alert *p) void Session::handleMetadataReceivedAlert(libt::metadata_received_alert *p)

View File

@@ -41,7 +41,6 @@
#endif #endif
#include <QNetworkConfigurationManager> #include <QNetworkConfigurationManager>
#include <QPointer> #include <QPointer>
#include <QReadWriteLock>
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
#include <QWaitCondition> #include <QWaitCondition>
@@ -327,6 +326,7 @@ namespace BitTorrent
bool isTrackerFilteringEnabled() const; bool isTrackerFilteringEnabled() const;
void setTrackerFilteringEnabled(bool enabled); void setTrackerFilteringEnabled(bool enabled);
void startUpTorrents();
TorrentHandle *findTorrent(const InfoHash &hash) const; TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const; QHash<InfoHash, TorrentHandle *> torrents() const;
TorrentStatusReport torrentStatusReport() const; TorrentStatusReport torrentStatusReport() const;
@@ -457,7 +457,6 @@ namespace BitTorrent
void enableIPFilter(); void enableIPFilter();
void disableIPFilter(); void disableIPFilter();
void startUpTorrents();
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri, bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
TorrentInfo torrentInfo = TorrentInfo(), TorrentInfo torrentInfo = TorrentInfo(),
const QByteArray &fastresumeData = QByteArray()); const QByteArray &fastresumeData = QByteArray());
@@ -610,8 +609,6 @@ namespace BitTorrent
QNetworkConfigurationManager m_networkManager; QNetworkConfigurationManager m_networkManager;
mutable QReadWriteLock m_lock;
static Session *m_instance; static Session *m_instance;
}; };
} }

View File

@@ -1330,6 +1330,10 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
} }
qDebug("Torrent is successfully moved from %s to %s", qPrintable(m_oldPath), qPrintable(m_newPath)); qDebug("Torrent is successfully moved from %s to %s", qPrintable(m_oldPath), qPrintable(m_newPath));
if (m_oldPath == m_session->torrentTempPath(hash())) {
qDebug() << "Removing torrent temp folder:" << m_oldPath;
Utils::Fs::smartRemoveEmptyFolderTree(m_oldPath);
}
updateStatus(); updateStatus();
m_newPath.clear(); m_newPath.clear();

View File

@@ -246,6 +246,21 @@ QVector<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
return res; return res;
} }
QVector<QByteArray> TorrentInfo::pieceHashes() const
{
if (!isValid())
return {};
const int count = piecesCount();
QVector<QByteArray> hashes;
hashes.reserve(count);
for (int i = 0; i < count; ++i)
hashes += { m_nativeInfo->hash_for_piece_ptr(i), libtorrent::sha1_hash::size };
return hashes;
}
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
{ {
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct

View File

@@ -91,6 +91,7 @@ namespace BitTorrent
QByteArray metadata() const; QByteArray metadata() const;
QStringList filesForPiece(int pieceIndex) const; QStringList filesForPiece(int pieceIndex) const;
QVector<int> fileIndicesForPiece(int pieceIndex) const; QVector<int> fileIndicesForPiece(int pieceIndex) const;
QVector<QByteArray> pieceHashes() const;
using PieceRange = IndexRange<int>; using PieceRange = IndexRange<int>;
// returns pair of the first and the last pieces into which // returns pair of the first and the last pieces into which

View File

@@ -29,14 +29,17 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include <QTcpSocket> #include "connection.h"
#include <QDebug>
#include <QRegExp> #include <QRegExp>
#include "types.h" #ifndef QBT_USES_QT5
#include <QStringList>
#endif
#include <QTcpSocket>
#include "irequesthandler.h"
#include "requestparser.h" #include "requestparser.h"
#include "responsegenerator.h" #include "responsegenerator.h"
#include "irequesthandler.h"
#include "connection.h"
using namespace Http; using namespace Http;
@@ -46,27 +49,33 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
, m_requestHandler(requestHandler) , m_requestHandler(requestHandler)
{ {
m_socket->setParent(this); m_socket->setParent(this);
m_idleTimer.start();
connect(m_socket, SIGNAL(readyRead()), SLOT(read())); connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
} }
Connection::~Connection() Connection::~Connection()
{ {
m_socket->close();
} }
void Connection::read() void Connection::read()
{ {
m_receivedData.append(m_socket->readAll()); m_idleTimer.restart();
m_receivedData.append(m_socket->readAll());
Request request; Request request;
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); // TODO: transform request headers to lowercase
switch (err) { switch (err) {
case RequestParser::IncompleteRequest: case RequestParser::IncompleteRequest:
// Partial request waiting for the rest // Partial request waiting for the rest
break; break;
case RequestParser::BadRequest: case RequestParser::BadRequest:
sendResponse(Response(400, "Bad Request")); sendResponse(Response(400, "Bad Request"));
m_receivedData.clear();
break; break;
case RequestParser::NoError: case RequestParser::NoError:
Environment env; Environment env;
env.clientAddress = m_socket->peerAddress(); env.clientAddress = m_socket->peerAddress();
@@ -74,25 +83,69 @@ void Connection::read()
if (acceptsGzipEncoding(request.headers["accept-encoding"])) if (acceptsGzipEncoding(request.headers["accept-encoding"]))
response.headers[HEADER_CONTENT_ENCODING] = "gzip"; response.headers[HEADER_CONTENT_ENCODING] = "gzip";
sendResponse(response); sendResponse(response);
m_receivedData.clear();
break; break;
} }
} }
void Connection::sendResponse(const Response &response) void Connection::sendResponse(const Response &response)
{ {
m_socket->write(ResponseGenerator::generate(response)); m_socket->write(toByteArray(response));
m_socket->disconnectFromHost(); m_socket->close(); // TODO: remove when HTTP pipelining is supported
} }
bool Connection::acceptsGzipEncoding(const QString &encoding) bool Connection::hasExpired(const qint64 timeout) const
{ {
QRegExp rx("(gzip)(;q=([^,]+))?"); return m_idleTimer.hasExpired(timeout);
if (rx.indexIn(encoding) >= 0) { }
if (rx.cap(2).size() > 0)
// check if quality factor > 0 bool Connection::isClosed() const
return (rx.cap(3).toDouble() > 0); {
// if quality factor is not specified, then it's 1 return (m_socket->state() == QAbstractSocket::UnconnectedState);
}
bool Connection::acceptsGzipEncoding(QString codings)
{
// [rfc7231] 5.3.4. Accept-Encoding
const auto isCodingAvailable = [](const QStringList &list, const QString &encoding) -> bool
{
for (const QString &str: list) {
if (!str.startsWith(encoding))
continue;
// without quality values
if (str == encoding)
return true;
// [rfc7231] 5.3.1. Quality Values
#ifdef QBT_USES_QT5
const QStringRef substr = str.midRef(encoding.size() + 3); // ex. skip over "gzip;q="
#else
const QString substr = str.mid(encoding.size() + 3); // ex. skip over "gzip;q="
#endif
bool ok = false;
const double qvalue = substr.toDouble(&ok);
if (!ok || (qvalue <= 0.0))
return false;
return true;
}
return false;
};
const QStringList list = codings.remove(' ').remove('\t').split(',', QString::SkipEmptyParts);
if (list.isEmpty())
return false;
const bool canGzip = isCodingAvailable(list, QLatin1String("gzip"));
if (canGzip)
return true; return true;
}
const bool canAny = isCodingAvailable(list, QLatin1String("*"));
if (canAny)
return true;
return false; return false;
} }

View File

@@ -33,12 +33,12 @@
#ifndef HTTP_CONNECTION_H #ifndef HTTP_CONNECTION_H
#define HTTP_CONNECTION_H #define HTTP_CONNECTION_H
#include <QElapsedTimer>
#include <QObject> #include <QObject>
#include "types.h" #include "types.h"
QT_BEGIN_NAMESPACE
class QTcpSocket; class QTcpSocket;
QT_END_NAMESPACE
namespace Http namespace Http
{ {
@@ -53,16 +53,20 @@ namespace Http
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
~Connection(); ~Connection();
bool hasExpired(qint64 timeout) const;
bool isClosed() const;
private slots: private slots:
void read(); void read();
private: private:
static bool acceptsGzipEncoding(const QString &encoding); static bool acceptsGzipEncoding(QString codings);
void sendResponse(const Response &response); void sendResponse(const Response &response);
QTcpSocket *m_socket; QTcpSocket *m_socket;
IRequestHandler *m_requestHandler; IRequestHandler *m_requestHandler;
QByteArray m_receivedData; QByteArray m_receivedData;
QElapsedTimer m_idleTimer;
}; };
} }

View File

@@ -198,14 +198,14 @@ QList<QByteArray> RequestParser::splitMultipartData(const QByteArray& data, cons
start = end + sepLength; // skip first boundary start = end + sepLength; // skip first boundary
while ((end = data.indexOf(sep, start)) >= 0) { while ((end = data.indexOf(sep, start)) >= 0) {
ret << data.mid(start, end - start); ret << data.mid(start, end - EOL.length() - start);
start = end + sepLength; start = end + sepLength;
} }
// last or single part // last or single part
sep = boundary + "--" + EOL; sep = boundary + "--" + EOL;
if ((end = data.indexOf(sep, start)) >= 0) if ((end = data.indexOf(sep, start)) >= 0)
ret << data.mid(start, end - start); ret << data.mid(start, end - EOL.length() - start);
} }
return ret; return ret;

View File

@@ -29,39 +29,82 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "base/utils/gzip.h"
#include "responsegenerator.h" #include "responsegenerator.h"
using namespace Http; #include <QDateTime>
#ifndef QBT_USES_QT5
#include <QLocale>
#endif
QByteArray ResponseGenerator::generate(Response response) #include "base/utils/gzip.h"
QByteArray Http::toByteArray(Response response)
{ {
if (response.headers[HEADER_CONTENT_ENCODING] == "gzip") { compressContent(response);
// A gzip seems to have 23 bytes overhead.
// Also "Content-Encoding: gzip\r\n" is 26 bytes long
// So we only benefit from gzip if the message is bigger than 23+26 = 49
// If the message is smaller than 49 bytes we actually send MORE data if we gzip
QByteArray dest_buf;
if ((response.content.size() > 49) && (Utils::Gzip::compress(response.content, dest_buf)))
response.content = dest_buf;
else
response.headers.remove(HEADER_CONTENT_ENCODING);
}
if (response.content.length() > 0) response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length()); response.headers[HEADER_DATE] = httpDate();
QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n")); QByteArray buf;
buf.reserve(10 * 1024);
QString header; // Status Line
foreach (const QString& key, response.headers.keys()) buf += QString("HTTP/%1 %2 %3")
header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]); .arg("1.1", // TODO: depends on request
QString::number(response.status.code),
response.status.text)
.toLatin1()
.append(CRLF);
ret = ret.arg(response.status.code).arg(response.status.text).arg(header); // Header Fields
for (auto i = response.headers.constBegin(); i != response.headers.constEnd(); ++i)
buf += QString("%1: %2").arg(i.key(), i.value()).toLatin1().append(CRLF);
// qDebug() << Q_FUNC_INFO; // the first empty line
// qDebug() << "HTTP Response header:"; buf += CRLF;
// qDebug() << ret;
return ret.toUtf8() + response.content; // message body // TODO: support HEAD request
buf += response.content;
return buf;
}
QString Http::httpDate()
{
// [RFC 7231] 7.1.1.1. Date/Time Formats
// example: "Sun, 06 Nov 1994 08:49:37 GMT"
return QLocale::c().toString(QDateTime::currentDateTimeUtc(), QLatin1String("ddd, dd MMM yyyy HH:mm:ss"))
.append(QLatin1String(" GMT"));
}
void Http::compressContent(Response &response)
{
if (response.headers.value(HEADER_CONTENT_ENCODING) != QLatin1String("gzip"))
return;
response.headers.remove(HEADER_CONTENT_ENCODING);
// for very small files, compressing them only wastes cpu cycles
const int contentSize = response.content.size();
if (contentSize <= 1024) // 1 kb
return;
// filter out known hard-to-compress types
const QString contentType = response.headers[HEADER_CONTENT_TYPE];
if ((contentType == CONTENT_TYPE_GIF) || (contentType == CONTENT_TYPE_PNG))
return;
// try compressing
bool ok = false;
const QByteArray compressedData = Utils::Gzip::compress(response.content, 6, &ok);
if (!ok)
return;
// "Content-Encoding: gzip\r\n" is 24 bytes long
if ((compressedData.size() + 24) >= contentSize)
return;
response.content = compressedData;
response.headers[HEADER_CONTENT_ENCODING] = QLatin1String("gzip");
} }

View File

@@ -37,11 +37,9 @@
namespace Http namespace Http
{ {
class ResponseGenerator QByteArray toByteArray(Response response);
{ QString httpDate();
public: void compressContent(Response &response);
static QByteArray generate(Response response);
};
} }
#endif // HTTP_RESPONSEGENERATOR_H #endif // HTTP_RESPONSEGENERATOR_H

View File

@@ -30,8 +30,10 @@
#include "server.h" #include "server.h"
#include <QMutableListIterator>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QStringList> #include <QStringList>
#include <QTimer>
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
#include <QSslSocket> #include <QSslSocket>
@@ -41,6 +43,10 @@
#include "connection.h" #include "connection.h"
static const int KEEP_ALIVE_DURATION = 7; // seconds
static const int CONNECTIONS_LIMIT = 500;
static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
using namespace Http; using namespace Http;
Server::Server(IRequestHandler *requestHandler, QObject *parent) Server::Server(IRequestHandler *requestHandler, QObject *parent)
@@ -54,34 +60,24 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
QSslSocket::setDefaultCiphers(safeCipherList()); QSslSocket::setDefaultCiphers(safeCipherList());
#endif #endif
QTimer *dropConnectionTimer = new QTimer(this);
connect(dropConnectionTimer, SIGNAL(timeout()), SLOT(dropTimedOutConnection()));
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
} }
Server::~Server() Server::~Server()
{ {
} }
#ifndef QT_NO_OPENSSL
void Server::enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key)
{
m_certificates = certificates;
m_key = key;
m_https = true;
}
void Server::disableHttps()
{
m_https = false;
m_certificates.clear();
m_key.clear();
}
#endif
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
void Server::incomingConnection(qintptr socketDescriptor) void Server::incomingConnection(qintptr socketDescriptor)
#else #else
void Server::incomingConnection(int socketDescriptor) void Server::incomingConnection(int socketDescriptor)
#endif #endif
{ {
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
QTcpSocket *serverSocket; QTcpSocket *serverSocket;
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
if (m_https) if (m_https)
@@ -90,28 +86,79 @@ void Server::incomingConnection(int socketDescriptor)
#endif #endif
serverSocket = new QTcpSocket(this); serverSocket = new QTcpSocket(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) { if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
#ifndef QT_NO_OPENSSL delete serverSocket;
if (m_https) { return;
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
#ifdef QBT_USES_QT5
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
#else
static_cast<QSslSocket *>(serverSocket)->setLocalCertificate(m_certificates.first());
#endif
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
#endif
new Connection(serverSocket, m_requestHandler, this);
} }
else {
serverSocket->deleteLater(); #ifndef QT_NO_OPENSSL
if (m_https) {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
#ifdef QBT_USES_QT5
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
#else
static_cast<QSslSocket *>(serverSocket)->setLocalCertificate(m_certificates.first());
#endif
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
#endif
Connection *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.append(c);
}
void Server::dropTimedOutConnection()
{
QMutableListIterator<Connection *> i(m_connections);
while (i.hasNext()) {
auto connection = i.next();
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
delete connection;
i.remove();
}
} }
} }
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &key)
{
QSslKey sslKey(key, QSsl::Rsa);
if (sslKey.isNull())
#ifdef QBT_USES_QT5
sslKey = QSslKey(key, QSsl::Ec);
#else
{
disableHttps();
return false;
}
#endif
const QList<QSslCertificate> certs = QSslCertificate::fromData(certificates);
const bool areCertsValid = !certs.empty() && std::all_of(certs.begin(), certs.end(), [](const QSslCertificate &c) { return !c.isNull(); });
if (!sslKey.isNull() && areCertsValid)
{
m_key = sslKey;
m_certificates = certs;
m_https = true;
return true;
}
else
{
disableHttps();
return false;
}
}
void Server::disableHttps()
{
m_https = false;
m_certificates.clear();
m_key.clear();
}
QList<QSslCipher> Server::safeCipherList() const QList<QSslCipher> Server::safeCipherList() const
{ {
const QStringList badCiphers = {"idea", "rc4"}; const QStringList badCiphers = {"idea", "rc4"};

View File

@@ -34,6 +34,7 @@
#define HTTP_SERVER_H #define HTTP_SERVER_H
#include <QTcpServer> #include <QTcpServer>
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
#include <QSslCertificate> #include <QSslCertificate>
#include <QSslCipher> #include <QSslCipher>
@@ -55,19 +56,23 @@ namespace Http
~Server(); ~Server();
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
void enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key); bool setupHttps(const QByteArray &certificates, const QByteArray &key);
void disableHttps(); void disableHttps();
#endif #endif
private: private slots:
IRequestHandler *m_requestHandler; void dropTimedOutConnection();
private:
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
void incomingConnection(qintptr socketDescriptor); void incomingConnection(qintptr socketDescriptor);
#else #else
void incomingConnection(int socketDescriptor); void incomingConnection(int socketDescriptor);
#endif #endif
IRequestHandler *m_requestHandler;
QList<Connection *> m_connections; // for tracking persistence connections
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
QList<QSslCipher> safeCipherList() const; QList<QSslCipher> safeCipherList() const;

View File

@@ -29,32 +29,39 @@
#ifndef HTTP_TYPES_H #ifndef HTTP_TYPES_H
#define HTTP_TYPES_H #define HTTP_TYPES_H
#include <QString>
#include <QMap>
#include <QHostAddress> #include <QHostAddress>
#include <QString>
#include <QVector> #include <QVector>
#include "base/types.h" #include "base/types.h"
namespace Http namespace Http
{ {
const QString HEADER_SET_COOKIE = "Set-Cookie"; const char HEADER_CACHE_CONTROL[] = "Cache-Control";
const QString HEADER_CONTENT_TYPE = "Content-Type"; const char HEADER_CONTENT_ENCODING[] = "Content-Encoding";
const QString HEADER_CONTENT_ENCODING = "Content-Encoding"; const char HEADER_CONTENT_LENGTH[] = "Content-Length";
const QString HEADER_CONTENT_LENGTH = "Content-Length"; const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy";
const QString HEADER_CACHE_CONTROL = "Cache-Control"; const char HEADER_CONTENT_TYPE[] = "Content-Type";
const QString HEADER_X_FRAME_OPTIONS = "X-Frame-Options"; const char HEADER_DATE[] = "Date";
const QString HEADER_X_XSS_PROTECTION = "X-XSS-Protection"; const char HEADER_HOST[] = "host";
const QString HEADER_X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; const char HEADER_ORIGIN[] = "origin";
const QString HEADER_CONTENT_SECURITY_POLICY = "Content-Security-Policy"; const char HEADER_REFERER[] = "referer";
const char HEADER_SET_COOKIE[] = "Set-Cookie";
const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options";
const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host";
const char HEADER_X_FRAME_OPTIONS[] = "X-Frame-Options";
const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection";
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8"; const char CONTENT_TYPE_CSS[] = "text/css; charset=UTF-8";
const QString CONTENT_TYPE_GIF = "image/gif"; const char CONTENT_TYPE_GIF[] = "image/gif";
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8"; const char CONTENT_TYPE_HTML[] = "text/html; charset=UTF-8";
const QString CONTENT_TYPE_JS = "application/javascript; charset=UTF-8"; const char CONTENT_TYPE_JS[] = "application/javascript; charset=UTF-8";
const QString CONTENT_TYPE_JSON = "application/json"; const char CONTENT_TYPE_JSON[] = "application/json";
const QString CONTENT_TYPE_PNG = "image/png"; const char CONTENT_TYPE_PNG[] = "image/png";
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8"; const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
// portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'};
struct Environment struct Environment
{ {

View File

@@ -92,8 +92,8 @@ void DownloadHandler::processFinishedDownload()
// Success // Success
QByteArray replyData = m_reply->readAll(); QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") { if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// uncompress gzip reply // decompress gzip reply
Utils::Gzip::uncompress(replyData, replyData); replyData = Utils::Gzip::decompress(replyData);
} }
if (m_saveToFile) { if (m_saveToFile) {

View File

@@ -415,8 +415,10 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
{ {
Q_UNUSED(url); Q_UNUSED(url);
if (!Utils::Gzip::uncompress(data, data)) { bool ok = false;
Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING); data = Utils::Gzip::decompress(data, &ok);
if (!ok) {
Logger::instance()->addMessage(tr("Could not decompress GeoIP database file."), Log::WARNING);
return; return;
} }

View File

@@ -96,7 +96,7 @@ private:
// Search data // Search data
mutable QHash<quint32, QString> m_countries; mutable QHash<quint32, QString> m_countries;
quint32 m_size; quint32 m_size;
const uchar *m_data; uchar *m_data;
}; };
#endif // GEOIPDATABASE_H #endif // GEOIPDATABASE_H

View File

@@ -528,7 +528,8 @@ QString Smtp::getCurrentDateTime() const
int timeOffsetHour = nowDateTime.secsTo(tmp) / 3600; int timeOffsetHour = nowDateTime.secsTo(tmp) / 3600;
int timeOffsetMin = nowDateTime.secsTo(tmp) / 60 - (60 * timeOffsetHour); int timeOffsetMin = nowDateTime.secsTo(tmp) / 60 - (60 * timeOffsetHour);
int timeOffset = timeOffsetHour * 100 + timeOffsetMin; int timeOffset = timeOffsetHour * 100 + timeOffsetMin;
char buf[6] = {0}; // buf size = 11 to avoid format truncation warnings from snprintf
char buf[11] = {0};
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset); std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
QString timeOffsetStr = buf; QString timeOffsetStr = buf;

View File

@@ -40,8 +40,15 @@ class CachedSettingValue
using ProxyFunc = std::function<T (const T&)>; using ProxyFunc = std::function<T (const T&)>;
public: public:
explicit CachedSettingValue(const char *keyName, const T &defaultValue = T() explicit CachedSettingValue(const char *keyName, const T &defaultValue = T())
, ProxyFunc proxyFunc = [](const T &value) { return value; }) : m_keyName(QLatin1String(keyName))
, m_value(SettingsStorage::instance()->loadValue(
m_keyName, defaultValue).template value<T>())
{
}
explicit CachedSettingValue(const char *keyName, const T &defaultValue
, const ProxyFunc &proxyFunc)
: m_keyName(QLatin1String(keyName)) : m_keyName(QLatin1String(keyName))
, m_value(proxyFunc(SettingsStorage::instance()->loadValue( , m_value(proxyFunc(SettingsStorage::instance()->loadValue(
m_keyName, defaultValue).template value<T>())) m_keyName, defaultValue).template value<T>()))
@@ -53,6 +60,11 @@ public:
return m_value; return m_value;
} }
operator T() const
{
return value();
}
CachedSettingValue<T> &operator=(const T &newValue) CachedSettingValue<T> &operator=(const T &newValue)
{ {
m_value = newValue; m_value = newValue;
@@ -60,11 +72,6 @@ public:
return *this; return *this;
} }
operator T() const
{
return value();
}
private: private:
const QString m_keyName; const QString m_keyName;
T m_value; T m_value;

View File

@@ -119,7 +119,7 @@ QString Utils::Fs::folderName(const QString& file_path)
*/ */
bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& path) bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& path)
{ {
if (!QDir(path).exists()) if (path.isEmpty() || !QDir(path).exists())
return false; return false;
static const QStringList deleteFilesList = { static const QStringList deleteFilesList = {
@@ -180,12 +180,14 @@ bool Utils::Fs::forceRemove(const QString& file_path)
* Removes directory and its content recursively. * Removes directory and its content recursively.
* *
*/ */
void Utils::Fs::removeDirRecursive(const QString& dirName) void Utils::Fs::removeDirRecursive(const QString &path)
{ {
if (path.isEmpty())
return;
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
QDir(dirName).removeRecursively(); QDir(path).removeRecursively();
#else #else
QDir dir(dirName); QDir dir(path);
if (!dir.exists()) return; if (!dir.exists()) return;
@@ -198,7 +200,7 @@ void Utils::Fs::removeDirRecursive(const QString& dirName)
else forceRemove(info.absoluteFilePath()); else forceRemove(info.absoluteFilePath());
} }
dir.rmdir(dirName); dir.rmdir(path);
#endif #endif
} }

View File

@@ -58,7 +58,7 @@ namespace Utils
bool smartRemoveEmptyFolderTree(const QString& path); bool smartRemoveEmptyFolderTree(const QString& path);
bool forceRemove(const QString& file_path); bool forceRemove(const QString& file_path);
void removeDirRecursive(const QString& dirName); void removeDirRecursive(const QString& path);
/* Ported from Qt4 to drop dependency on QtGui */ /* Ported from Qt4 to drop dependency on QtGui */
QString QDesktopServicesDataLocation(); QString QDesktopServicesDataLocation();

View File

@@ -27,116 +27,125 @@
* exception statement from your version. * exception statement from your version.
*/ */
#include <QByteArray>
#include <zlib.h>
#include "gzip.h" #include "gzip.h"
bool Utils::Gzip::compress(QByteArray src, QByteArray &dest) #include <vector>
{
static const int BUFSIZE = 128 * 1024;
char tmpBuf[BUFSIZE];
int ret;
dest.clear(); #include <QByteArray>
#ifndef ZLIB_CONST
#define ZLIB_CONST // make z_stream.next_in const
#endif
#include <zlib.h>
QByteArray Utils::Gzip::compress(const QByteArray &data, const int level, bool *ok)
{
if (ok) *ok = false;
if (data.isEmpty())
return {};
const int BUFSIZE = 128 * 1024;
std::vector<char> tmpBuf(BUFSIZE);
z_stream strm; z_stream strm;
strm.zalloc = Z_NULL; strm.zalloc = Z_NULL;
strm.zfree = Z_NULL; strm.zfree = Z_NULL;
strm.opaque = Z_NULL; strm.opaque = Z_NULL;
strm.next_in = reinterpret_cast<uchar *>(src.data()); strm.next_in = reinterpret_cast<const Bytef *>(data.constData());
strm.avail_in = src.length(); strm.avail_in = uInt(data.size());
strm.next_out = reinterpret_cast<uchar *>(tmpBuf); strm.next_out = reinterpret_cast<Bytef *>(tmpBuf.data());
strm.avail_out = BUFSIZE; strm.avail_out = BUFSIZE;
// windowBits = 15 + 16 to enable gzip // windowBits = 15 + 16 to enable gzip
// From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits // From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits
// to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. // to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); int result = deflateInit2(&strm, level, Z_DEFLATED, (15 + 16), 9, Z_DEFAULT_STRATEGY);
if (result != Z_OK)
return {};
if (ret != Z_OK) QByteArray output;
return false; output.reserve(deflateBound(&strm, data.size()));
while (strm.avail_in != 0) { // feed to deflate
ret = deflate(&strm, Z_NO_FLUSH); while (strm.avail_in > 0) {
if (ret != Z_OK) result = deflate(&strm, Z_NO_FLUSH);
return false;
if (strm.avail_out == 0) { if (result != Z_OK) {
dest.append(tmpBuf, BUFSIZE); deflateEnd(&strm);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf); return {};
strm.avail_out = BUFSIZE;
}
}
int deflateRes = Z_OK;
while (deflateRes == Z_OK) {
if (strm.avail_out == 0) {
dest.append(tmpBuf, BUFSIZE);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
} }
deflateRes = deflate(&strm, Z_FINISH); output.append(tmpBuf.data(), (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf.data());
strm.avail_out = BUFSIZE;
} }
if (deflateRes != Z_STREAM_END) // flush the rest from deflate
return false; while (result != Z_STREAM_END) {
result = deflate(&strm, Z_FINISH);
output.append(tmpBuf.data(), (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf.data());
strm.avail_out = BUFSIZE;
}
dest.append(tmpBuf, BUFSIZE - strm.avail_out);
deflateEnd(&strm); deflateEnd(&strm);
return true; if (ok) *ok = true;
return output;
} }
bool Utils::Gzip::uncompress(QByteArray src, QByteArray &dest) QByteArray Utils::Gzip::decompress(const QByteArray &data, bool *ok)
{ {
dest.clear(); if (ok) *ok = false;
if (src.size() <= 4) { if (data.isEmpty())
qWarning("uncompress: Input data is truncated"); return {};
return false;
} const int BUFSIZE = 1024 * 1024;
std::vector<char> tmpBuf(BUFSIZE);
z_stream strm; z_stream strm;
static const int CHUNK_SIZE = 1024;
char out[CHUNK_SIZE];
/* allocate inflate state */
strm.zalloc = Z_NULL; strm.zalloc = Z_NULL;
strm.zfree = Z_NULL; strm.zfree = Z_NULL;
strm.opaque = Z_NULL; strm.opaque = Z_NULL;
strm.avail_in = static_cast<uint>(src.size()); strm.next_in = reinterpret_cast<const Bytef *>(data.constData());
strm.next_in = reinterpret_cast<uchar *>(src.data()); strm.avail_in = uInt(data.size());
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf.data());
strm.avail_out = BUFSIZE;
const int windowBits = 15; // windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing
const int ENABLE_ZLIB_GZIP = 32; // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection
int result = inflateInit2(&strm, (15 + 32));
if (result != Z_OK)
return {};
int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding QByteArray output;
if (ret != Z_OK) // from lzbench, level 9 average compression ratio is: 31.92%, which decompression ratio is: 1 / 0.3192 = 3.13
return false; output.reserve(data.size() * 3);
// run inflate() // run inflate
do { while (true) {
strm.avail_out = CHUNK_SIZE; result = inflate(&strm, Z_NO_FLUSH);
strm.next_out = reinterpret_cast<uchar *>(out);
ret = inflate(&strm, Z_NO_FLUSH); if (result == Z_STREAM_END) {
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered output.append(tmpBuf.data(), (BUFSIZE - strm.avail_out));
break;
switch (ret) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&strm);
return false;
} }
dest.append(out, CHUNK_SIZE - strm.avail_out); if (result != Z_OK) {
} inflateEnd(&strm);
while (!strm.avail_out); return {};
}
output.append(tmpBuf.data(), (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf.data());
strm.avail_out = BUFSIZE;
}
// clean up and return
inflateEnd(&strm); inflateEnd(&strm);
return true;
if (ok) *ok = true;
return output;
} }

View File

@@ -36,8 +36,8 @@ namespace Utils
{ {
namespace Gzip namespace Gzip
{ {
bool compress(QByteArray src, QByteArray &dest); QByteArray compress(const QByteArray &data, int level = 6, bool *ok = nullptr);
bool uncompress(QByteArray src, QByteArray &dest); QByteArray decompress(const QByteArray &data, bool *ok = nullptr);
} }
} }

View File

@@ -30,10 +30,10 @@
#ifndef UTILS_STRING_H #ifndef UTILS_STRING_H
#define UTILS_STRING_H #define UTILS_STRING_H
#include <string> #include <QString>
class QByteArray; class QByteArray;
class QString; class QLatin1String;
namespace Utils namespace Utils
{ {
@@ -51,6 +51,19 @@ namespace Utils
bool naturalCompareCaseSensitive(const QString &left, const QString &right); bool naturalCompareCaseSensitive(const QString &left, const QString &right);
bool naturalCompareCaseInsensitive(const QString &left, const QString &right); bool naturalCompareCaseInsensitive(const QString &left, const QString &right);
template <typename T>
T unquote(const T &str, const QString &quotes = QLatin1String("\""))
{
if (str.length() < 2) return str;
for (auto const quote : quotes) {
if (str.startsWith(quote) && str.endsWith(quote))
return str.mid(1, str.length() - 2);
}
return str;
}
} }
} }

View File

@@ -33,6 +33,7 @@ advancedsettings.h
advancedsettings.h advancedsettings.h
autoexpandabledialog.h autoexpandabledialog.h
categoryfiltermodel.h categoryfiltermodel.h
categoryfilterproxymodel.h
categoryfilterwidget.h categoryfilterwidget.h
cookiesdialog.h cookiesdialog.h
cookiesmodel.h cookiesmodel.h
@@ -74,6 +75,7 @@ addnewtorrentdialog.cpp
advancedsettings.cpp advancedsettings.cpp
autoexpandabledialog.cpp autoexpandabledialog.cpp
categoryfiltermodel.cpp categoryfiltermodel.cpp
categoryfilterproxymodel.cpp
categoryfilterwidget.cpp categoryfilterwidget.cpp
cookiesdialog.cpp cookiesdialog.cpp
cookiesmodel.cpp cookiesmodel.cpp
@@ -132,7 +134,7 @@ shutdownconfirmdlg.ui
qbt_target_sources(about.qrc) qbt_target_sources(about.qrc)
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES}) add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES} ${QBT_GUI_FORMS})
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine
${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base ${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base
QtSingleApplication::QtSingleApplication QtSingleApplication::QtSingleApplication

View File

@@ -68,8 +68,11 @@ const QString KEY_SAVEPATHHISTORY = SETTINGS_KEY("SavePathHistory");
namespace namespace
{ {
//just a shortcut // just a shortcut
inline SettingsStorage *settings() { return SettingsStorage::instance(); } inline SettingsStorage *settings()
{
return SettingsStorage::instance();
}
} }
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
@@ -88,7 +91,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
auto session = BitTorrent::Session::instance(); auto session = BitTorrent::Session::instance();
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused()); ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused());
ui->comboTTM->blockSignals(true); //the TreeView size isn't correct if the slot does it job at this point ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault()); ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
ui->comboTTM->blockSignals(false); ui->comboTTM->blockSignals(false);
populateSavePathComboBox(); populateSavePathComboBox();
@@ -111,7 +114,6 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
if (category != defaultCategory) if (category != defaultCategory)
ui->categoryComboBox->addItem(category); ui->categoryComboBox->addItem(category);
ui->categoryComboBox->model()->sort(0);
ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder); ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder);
loadState(); loadState();
// Signal / slots // Signal / slots
@@ -181,9 +183,9 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
if (Utils::Misc::isUrl(source)) { if (Utils::Misc::isUrl(source)) {
// Launch downloader // Launch downloader
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true); Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
connect(handler, SIGNAL(downloadFinished(QString, QString)), dlg, SLOT(handleDownloadFinished(QString, QString))); connect(handler, SIGNAL(downloadFinished(QString,QString)), dlg, SLOT(handleDownloadFinished(QString,QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString))); connect(handler, SIGNAL(downloadFailed(QString,QString)), dlg, SLOT(handleDownloadFailed(QString,QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString))); connect(handler, SIGNAL(redirectedToMagnet(QString,QString)), dlg, SLOT(handleRedirectedToMagnet(QString,QString)));
} }
else { else {
bool ok = false; bool ok = false;
@@ -309,7 +311,6 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
raise(); raise();
} }
void AddNewTorrentDialog::showAdvancedSettings(bool show) void AddNewTorrentDialog::showAdvancedSettings(bool show)
{ {
const int minimumW = minimumWidth(); const int minimumW = minimumWidth();
@@ -319,7 +320,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show)
ui->settings_group->setVisible(true); ui->settings_group->setVisible(true);
ui->infoGroup->setVisible(true); ui->infoGroup->setVisible(true);
ui->contentTreeView->setVisible(m_hasMetadata); ui->contentTreeView->setVisible(m_hasMetadata);
static_cast<QVBoxLayout*>(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button); static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button);
} }
else { else {
ui->adv_button->setText(QString::fromUtf8(C_DOWN)); ui->adv_button->setText(QString::fromUtf8(C_DOWN));
@@ -337,7 +338,7 @@ void AddNewTorrentDialog::saveSavePathHistory() const
// Get current history // Get current history
QStringList history = settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList(); QStringList history = settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList();
QList<QDir> historyDirs; QList<QDir> historyDirs;
foreach(const QString dir, history) foreach (const QString dir, history)
historyDirs << QDir(dir); historyDirs << QDir(dir);
if (!historyDirs.contains(selectedSavePath)) { if (!historyDirs.contains(selectedSavePath)) {
// Add save path to history // Add save path to history
@@ -381,8 +382,8 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable.")); QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable."));
size_string += " ("; size_string += " (";
size_string += tr("Free space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath( size_string += tr("Free space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
ui->savePathComboBox->itemData( ui->savePathComboBox->itemData(
ui->savePathComboBox->currentIndex()).toString()))); ui->savePathComboBox->currentIndex()).toString())));
size_string += ")"; size_string += ")";
ui->size_lbl->setText(size_string); ui->size_lbl->setText(size_string);
} }
@@ -392,8 +393,8 @@ void AddNewTorrentDialog::onSavePathChanged(int index)
// Toggle default save path setting checkbox visibility // Toggle default save path setting checkbox visibility
ui->defaultSavePathCheckBox->setChecked(false); ui->defaultSavePathCheckBox->setChecked(false);
ui->defaultSavePathCheckBox->setVisible( ui->defaultSavePathCheckBox->setVisible(
QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString()) QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString())
!= QDir(BitTorrent::Session::instance()->defaultSavePath())); != QDir(BitTorrent::Session::instance()->defaultSavePath()));
// Remember index // Remember index
m_oldIndex = index; m_oldIndex = index;
@@ -502,7 +503,7 @@ void AddNewTorrentDialog::renameSelectedFile()
QStringList path_items; QStringList path_items;
path_items << index.data().toString(); path_items << index.data().toString();
QModelIndex parent = m_contentModel->parent(index); QModelIndex parent = m_contentModel->parent(index);
while(parent.isValid()) { while (parent.isValid()) {
path_items.prepend(parent.data().toString()); path_items.prepend(parent.data().toString());
parent = m_contentModel->parent(parent); parent = m_contentModel->parent(parent);
} }
@@ -549,6 +550,10 @@ void AddNewTorrentDialog::renameSelectedFile()
void AddNewTorrentDialog::setdialogPosition() void AddNewTorrentDialog::setdialogPosition()
{ {
// In macOS, AddNewTorrentDialog is a sheet, not a window. Moving it
// causes very bad things to happen, especially if AddNewTorrentDialog is
// on a secondary monitor.
#ifndef Q_OS_MAC
qApp->processEvents(); qApp->processEvents();
QPoint center(Utils::Misc::screenCenter(this)); QPoint center(Utils::Misc::screenCenter(this));
// Adjust y // Adjust y
@@ -562,6 +567,7 @@ void AddNewTorrentDialog::setdialogPosition()
center.setY(0); center.setY(0);
} }
move(center); move(center);
#endif
} }
void AddNewTorrentDialog::populateSavePathComboBox() void AddNewTorrentDialog::populateSavePathComboBox()
@@ -577,7 +583,7 @@ void AddNewTorrentDialog::populateSavePathComboBox()
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath); ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath);
} }
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
{ {
QMenu myFilesLlistMenu; QMenu myFilesLlistMenu;
const QModelIndexList selectedRows = ui->contentTreeView->selectionModel()->selectedRows(0); const QModelIndexList selectedRows = ui->contentTreeView->selectionModel()->selectedRows(0);
@@ -620,7 +626,7 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
void AddNewTorrentDialog::accept() void AddNewTorrentDialog::accept()
{ {
if (!m_hasMetadata) if (!m_hasMetadata)
disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo &))); disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo&)));
BitTorrent::AddTorrentParams params; BitTorrent::AddTorrentParams params;
@@ -718,10 +724,10 @@ void AddNewTorrentDialog::setupTreeview()
m_contentModel = new TorrentContentFilterModel(this); m_contentModel = new TorrentContentFilterModel(this);
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel())); connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
ui->contentTreeView->setModel(m_contentModel); ui->contentTreeView->setModel(m_contentModel);
m_contentDelegate = new PropListDelegate(); m_contentDelegate = new PropListDelegate(nullptr);
ui->contentTreeView->setItemDelegate(m_contentDelegate); ui->contentTreeView->setItemDelegate(m_contentDelegate);
connect(ui->contentTreeView, SIGNAL(clicked(const QModelIndex &)), ui->contentTreeView, SLOT(edit(const QModelIndex &))); connect(ui->contentTreeView, SIGNAL(clicked(const QModelIndex&)), ui->contentTreeView, SLOT(edit(const QModelIndex&)));
connect(ui->contentTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); connect(ui->contentTreeView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContentTreeMenu(const QPoint&)));
// List files in torrent // List files in torrent
m_contentModel->model()->setupModelData(m_torrentInfo); m_contentModel->model()->setupModelData(m_torrentInfo);

View File

@@ -139,8 +139,7 @@ public:
item->m_parent = this; item->m_parent = this;
m_children[uid] = item; m_children[uid] = item;
auto pos = std::lower_bound(m_childUids.begin(), m_childUids.end(), uid); m_childUids.append(uid);
m_childUids.insert(pos, uid);
m_torrentsCount += item->torrentsCount(); m_torrentsCount += item->torrentsCount();
} }
@@ -195,6 +194,13 @@ CategoryFilterModel::~CategoryFilterModel()
delete m_rootItem; delete m_rootItem;
} }
bool CategoryFilterModel::isSpecialItem(const QModelIndex &index)
{
// the first two items at first level are special items:
// 'All' and 'Uncategorized'
return (!index.parent().isValid() && (index.row() <= 1));
}
int CategoryFilterModel::columnCount(const QModelIndex &) const int CategoryFilterModel::columnCount(const QModelIndex &) const
{ {
return 1; return 1;
@@ -307,11 +313,10 @@ void CategoryFilterModel::categoryAdded(const QString &categoryName)
parent = findItem(expanded[expanded.count() - 2]); parent = findItem(expanded[expanded.count() - 2]);
} }
auto item = new CategoryModelItem( int row = parent->childCount();
parent, m_isSubcategoriesEnabled ? shortName(categoryName) : categoryName); beginInsertRows(index(parent), row, row);
new CategoryModelItem(
QModelIndex i = index(item); parent, m_isSubcategoriesEnabled ? shortName(categoryName) : categoryName);
beginInsertRows(i.parent(), i.row(), i.row());
endInsertRows(); endInsertRows();
} }

View File

@@ -48,6 +48,8 @@ public:
explicit CategoryFilterModel(QObject *parent = nullptr); explicit CategoryFilterModel(QObject *parent = nullptr);
~CategoryFilterModel(); ~CategoryFilterModel();
static bool isSpecialItem(const QModelIndex &index);
int columnCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override; Qt::ItemFlags flags(const QModelIndex &index) const override;

View File

@@ -0,0 +1,57 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Frédéric Brière <fbriere@fbriere.net>
*
* 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 "categoryfilterproxymodel.h"
#include "base/utils/string.h"
#include "categoryfiltermodel.h"
CategoryFilterProxyModel::CategoryFilterProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
}
QModelIndex CategoryFilterProxyModel::index(const QString &categoryName) const
{
return mapFromSource(static_cast<CategoryFilterModel *>(sourceModel())->index(categoryName));
}
QString CategoryFilterProxyModel::categoryName(const QModelIndex &index) const
{
return static_cast<CategoryFilterModel *>(sourceModel())->categoryName(mapToSource(index));
}
bool CategoryFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
// "All" and "Uncategorized" must be left in place
if (CategoryFilterModel::isSpecialItem(left) || CategoryFilterModel::isSpecialItem(right))
return left.row() < right.row();
else
return Utils::String::naturalCompareCaseInsensitive(
left.data().toString(), right.data().toString());
}

View File

@@ -0,0 +1,52 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Frédéric Brière <fbriere@fbriere.net>
*
* 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 CATEGORYFILTERPROXYMODEL_H
#define CATEGORYFILTERPROXYMODEL_H
#include <QSortFilterProxyModel>
#include <QString>
class CategoryFilterProxyModel: public QSortFilterProxyModel
{
public:
explicit CategoryFilterProxyModel(QObject *parent = nullptr);
// CategoryFilterModel methods which we need to relay
QModelIndex index(const QString &categoryName) const;
QString categoryName(const QModelIndex &index) const;
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
// we added another overload of index(), hence this using directive:
using QSortFilterProxyModel::index;
};
#endif // CATEGORYFILTERPROXYMODEL_H

View File

@@ -38,11 +38,12 @@
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "categoryfiltermodel.h" #include "categoryfiltermodel.h"
#include "categoryfilterproxymodel.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
namespace namespace
{ {
QString getCategoryFilter(const CategoryFilterModel *const model, const QModelIndex &index) QString getCategoryFilter(const CategoryFilterProxyModel *const model, const QModelIndex &index)
{ {
QString categoryFilter; // Defaults to All QString categoryFilter; // Defaults to All
if (index.isValid()) { if (index.isValid()) {
@@ -54,19 +55,15 @@ namespace
return categoryFilter; return categoryFilter;
} }
bool isSpecialItem(const QModelIndex &index)
{
// the first two items at first level are special items:
// 'All' and 'Uncategorized'
return (!index.parent().isValid() && (index.row() <= 1));
}
} }
CategoryFilterWidget::CategoryFilterWidget(QWidget *parent) CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
: QTreeView(parent) : QTreeView(parent)
{ {
setModel(new CategoryFilterModel(this)); CategoryFilterProxyModel *proxyModel = new CategoryFilterProxyModel(this);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
proxyModel->setSourceModel(new CategoryFilterModel(this));
setModel(proxyModel);
setFrameShape(QFrame::NoFrame); setFrameShape(QFrame::NoFrame);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -78,6 +75,7 @@ CategoryFilterWidget::CategoryFilterWidget(QWidget *parent)
setAttribute(Qt::WA_MacShowFocusRect, false); setAttribute(Qt::WA_MacShowFocusRect, false);
#endif #endif
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
sortByColumn(0, Qt::AscendingOrder);
setCurrentIndex(model()->index(0, 0)); setCurrentIndex(model()->index(0, 0));
connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(callUpdateGeometry())); connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(callUpdateGeometry()));
@@ -95,14 +93,14 @@ QString CategoryFilterWidget::currentCategory() const
if (!selectedRows.isEmpty()) if (!selectedRows.isEmpty())
current = selectedRows.first(); current = selectedRows.first();
return getCategoryFilter(static_cast<CategoryFilterModel *>(model()), current); return getCategoryFilter(static_cast<CategoryFilterProxyModel *>(model()), current);
} }
void CategoryFilterWidget::onCurrentRowChanged(const QModelIndex &current, const QModelIndex &previous) void CategoryFilterWidget::onCurrentRowChanged(const QModelIndex &current, const QModelIndex &previous)
{ {
Q_UNUSED(previous); Q_UNUSED(previous);
emit categoryChanged(getCategoryFilter(static_cast<CategoryFilterModel *>(model()), current)); emit categoryChanged(getCategoryFilter(static_cast<CategoryFilterProxyModel *>(model()), current));
} }
void CategoryFilterWidget::showMenu(QPoint) void CategoryFilterWidget::showMenu(QPoint)
@@ -115,7 +113,7 @@ void CategoryFilterWidget::showMenu(QPoint)
connect(addAct, SIGNAL(triggered()), SLOT(addCategory())); connect(addAct, SIGNAL(triggered()), SLOT(addCategory()));
auto selectedRows = selectionModel()->selectedRows(); auto selectedRows = selectionModel()->selectedRows();
if (!selectedRows.empty() && !isSpecialItem(selectedRows.first())) { if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) {
if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) { if (BitTorrent::Session::instance()->isSubcategoriesEnabled()) {
QAction *addSubAct = menu.addAction( QAction *addSubAct = menu.addAction(
GuiIconProvider::instance()->getIcon("list-add") GuiIconProvider::instance()->getIcon("list-add")
@@ -253,9 +251,9 @@ void CategoryFilterWidget::addSubcategory()
void CategoryFilterWidget::removeCategory() void CategoryFilterWidget::removeCategory()
{ {
auto selectedRows = selectionModel()->selectedRows(); auto selectedRows = selectionModel()->selectedRows();
if (!selectedRows.empty() && !isSpecialItem(selectedRows.first())) { if (!selectedRows.empty() && !CategoryFilterModel::isSpecialItem(selectedRows.first())) {
BitTorrent::Session::instance()->removeCategory( BitTorrent::Session::instance()->removeCategory(
static_cast<CategoryFilterModel *>(model())->categoryName(selectedRows.first())); static_cast<CategoryFilterProxyModel *>(model())->categoryName(selectedRows.first()));
updateGeometry(); updateGeometry();
} }
} }
@@ -264,7 +262,7 @@ void CategoryFilterWidget::removeUnusedCategories()
{ {
auto session = BitTorrent::Session::instance(); auto session = BitTorrent::Session::instance();
foreach (const QString &category, session->categories()) foreach (const QString &category, session->categories())
if (model()->data(static_cast<CategoryFilterModel *>(model())->index(category), Qt::UserRole) == 0) if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
session->removeCategory(category); session->removeCategory(category);
updateGeometry(); updateGeometry();
} }

View File

@@ -17,7 +17,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Deletion confirmation - qBittorrent</string> <string>Deletion confirmation</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>

View File

@@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Download from urls</string> <string>Download from URLs</string>
</property> </property>
<layout class="QVBoxLayout"> <layout class="QVBoxLayout">
<item> <item>
@@ -33,12 +33,12 @@
</item> </item>
<item> <item>
<widget class="QTextEdit" name="textUrls"> <widget class="QTextEdit" name="textUrls">
<property name="acceptRichText">
<bool>false</bool>
</property>
<property name="tabChangesFocus"> <property name="tabChangesFocus">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>

View File

@@ -51,6 +51,7 @@ HEADERS += \
$$PWD/cookiesmodel.h \ $$PWD/cookiesmodel.h \
$$PWD/cookiesdialog.h \ $$PWD/cookiesdialog.h \
$$PWD/categoryfiltermodel.h \ $$PWD/categoryfiltermodel.h \
$$PWD/categoryfilterproxymodel.h \
$$PWD/categoryfilterwidget.h $$PWD/categoryfilterwidget.h
SOURCES += \ SOURCES += \
@@ -93,6 +94,7 @@ SOURCES += \
$$PWD/cookiesmodel.cpp \ $$PWD/cookiesmodel.cpp \
$$PWD/cookiesdialog.cpp \ $$PWD/cookiesdialog.cpp \
$$PWD/categoryfiltermodel.cpp \ $$PWD/categoryfiltermodel.cpp \
$$PWD/categoryfilterproxymodel.cpp \
$$PWD/categoryfilterwidget.cpp $$PWD/categoryfilterwidget.cpp
win32|macx { win32|macx {

View File

@@ -28,6 +28,8 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "mainwindow.h"
#include <QtGlobal> #include <QtGlobal>
#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>
@@ -44,6 +46,7 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QShortcut> #include <QShortcut>
#include <QScrollBar> #include <QScrollBar>
#include <QSplitter>
#include <QSysInfo> #include <QSysInfo>
#include <QMimeData> #include <QMimeData>
#include <QCryptographicHash> #include <QCryptographicHash>
@@ -91,7 +94,6 @@
#include "executionlog.h" #include "executionlog.h"
#include "hidabletabwidget.h" #include "hidabletabwidget.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "mainwindow.h"
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
void qt_mac_set_dock_menu(QMenu *menu); void qt_mac_set_dock_menu(QMenu *menu);
@@ -941,17 +943,17 @@ void MainWindow::notifyOfUpdate(QString)
} }
// Toggle Main window visibility // Toggle Main window visibility
void MainWindow::toggleVisibility(QSystemTrayIcon::ActivationReason e) void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason)
{ {
if ((e == QSystemTrayIcon::Trigger) || (e == QSystemTrayIcon::DoubleClick)) { switch (reason) {
case QSystemTrayIcon::Trigger: {
if (isHidden()) { if (isHidden()) {
if (m_uiLocked) { if (m_uiLocked && !unlockUI()) // Ask for UI lock password
// Ask for UI lock password return;
if (!unlockUI())
return;
}
// Make sure the window is not minimized // Make sure the window is not minimized
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
// Then show it // Then show it
show(); show();
raise(); raise();
@@ -960,6 +962,12 @@ void MainWindow::toggleVisibility(QSystemTrayIcon::ActivationReason e)
else { else {
hide(); hide();
} }
break;
}
default:
break;
} }
} }
@@ -968,7 +976,7 @@ void MainWindow::on_actionAbout_triggered()
{ {
// About dialog // About dialog
if (m_aboutDlg) if (m_aboutDlg)
m_aboutDlg->setFocus(); m_aboutDlg->activateWindow();
else else
m_aboutDlg = new about(this); m_aboutDlg = new about(this);
} }
@@ -976,7 +984,7 @@ void MainWindow::on_actionAbout_triggered()
void MainWindow::on_actionStatistics_triggered() void MainWindow::on_actionStatistics_triggered()
{ {
if (m_statsDlg) if (m_statsDlg)
m_statsDlg->setFocus(); m_statsDlg->activateWindow();
else else
m_statsDlg = new StatsDialog(this); m_statsDlg = new StatsDialog(this);
} }
@@ -1013,7 +1021,8 @@ void MainWindow::closeEvent(QCloseEvent *e)
if (!isVisible()) if (!isVisible())
show(); show();
QMessageBox confirmBox(QMessageBox::Question, tr("Exiting qBittorrent"), QMessageBox confirmBox(QMessageBox::Question, tr("Exiting qBittorrent"),
tr("Some files are currently transferring.\nAre you sure you want to quit qBittorrent?"), // Split it because the last sentence is used in the Web UI
tr("Some files are currently transferring.") + "\n" + tr("Are you sure you want to quit qBittorrent?"),
QMessageBox::NoButton, this); QMessageBox::NoButton, this);
QPushButton *noBtn = confirmBox.addButton(tr("&No"), QMessageBox::NoRole); QPushButton *noBtn = confirmBox.addButton(tr("&No"), QMessageBox::NoRole);
confirmBox.addButton(tr("&Yes"), QMessageBox::YesRole); confirmBox.addButton(tr("&Yes"), QMessageBox::YesRole);
@@ -1049,7 +1058,7 @@ void MainWindow::closeEvent(QCloseEvent *e)
void MainWindow::on_actionCreateTorrent_triggered() void MainWindow::on_actionCreateTorrent_triggered()
{ {
if (m_createTorrentDlg) if (m_createTorrentDlg)
m_createTorrentDlg->setFocus(); m_createTorrentDlg->activateWindow();
else else
m_createTorrentDlg = new TorrentCreatorDlg(this); m_createTorrentDlg = new TorrentCreatorDlg(this);
} }
@@ -1476,7 +1485,7 @@ void MainWindow::createTrayIcon()
void MainWindow::on_actionOptions_triggered() void MainWindow::on_actionOptions_triggered()
{ {
if (m_options) if (m_options)
m_options->setFocus(); m_options->activateWindow();
else else
m_options = new OptionsDialog(this); m_options = new OptionsDialog(this);
} }

View File

@@ -103,7 +103,7 @@ public:
void showNotificationBaloon(QString title, QString msg) const; void showNotificationBaloon(QString title, QString msg) const;
private slots: private slots:
void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger); void toggleVisibility(const QSystemTrayIcon::ActivationReason reason = QSystemTrayIcon::Trigger);
void balloonClicked(); void balloonClicked();
void writeSettings(); void writeSettings();

View File

@@ -2613,7 +2613,7 @@
<item> <item>
<widget class="QGroupBox" name="checkWebUi"> <widget class="QGroupBox" name="checkWebUi">
<property name="title"> <property name="title">
<string>Enable Web User Interface (Remote control)</string> <string>Web User Interface (Remote control)</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>

View File

@@ -42,7 +42,7 @@ speedwidget.cpp
speedplotview.cpp speedplotview.cpp
) )
add_library(qbt_properties STATIC ${QBT_PROPERTIES_HEADERS} ${QBT_PROPERTIES_SOURCES}) add_library(qbt_properties STATIC ${QBT_PROPERTIES_HEADERS} ${QBT_PROPERTIES_SOURCES} ${QBT_PROPERTIES_FORMS})
target_link_libraries(qbt_properties qbt_base) target_link_libraries(qbt_properties qbt_base)
if (NOT QT4_FOUND) if (NOT QT4_FOUND)
target_link_libraries(qbt_properties Qt5::Widgets Qt5::Concurrent) target_link_libraries(qbt_properties Qt5::Widgets Qt5::Concurrent)

View File

@@ -38,7 +38,8 @@
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
class PeerListDelegate: public QItemDelegate { class PeerListDelegate: public QItemDelegate
{
Q_OBJECT Q_OBJECT
public: public:
@@ -67,14 +68,16 @@ public:
~PeerListDelegate() {} ~PeerListDelegate() {}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{ {
painter->save(); painter->save();
const bool hideValues = Preferences::instance()->getHideZeroValues(); const bool hideValues = Preferences::instance()->getHideZeroValues();
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option); QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
switch(index.column()) { QItemDelegate::drawBackground(painter, opt, index);
switch (index.column()) {
case PORT: { case PORT: {
QItemDelegate::drawBackground(painter, opt, index);
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, option.rect, index.data().toString()); QItemDelegate::drawDisplay(painter, opt, option.rect, index.data().toString());
} }
@@ -84,40 +87,38 @@ public:
qlonglong size = index.data().toLongLong(); qlonglong size = index.data().toLongLong();
if (hideValues && (size <= 0)) if (hideValues && (size <= 0))
break; break;
QItemDelegate::drawBackground(painter, opt, index);
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(size)); QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(size));
} }
break; break;
case DOWN_SPEED: case DOWN_SPEED:
case UP_SPEED:{ case UP_SPEED: {
QItemDelegate::drawBackground(painter, opt, index);
qreal speed = index.data().toDouble(); qreal speed = index.data().toDouble();
if (speed <= 0.0)
break;
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
if (speed > 0.0) QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::Misc::friendlyUnit(speed, true));
QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::Misc::friendlyUnit(speed, true));
} }
break; break;
case PROGRESS: case PROGRESS:
case RELEVANCE: { case RELEVANCE: {
QItemDelegate::drawBackground(painter, opt, index);
qreal progress = index.data().toDouble(); qreal progress = index.data().toDouble();
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::String::fromDouble(progress*100.0, 1)+"%"); QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::String::fromDouble(progress * 100.0, 1) + "%");
} }
break; break;
default: default:
QItemDelegate::paint(painter, option, index); QItemDelegate::paint(painter, option, index);
} }
painter->restore(); painter->restore();
} }
QWidget* createEditor(QWidget*, const QStyleOptionViewItem &, const QModelIndex &) const QWidget *createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const override
{ {
// No editor here // No editor here
return 0; return nullptr;
} }
}; };
#endif // PEERLISTDELEGATE_H #endif // PEERLISTDELEGATE_H

View File

@@ -28,6 +28,9 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "peerlistwidget.h"
#include <QApplication>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QSet> #include <QSet>
@@ -53,7 +56,6 @@
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "peerlistdelegate.h" #include "peerlistdelegate.h"
#include "peerlistsortmodel.h" #include "peerlistsortmodel.h"
#include "peerlistwidget.h"
PeerListWidget::PeerListWidget(PropertiesWidget *parent) PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent) : QTreeView(parent)
@@ -69,7 +71,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionMode(QAbstractItemView::ExtendedSelection);
header()->setStretchLastSection(false); header()->setStretchLastSection(false);
// List Model // List Model
m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT); m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT, this);
m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, tr("Country")); // Country flag column m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, tr("Country")); // Country flag column
m_listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP")); m_listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP"));
m_listModel->setHeaderData(PeerListDelegate::PORT, Qt::Horizontal, tr("Port")); m_listModel->setHeaderData(PeerListDelegate::PORT, Qt::Horizontal, tr("Port"));
@@ -92,7 +94,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole); m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
m_listModel->setHeaderData(PeerListDelegate::RELEVANCE, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole); m_listModel->setHeaderData(PeerListDelegate::RELEVANCE, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
// Proxy model to support sorting without actually altering the underlying model // Proxy model to support sorting without actually altering the underlying model
m_proxyModel = new PeerListSortModel(); m_proxyModel = new PeerListSortModel(this);
m_proxyModel->setDynamicSortFilter(true); m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSourceModel(m_listModel); m_proxyModel->setSourceModel(m_listModel);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
@@ -102,7 +104,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
m_resolveCountries = Preferences::instance()->resolvePeerCountries(); m_resolveCountries = Preferences::instance()->resolvePeerCountries();
if (!m_resolveCountries) if (!m_resolveCountries)
hideColumn(PeerListDelegate::COUNTRY); hideColumn(PeerListDelegate::COUNTRY);
//Ensure that at least one column is visible at all times // Ensure that at least one column is visible at all times
bool atLeastOne = false; bool atLeastOne = false;
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) { for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) {
if (!isColumnHidden(i)) { if (!isColumnHidden(i)) {
@@ -112,9 +114,9 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
} }
if (!atLeastOne) if (!atLeastOne)
setColumnHidden(PeerListDelegate::IP, false); setColumnHidden(PeerListDelegate::IP, false);
//To also mitigate the above issue, we have to resize each column when // To also mitigate the above issue, we have to resize each column when
//its size is 0, because explicitly 'showing' the column isn't enough // its size is 0, because explicitly 'showing' the column isn't enough
//in the above scenario. // in the above scenario.
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
if ((columnWidth(i) <= 0) && !isColumnHidden(i)) if ((columnWidth(i) <= 0) && !isColumnHidden(i))
resizeColumnToContents(i); resizeColumnToContents(i);
@@ -130,7 +132,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
updatePeerHostNameResolutionState(); updatePeerHostNameResolutionState();
// SIGNAL/SLOT // SIGNAL/SLOT
header()->setContextMenuPolicy(Qt::CustomContextMenu); header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayToggleColumnsMenu(const QPoint &))); connect(header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayToggleColumnsMenu(const QPoint&)));
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int))); connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
handleSortColumnChanged(header()->sortIndicatorSection()); handleSortColumnChanged(header()->sortIndicatorSection());
m_copyHotkey = new QShortcut(QKeySequence::Copy, this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut); m_copyHotkey = new QShortcut(QKeySequence::Copy, this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut);
@@ -148,19 +150,15 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
PeerListWidget::~PeerListWidget() PeerListWidget::~PeerListWidget()
{ {
saveSettings(); saveSettings();
delete m_proxyModel;
delete m_listModel;
delete m_listDelegate;
if (m_resolver) if (m_resolver)
delete m_resolver; delete m_resolver;
delete m_copyHotkey;
} }
void PeerListWidget::displayToggleColumnsMenu(const QPoint&) void PeerListWidget::displayToggleColumnsMenu(const QPoint &)
{ {
QMenu hideshowColumn(this); QMenu hideshowColumn(this);
hideshowColumn.setTitle(tr("Column visibility")); hideshowColumn.setTitle(tr("Column visibility"));
QList<QAction*> actions; QList<QAction *> actions;
for (int i = 0; i < PeerListDelegate::IP_HIDDEN; ++i) { for (int i = 0; i < PeerListDelegate::IP_HIDDEN; ++i) {
if ((i == PeerListDelegate::COUNTRY) && !Preferences::instance()->resolvePeerCountries()) { if ((i == PeerListDelegate::COUNTRY) && !Preferences::instance()->resolvePeerCountries()) {
actions.append(nullptr); // keep the index in sync actions.append(nullptr); // keep the index in sync
@@ -201,7 +199,7 @@ void PeerListWidget::updatePeerHostNameResolutionState()
if (Preferences::instance()->resolvePeerHostNames()) { if (Preferences::instance()->resolvePeerHostNames()) {
if (!m_resolver) { if (!m_resolver) {
m_resolver = new Net::ReverseResolution(this); m_resolver = new Net::ReverseResolution(this);
connect(m_resolver, SIGNAL(ipResolved(QString, QString)), SLOT(handleResolved(QString, QString))); connect(m_resolver, SIGNAL(ipResolved(QString,QString)), SLOT(handleResolved(QString,QString)));
loadPeers(m_properties->getCurrentTorrent(), true); loadPeers(m_properties->getCurrentTorrent(), true);
} }
} }
@@ -226,7 +224,7 @@ void PeerListWidget::updatePeerCountryResolutionState()
} }
} }
void PeerListWidget::showPeerListMenu(const QPoint&) void PeerListWidget::showPeerListMenu(const QPoint &)
{ {
QMenu menu; QMenu menu;
bool emptyMenu = true; bool emptyMenu = true;
@@ -370,7 +368,7 @@ void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool fo
// Delete peers that are gone // Delete peers that are gone
QSetIterator<QString> it(oldeersSet); QSetIterator<QString> it(oldeersSet);
while (it.hasNext()) { while (it.hasNext()) {
const QString& ip = it.next(); const QString &ip = it.next();
m_missingFlags.remove(ip); m_missingFlags.remove(ip);
m_peerAddresses.remove(ip); m_peerAddresses.remove(ip);
QStandardItem *item = m_peerItems.take(ip); QStandardItem *item = m_peerItems.take(ip);
@@ -378,7 +376,7 @@ void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool fo
} }
} }
QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) QStandardItem *PeerListWidget::addPeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer)
{ {
int row = m_listModel->rowCount(); int row = m_listModel->rowCount();
// Adding Peer to peer list // Adding Peer to peer list
@@ -468,7 +466,7 @@ void PeerListWidget::wheelEvent(QWheelEvent *event)
{ {
event->accept(); event->accept();
if(event->modifiers() & Qt::ShiftModifier) { if (event->modifiers() & Qt::ShiftModifier) {
// Shift + scroll = horizontal scroll // Shift + scroll = horizontal scroll
QWheelEvent scrollHEvent(event->pos(), event->globalPos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal); QWheelEvent scrollHEvent(event->pos(), event->globalPos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal);
QTreeView::wheelEvent(&scrollHEvent); QTreeView::wheelEvent(&scrollHEvent);

View File

@@ -77,8 +77,8 @@ public:
private slots: private slots:
void loadSettings(); void loadSettings();
void saveSettings() const; void saveSettings() const;
void displayToggleColumnsMenu(const QPoint&); void displayToggleColumnsMenu(const QPoint &);
void showPeerListMenu(const QPoint&); void showPeerListMenu(const QPoint &);
void banSelectedPeers(); void banSelectedPeers();
void copySelectedPeers(); void copySelectedPeers();
void handleSortColumnChanged(int col); void handleSortColumnChanged(int col);
@@ -90,7 +90,7 @@ private:
QStandardItemModel *m_listModel; QStandardItemModel *m_listModel;
PeerListDelegate *m_listDelegate; PeerListDelegate *m_listDelegate;
PeerListSortModel *m_proxyModel; PeerListSortModel *m_proxyModel;
QHash<QString, QStandardItem*> m_peerItems; QHash<QString, QStandardItem *> m_peerItems;
QHash<QString, BitTorrent::PeerAddress> m_peerAddresses; QHash<QString, BitTorrent::PeerAddress> m_peerAddresses;
QSet<QString> m_missingFlags; QSet<QString> m_missingFlags;
QPointer<Net::ReverseResolution> m_resolver; QPointer<Net::ReverseResolution> m_resolver;

View File

@@ -28,23 +28,31 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "peersadditiondlg.h"
#include <QMessageBox> #include <QMessageBox>
#include <QHostAddress> #include <QHostAddress>
#include "peersadditiondlg.h" #include "ui_peersadditiondlg.h"
PeersAdditionDlg::PeersAdditionDlg(QWidget *parent) PeersAdditionDlg::PeersAdditionDlg(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_ui(new Ui::addPeersDialog())
{ {
setupUi(this); m_ui->setupUi(this);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput())); connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(validateInput()));
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
label_format->hide(); m_ui->label_format->hide();
peers_txt->setPlaceholderText("Format: IPv4:port / [IPv6]:port"); m_ui->peers_txt->setPlaceholderText("Format: IPv4:port / [IPv6]:port");
#endif #endif
} }
PeersAdditionDlg::~PeersAdditionDlg()
{
delete m_ui;
}
QList<BitTorrent::PeerAddress> PeersAdditionDlg::askForPeers() QList<BitTorrent::PeerAddress> PeersAdditionDlg::askForPeers()
{ {
PeersAdditionDlg dlg; PeersAdditionDlg dlg;
@@ -54,13 +62,13 @@ QList<BitTorrent::PeerAddress> PeersAdditionDlg::askForPeers()
void PeersAdditionDlg::validateInput() void PeersAdditionDlg::validateInput()
{ {
if (peers_txt->toPlainText().trimmed().isEmpty()) { if (m_ui->peers_txt->toPlainText().trimmed().isEmpty()) {
QMessageBox::warning(this, tr("No peer entered"), QMessageBox::warning(this, tr("No peer entered"),
tr("Please type at least one peer."), tr("Please type at least one peer."),
QMessageBox::Ok); QMessageBox::Ok);
return; return;
} }
foreach (const QString &peer, peers_txt->toPlainText().trimmed().split("\n")) { foreach (const QString &peer, m_ui->peers_txt->toPlainText().trimmed().split("\n")) {
BitTorrent::PeerAddress addr = parsePeer(peer); BitTorrent::PeerAddress addr = parsePeer(peer);
if (!addr.ip.isNull()) { if (!addr.ip.isNull()) {
m_peersList.append(addr); m_peersList.append(addr);

View File

@@ -34,14 +34,21 @@
#include <QDialog> #include <QDialog>
#include "base/bittorrent/peerinfo.h" #include "base/bittorrent/peerinfo.h"
#include "ui_peersadditiondlg.h"
class PeersAdditionDlg: public QDialog, private Ui::addPeersDialog template <class T> class QList;
namespace Ui
{
class addPeersDialog;
}
class PeersAdditionDlg: public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
PeersAdditionDlg(QWidget *parent = 0); PeersAdditionDlg(QWidget *parent = 0);
~PeersAdditionDlg();
static QList<BitTorrent::PeerAddress> askForPeers(); static QList<BitTorrent::PeerAddress> askForPeers();
@@ -50,6 +57,8 @@ protected slots:
private: private:
BitTorrent::PeerAddress parsePeer(QString peer); BitTorrent::PeerAddress parsePeer(QString peer);
Ui::addPeersDialog *m_ui;
QList<BitTorrent::PeerAddress> m_peersList; QList<BitTorrent::PeerAddress> m_peersList;
}; };

View File

@@ -50,7 +50,8 @@ namespace
{ {
public: public:
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image) PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
: m_bytesPerPixel {image.width() > 0 ? torrentInfo.totalSize() / image.width() : -1} : m_bytesPerPixel {(image.width() > 0 && torrentInfo.totalSize() >= image.width())
? torrentInfo.totalSize() / image.width() : -1}
, m_torrentInfo {torrentInfo} , m_torrentInfo {torrentInfo}
{ {
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10)) if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
@@ -251,8 +252,8 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
QString toolTipText; QString toolTipText;
QTextStream stream(&toolTipText, QIODevice::WriteOnly); QTextStream stream(&toolTipText, QIODevice::WriteOnly);
bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier); const bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (showDetailedInformation) { if (showDetailedInformation && m_torrent->hasMetadata()) {
const int imagePos = e->pos().x() - borderWidth; const int imagePos = e->pos().x() - borderWidth;
if ((imagePos >=0) && (imagePos < m_image.width())) { if ((imagePos >=0) && (imagePos < m_image.width())) {
stream << "<html><body>"; stream << "<html><body>";
@@ -286,7 +287,10 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
} }
else { else {
stream << simpleToolTipText(); stream << simpleToolTipText();
stream << '\n' << tr("Hold Shift key for detailed information"); if (showDetailedInformation) // metadata are not available at this point
stream << '\n' << tr("Wait until metadata become available to see detailed information");
else
stream << '\n' << tr("Hold Shift key for detailed information");
} }
stream.flush(); stream.flush();
@@ -296,7 +300,7 @@ void PiecesBar::showToolTip(const QHelpEvent *e)
void PiecesBar::highlightFile(int imagePos) void PiecesBar::highlightFile(int imagePos)
{ {
if (!m_torrent || (imagePos < 0) || (imagePos >= m_image.width())) if (!m_torrent || !m_torrent->hasMetadata() || (imagePos < 0) || (imagePos >= m_image.width()))
return; return;
PieceIndexToImagePos transform {m_torrent->info(), m_image}; PieceIndexToImagePos transform {m_torrent->info(), m_image};

View File

@@ -39,6 +39,7 @@
#include <QHeaderView> #include <QHeaderView>
#include <QAction> #include <QAction>
#include <QMenu> #include <QMenu>
#include <QModelIndex>
#include <QFileDialog> #include <QFileDialog>
#include <QBitArray> #include <QBitArray>
@@ -64,43 +65,49 @@
#include "transferlistwidget.h" #include "transferlistwidget.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "ui_propertieswidget.h"
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList) PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, TransferListWidget *transferList)
: QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0) : QWidget(parent)
, m_ui(new Ui::PropertiesWidget())
, transferList(transferList)
, main_window(main_window)
, m_torrent(0)
{ {
setupUi(this); m_ui->setupUi(this);
setAutoFillBackground(true); setAutoFillBackground(true);
state = VISIBLE; state = VISIBLE;
// Set Properties list model // Set Properties list model
PropListModel = new TorrentContentFilterModel(); PropListModel = new TorrentContentFilterModel();
filesList->setModel(PropListModel); m_ui->filesList->setModel(PropListModel);
PropDelegate = new PropListDelegate(this); PropDelegate = new PropListDelegate(this);
filesList->setItemDelegate(PropDelegate); m_ui->filesList->setItemDelegate(PropDelegate);
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->setMaximumSize(300, m_contentFilterLine->size().height());
connect(m_contentFilterLine, SIGNAL(textChanged(QString)), this, SLOT(filterText(QString))); connect(m_contentFilterLine, SIGNAL(textChanged(QString)), this, SLOT(filterText(QString)));
contentFilterLayout->insertWidget(3, m_contentFilterLine); m_ui->contentFilterLayout->insertWidget(3, m_contentFilterLine);
// SIGNAL/SLOTS // SIGNAL/SLOTS
connect(filesList, SIGNAL(clicked(const QModelIndex&)), filesList, SLOT(edit(const QModelIndex&))); connect(m_ui->filesList, SIGNAL(clicked(const QModelIndex&)), m_ui->filesList, SLOT(edit(const QModelIndex&)));
connect(selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll())); connect(m_ui->selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
connect(selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone())); connect(m_ui->selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&))); connect(m_ui->filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
connect(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(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(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(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(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(filesList->header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveSettings())); connect(m_ui->filesList->header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveSettings()));
connect(filesList->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveSettings())); connect(m_ui->filesList->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveSettings()));
connect(filesList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSettings())); connect(m_ui->filesList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSettings()));
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
// set bar height relative to screen dpi // set bar height relative to screen dpi
@@ -113,45 +120,45 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, Tra
#endif #endif
// Downloaded pieces progress bar // Downloaded pieces progress bar
tempProgressBarArea->setVisible(false); m_ui->tempProgressBarArea->setVisible(false);
downloaded_pieces = new DownloadedPiecesBar(this); downloaded_pieces = new DownloadedPiecesBar(this);
groupBarLayout->addWidget(downloaded_pieces, 0, 1); m_ui->groupBarLayout->addWidget(downloaded_pieces, 0, 1);
downloaded_pieces->setFixedHeight(barHeight); downloaded_pieces->setFixedHeight(barHeight);
downloaded_pieces->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); downloaded_pieces->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Pieces availability bar // Pieces availability bar
tempAvailabilityBarArea->setVisible(false); m_ui->tempAvailabilityBarArea->setVisible(false);
pieces_availability = new PieceAvailabilityBar(this); pieces_availability = new PieceAvailabilityBar(this);
groupBarLayout->addWidget(pieces_availability, 1, 1); m_ui->groupBarLayout->addWidget(pieces_availability, 1, 1);
pieces_availability->setFixedHeight(barHeight); pieces_availability->setFixedHeight(barHeight);
pieces_availability->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); pieces_availability->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// Tracker list // Tracker list
trackerList = new TrackerList(this); trackerList = new TrackerList(this);
trackerUpButton->setIcon(GuiIconProvider::instance()->getIcon("go-up")); m_ui->trackerUpButton->setIcon(GuiIconProvider::instance()->getIcon("go-up"));
trackerUpButton->setIconSize(Utils::Misc::smallIconSize()); m_ui->trackerUpButton->setIconSize(Utils::Misc::smallIconSize());
trackerDownButton->setIcon(GuiIconProvider::instance()->getIcon("go-down")); m_ui->trackerDownButton->setIcon(GuiIconProvider::instance()->getIcon("go-down"));
trackerDownButton->setIconSize(Utils::Misc::smallIconSize()); m_ui->trackerDownButton->setIconSize(Utils::Misc::smallIconSize());
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp())); connect(m_ui->trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown())); connect(m_ui->trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
horizontalLayout_trackers->insertWidget(0, trackerList); m_ui->horizontalLayout_trackers->insertWidget(0, trackerList);
connect(trackerList->header(), SIGNAL(sectionMoved(int,int,int)), trackerList, SLOT(saveSettings())); connect(trackerList->header(), SIGNAL(sectionMoved(int,int,int)), trackerList, SLOT(saveSettings()));
connect(trackerList->header(), SIGNAL(sectionResized(int,int,int)), trackerList, SLOT(saveSettings())); connect(trackerList->header(), SIGNAL(sectionResized(int,int,int)), trackerList, SLOT(saveSettings()));
connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), trackerList, SLOT(saveSettings())); connect(trackerList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), trackerList, SLOT(saveSettings()));
// Peers list // Peers list
peersList = new PeerListWidget(this); peersList = new PeerListWidget(this);
peerpage_layout->addWidget(peersList); m_ui->peerpage_layout->addWidget(peersList);
connect(peersList->header(), SIGNAL(sectionMoved(int,int,int)), peersList, SLOT(saveSettings())); connect(peersList->header(), SIGNAL(sectionMoved(int,int,int)), peersList, SLOT(saveSettings()));
connect(peersList->header(), SIGNAL(sectionResized(int,int,int)), peersList, SLOT(saveSettings())); connect(peersList->header(), SIGNAL(sectionResized(int,int,int)), peersList, SLOT(saveSettings()));
connect(peersList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), peersList, SLOT(saveSettings())); connect(peersList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), peersList, SLOT(saveSettings()));
// Speed widget // Speed widget
speedWidget = new SpeedWidget(this); speedWidget = new SpeedWidget(this);
speed_layout->addWidget(speedWidget); m_ui->speed_layout->addWidget(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);
verticalLayout->addLayout(m_tabBar); m_ui->verticalLayout->addLayout(m_tabBar);
connect(m_tabBar, SIGNAL(tabChanged(int)), stackedProperties, SLOT(setCurrentIndex(int))); connect(m_tabBar, SIGNAL(tabChanged(int)), m_ui->stackedProperties, SLOT(setCurrentIndex(int)));
connect(m_tabBar, SIGNAL(tabChanged(int)), this, SLOT(saveSettings())); connect(m_tabBar, SIGNAL(tabChanged(int)), this, SLOT(saveSettings()));
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()));
@@ -159,14 +166,14 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, Tra
refreshTimer = new QTimer(this); refreshTimer = new QTimer(this);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData())); connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
refreshTimer->start(3000); // 3sec refreshTimer->start(3000); // 3sec
editHotkeyFile = new QShortcut(Qt::Key_F2, filesList, 0, 0, Qt::WidgetShortcut); editHotkeyFile = new QShortcut(Qt::Key_F2, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(editHotkeyFile, SIGNAL(activated()), SLOT(renameSelectedFile())); connect(editHotkeyFile, SIGNAL(activated()), SLOT(renameSelectedFile()));
editHotkeyWeb = new QShortcut(Qt::Key_F2, listWebSeeds, 0, 0, Qt::WidgetShortcut); editHotkeyWeb = new QShortcut(Qt::Key_F2, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(editHotkeyWeb, SIGNAL(activated()), SLOT(editWebSeed())); connect(editHotkeyWeb, SIGNAL(activated()), SLOT(editWebSeed()));
connect(listWebSeeds, SIGNAL(doubleClicked(QModelIndex)), SLOT(editWebSeed())); connect(m_ui->listWebSeeds, SIGNAL(doubleClicked(QModelIndex)), SLOT(editWebSeed()));
deleteHotkeyWeb = new QShortcut(QKeySequence::Delete, listWebSeeds, 0, 0, Qt::WidgetShortcut); deleteHotkeyWeb = new QShortcut(QKeySequence::Delete, m_ui->listWebSeeds, 0, 0, Qt::WidgetShortcut);
connect(deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds())); connect(deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds()));
openHotkeyFile = new QShortcut(Qt::Key_Return, filesList, 0, 0, Qt::WidgetShortcut); openHotkeyFile = new QShortcut(Qt::Key_Return, m_ui->filesList, 0, 0, Qt::WidgetShortcut);
connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile())); connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile()));
} }
@@ -186,32 +193,33 @@ PropertiesWidget::~PropertiesWidget()
delete editHotkeyWeb; delete editHotkeyWeb;
delete deleteHotkeyWeb; delete deleteHotkeyWeb;
delete openHotkeyFile; delete openHotkeyFile;
delete m_ui;
qDebug() << Q_FUNC_INFO << "EXIT"; qDebug() << Q_FUNC_INFO << "EXIT";
} }
void PropertiesWidget::showPiecesAvailability(bool show) void PropertiesWidget::showPiecesAvailability(bool show)
{ {
avail_pieces_lbl->setVisible(show); m_ui->avail_pieces_lbl->setVisible(show);
pieces_availability->setVisible(show); pieces_availability->setVisible(show);
avail_average_lbl->setVisible(show); m_ui->avail_average_lbl->setVisible(show);
if (show || !downloaded_pieces->isVisible()) if (show || !downloaded_pieces->isVisible())
line_2->setVisible(show); m_ui->line_2->setVisible(show);
} }
void PropertiesWidget::showPiecesDownloaded(bool show) void PropertiesWidget::showPiecesDownloaded(bool show)
{ {
downloaded_pieces_lbl->setVisible(show); m_ui->downloaded_pieces_lbl->setVisible(show);
downloaded_pieces->setVisible(show); downloaded_pieces->setVisible(show);
progress_lbl->setVisible(show); m_ui->progress_lbl->setVisible(show);
if (show || !pieces_availability->isVisible()) if (show || !pieces_availability->isVisible())
line_2->setVisible(show); m_ui->line_2->setVisible(show);
} }
void PropertiesWidget::setVisibility(bool visible) void PropertiesWidget::setVisibility(bool visible)
{ {
if (!visible && ( state == VISIBLE) ) { if (!visible && ( state == VISIBLE) ) {
QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget()); QSplitter *hSplitter = static_cast<QSplitter *>(parentWidget());
stackedProperties->setVisible(false); m_ui->stackedProperties->setVisible(false);
slideSizes = hSplitter->sizes(); slideSizes = hSplitter->sizes();
hSplitter->handle(1)->setVisible(false); hSplitter->handle(1)->setVisible(false);
hSplitter->handle(1)->setDisabled(true); hSplitter->handle(1)->setDisabled(true);
@@ -222,7 +230,7 @@ void PropertiesWidget::setVisibility(bool visible)
} }
if (visible && ( state == REDUCED) ) { if (visible && ( state == REDUCED) ) {
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);
@@ -236,39 +244,39 @@ void PropertiesWidget::setVisibility(bool visible)
void PropertiesWidget::clear() void PropertiesWidget::clear()
{ {
qDebug("Clearing torrent properties"); qDebug("Clearing torrent properties");
save_path->clear(); m_ui->save_path->clear();
lbl_creationDate->clear(); m_ui->lbl_creationDate->clear();
label_total_pieces_val->clear(); m_ui->label_total_pieces_val->clear();
hash_lbl->clear(); m_ui->hash_lbl->clear();
comment_text->clear(); m_ui->comment_text->clear();
progress_lbl->clear(); m_ui->progress_lbl->clear();
trackerList->clear(); trackerList->clear();
downloaded_pieces->clear(); downloaded_pieces->clear();
pieces_availability->clear(); pieces_availability->clear();
avail_average_lbl->clear(); m_ui->avail_average_lbl->clear();
wasted->clear(); m_ui->wasted->clear();
upTotal->clear(); m_ui->upTotal->clear();
dlTotal->clear(); m_ui->dlTotal->clear();
peersList->clear(); peersList->clear();
lbl_uplimit->clear(); m_ui->lbl_uplimit->clear();
lbl_dllimit->clear(); m_ui->lbl_dllimit->clear();
lbl_elapsed->clear(); m_ui->lbl_elapsed->clear();
lbl_connections->clear(); m_ui->lbl_connections->clear();
reannounce_lbl->clear(); m_ui->reannounce_lbl->clear();
shareRatio->clear(); m_ui->shareRatio->clear();
listWebSeeds->clear(); m_ui->listWebSeeds->clear();
m_contentFilterLine->clear(); m_contentFilterLine->clear();
PropListModel->model()->clear(); PropListModel->model()->clear();
label_eta_val->clear(); m_ui->label_eta_val->clear();
label_seeds_val->clear(); m_ui->label_seeds_val->clear();
label_peers_val->clear(); m_ui->label_peers_val->clear();
label_dl_speed_val->clear(); m_ui->label_dl_speed_val->clear();
label_upload_speed_val->clear(); m_ui->label_upload_speed_val->clear();
label_total_size_val->clear(); m_ui->label_total_size_val->clear();
label_completed_on_val->clear(); m_ui->label_completed_on_val->clear();
label_last_complete_val->clear(); m_ui->label_last_complete_val->clear();
label_created_by_val->clear(); m_ui->label_created_by_val->clear();
label_added_on_val->clear(); m_ui->label_added_on_val->clear();
} }
BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
@@ -276,10 +284,15 @@ BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
return m_torrent; return m_torrent;
} }
QTreeView *PropertiesWidget::getFilesList() const
{
return m_ui->filesList;
}
void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent) void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent)
{ {
if (m_torrent == torrent) if (m_torrent == torrent)
save_path->setText(Utils::Fs::toNativePath(m_torrent->savePath())); m_ui->save_path->setText(Utils::Fs::toNativePath(m_torrent->savePath()));
} }
void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent) void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent)
@@ -305,25 +318,25 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
// Save path // Save path
updateSavePath(m_torrent); updateSavePath(m_torrent);
// Hash // Hash
hash_lbl->setText(m_torrent->hash()); m_ui->hash_lbl->setText(m_torrent->hash());
PropListModel->model()->clear(); PropListModel->model()->clear();
if (m_torrent->hasMetadata()) { if (m_torrent->hasMetadata()) {
// Creation date // Creation date
lbl_creationDate->setText(m_torrent->creationDate().toString(Qt::DefaultLocaleShortDate)); m_ui->lbl_creationDate->setText(m_torrent->creationDate().toString(Qt::DefaultLocaleShortDate));
label_total_size_val->setText(Utils::Misc::friendlyUnit(m_torrent->totalSize())); m_ui->label_total_size_val->setText(Utils::Misc::friendlyUnit(m_torrent->totalSize()));
// Comment // Comment
comment_text->setText(Utils::Misc::parseHtmlLinks(Utils::String::toHtmlEscaped(m_torrent->comment()))); m_ui->comment_text->setText(Utils::Misc::parseHtmlLinks(Utils::String::toHtmlEscaped(m_torrent->comment())));
// URL seeds // URL seeds
loadUrlSeeds(); loadUrlSeeds();
label_created_by_val->setText(Utils::String::toHtmlEscaped(m_torrent->creator())); m_ui->label_created_by_val->setText(Utils::String::toHtmlEscaped(m_torrent->creator()));
// List files in torrent // List files in torrent
PropListModel->model()->setupModelData(m_torrent->info()); PropListModel->model()->setupModelData(m_torrent->info());
filesList->setExpanded(PropListModel->index(0, 0), true); m_ui->filesList->setExpanded(PropListModel->index(0, 0), true);
// Load file priorities // Load file priorities
PropListModel->model()->updateFilesPriorities(m_torrent->filePriorities()); PropListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
@@ -346,8 +359,8 @@ void PropertiesWidget::readSettings()
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 // the following will call saveSettings but shouldn't change any state
if (!filesList->header()->restoreState(pref->getPropFileListState())) if (!m_ui->filesList->header()->restoreState(pref->getPropFileListState()))
filesList->header()->resizeSection(0, 400); // Default 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);
@@ -367,7 +380,7 @@ void PropertiesWidget::saveSettings()
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()));
pref->setPropFileListState(filesList->header()->saveState()); pref->setPropFileListState(m_ui->filesList->header()->saveState());
// Remember current tab // Remember current tab
pref->setPropCurTab(m_tabBar->currentIndex()); pref->setPropCurTab(m_tabBar->currentIndex());
} }
@@ -385,19 +398,19 @@ void PropertiesWidget::loadDynamicData()
if (!m_torrent || (main_window->currentTabWidget() != transferList) || (state != VISIBLE)) return; if (!m_torrent || (main_window->currentTabWidget() != transferList) || (state != VISIBLE)) return;
// Transfer infos // Transfer infos
switch (stackedProperties->currentIndex()) { switch (m_ui->stackedProperties->currentIndex()) {
case PropTabBar::MAIN_TAB: { case PropTabBar::MAIN_TAB: {
wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize())); m_ui->wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
upTotal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalUpload())) m_ui->upTotal->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())));
dlTotal->setText(tr("%1 (%2 this session)").arg(Utils::Misc::friendlyUnit(m_torrent->totalDownload())) m_ui->dlTotal->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())));
lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->uploadLimit(), true)); m_ui->lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->uploadLimit(), true));
lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->downloadLimit(), true)); m_ui->lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8(C_INFINITY) : Utils::Misc::friendlyUnit(m_torrent->downloadLimit(), true));
QString elapsed_txt; QString elapsed_txt;
if (m_torrent->isSeed()) if (m_torrent->isSeed())
@@ -406,51 +419,51 @@ void PropertiesWidget::loadDynamicData()
.arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime())); .arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime()));
else else
elapsed_txt = Utils::Misc::userFriendlyDuration(m_torrent->activeTime()); elapsed_txt = Utils::Misc::userFriendlyDuration(m_torrent->activeTime());
lbl_elapsed->setText(elapsed_txt); m_ui->lbl_elapsed->setText(elapsed_txt);
lbl_connections->setText(tr("%1 (%2 max)", "%1 and %2 are numbers, e.g. 3 (10 max)") m_ui->lbl_connections->setText(tr("%1 (%2 max)", "%1 and %2 are numbers, e.g. 3 (10 max)")
.arg(m_torrent->connectionsCount()) .arg(m_torrent->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())));
label_eta_val->setText(Utils::Misc::userFriendlyDuration(m_torrent->eta())); m_ui->label_eta_val->setText(Utils::Misc::userFriendlyDuration(m_torrent->eta()));
// Update next announce time // Update next announce time
reannounce_lbl->setText(Utils::Misc::userFriendlyDuration(m_torrent->nextAnnounce())); m_ui->reannounce_lbl->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();
shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8(C_INFINITY) : Utils::String::fromDouble(ratio, 2)); m_ui->shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8(C_INFINITY) : Utils::String::fromDouble(ratio, 2));
label_seeds_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)") m_ui->label_seeds_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->seedsCount())) .arg(QString::number(m_torrent->seedsCount()))
.arg(QString::number(m_torrent->totalSeedsCount()))); .arg(QString::number(m_torrent->totalSeedsCount())));
label_peers_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)") m_ui->label_peers_val->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)")
.arg(QString::number(m_torrent->leechsCount())) .arg(QString::number(m_torrent->leechsCount()))
.arg(QString::number(m_torrent->totalLeechersCount()))); .arg(QString::number(m_torrent->totalLeechersCount())));
label_dl_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)") m_ui->label_dl_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->downloadPayloadRate(), true)) .arg(Utils::Misc::friendlyUnit(m_torrent->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)));
label_upload_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)") m_ui->label_upload_speed_val->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
.arg(Utils::Misc::friendlyUnit(m_torrent->uploadPayloadRate(), true)) .arg(Utils::Misc::friendlyUnit(m_torrent->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)));
label_last_complete_val->setText(m_torrent->lastSeenComplete().isValid() ? m_torrent->lastSeenComplete().toString(Qt::DefaultLocaleShortDate) : tr("Never")); m_ui->label_last_complete_val->setText(m_torrent->lastSeenComplete().isValid() ? m_torrent->lastSeenComplete().toString(Qt::DefaultLocaleShortDate) : tr("Never"));
label_completed_on_val->setText(m_torrent->completedTime().isValid() ? m_torrent->completedTime().toString(Qt::DefaultLocaleShortDate) : ""); m_ui->label_completed_on_val->setText(m_torrent->completedTime().isValid() ? m_torrent->completedTime().toString(Qt::DefaultLocaleShortDate) : "");
label_added_on_val->setText(m_torrent->addedTime().toString(Qt::DefaultLocaleShortDate)); m_ui->label_added_on_val->setText(m_torrent->addedTime().toString(Qt::DefaultLocaleShortDate));
if (m_torrent->hasMetadata()) { if (m_torrent->hasMetadata()) {
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->label_total_pieces_val->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave()));
if (!m_torrent->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()); pieces_availability->setAvailability(m_torrent->pieceAvailability());
avail_average_lbl->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3)); m_ui->avail_average_lbl->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
} }
else { else {
showPiecesAvailability(false); showPiecesAvailability(false);
@@ -458,7 +471,7 @@ void PropertiesWidget::loadDynamicData()
// Progress // Progress
qreal progress = m_torrent->progress() * 100.; qreal progress = m_torrent->progress() * 100.;
progress_lbl->setText(Utils::String::fromDouble(progress, 1) + "%"); m_ui->progress_lbl->setText(Utils::String::fromDouble(progress, 1) + "%");
downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces()); downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
} }
else { else {
@@ -484,13 +497,13 @@ void PropertiesWidget::loadDynamicData()
// Files progress // Files progress
if (m_torrent->hasMetadata()) { if (m_torrent->hasMetadata()) {
qDebug("Updating priorities in files tab"); qDebug("Updating priorities in files tab");
filesList->setUpdatesEnabled(false); m_ui->filesList->setUpdatesEnabled(false);
PropListModel->model()->updateFilesProgress(m_torrent->filesProgress()); PropListModel->model()->updateFilesProgress(m_torrent->filesProgress());
// 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.
// PropListModel->model()->updateFilesPriorities(h.file_priorities()); // PropListModel->model()->updateFilesPriorities(h.file_priorities());
filesList->setUpdatesEnabled(true); m_ui->filesList->setUpdatesEnabled(true);
} }
break; break;
} }
@@ -501,13 +514,13 @@ void PropertiesWidget::loadDynamicData()
void PropertiesWidget::loadUrlSeeds() void PropertiesWidget::loadUrlSeeds()
{ {
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> hc_seeds = m_torrent->urlSeeds();
// Add url seeds // Add url seeds
foreach (const QUrl &hc_seed, hc_seeds) { foreach (const QUrl &hc_seed, hc_seeds) {
qDebug("Loading URL seed: %s", qPrintable(hc_seed.toString())); qDebug("Loading URL seed: %s", qPrintable(hc_seed.toString()));
new QListWidgetItem(hc_seed.toString(), listWebSeeds); new QListWidgetItem(hc_seed.toString(), m_ui->listWebSeeds);
} }
} }
@@ -571,7 +584,7 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
{ {
if (!m_torrent) return; if (!m_torrent) return;
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0); QModelIndexList selectedRows = m_ui->filesList->selectionModel()->selectedRows(0);
if (selectedRows.empty()) if (selectedRows.empty())
return; return;
QMenu myFilesLlistMenu; QMenu myFilesLlistMenu;
@@ -587,10 +600,10 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
QMenu subMenu; QMenu subMenu;
if (!m_torrent->isSeed()) { if (!m_torrent->isSeed()) {
subMenu.setTitle(tr("Priority")); subMenu.setTitle(tr("Priority"));
subMenu.addAction(actionNot_downloaded); subMenu.addAction(m_ui->actionNot_downloaded);
subMenu.addAction(actionNormal); subMenu.addAction(m_ui->actionNormal);
subMenu.addAction(actionHigh); subMenu.addAction(m_ui->actionHigh);
subMenu.addAction(actionMaximum); subMenu.addAction(m_ui->actionMaximum);
myFilesLlistMenu.addMenu(&subMenu); myFilesLlistMenu.addMenu(&subMenu);
} }
// Call menu // Call menu
@@ -612,11 +625,11 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
} }
else { else {
int prio = prio::NORMAL; int prio = prio::NORMAL;
if (act == actionHigh) if (act == m_ui->actionHigh)
prio = prio::HIGH; prio = prio::HIGH;
else if (act == actionMaximum) else if (act == m_ui->actionMaximum)
prio = prio::MAXIMUM; prio = prio::MAXIMUM;
else if (act == actionNot_downloaded) else if (act == m_ui->actionNot_downloaded)
prio = prio::IGNORED; prio = prio::IGNORED;
qDebug("Setting files priority"); qDebug("Setting files priority");
@@ -635,7 +648,7 @@ void PropertiesWidget::displayWebSeedListMenu(const QPoint &)
if (!m_torrent) return; if (!m_torrent) return;
QMenu seedMenu; QMenu seedMenu;
QModelIndexList rows = listWebSeeds->selectionModel()->selectedRows(); QModelIndexList rows = m_ui->listWebSeeds->selectionModel()->selectedRows();
QAction *actAdd = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New Web seed")); QAction *actAdd = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New Web seed"));
QAction *actDel = 0; QAction *actDel = 0;
QAction *actCpy = 0; QAction *actCpy = 0;
@@ -663,7 +676,7 @@ void PropertiesWidget::displayWebSeedListMenu(const QPoint &)
void PropertiesWidget::renameSelectedFile() void PropertiesWidget::renameSelectedFile()
{ {
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0); const QModelIndexList selectedIndexes = m_ui->filesList->selectionModel()->selectedRows(0);
if (selectedIndexes.size() != 1) if (selectedIndexes.size() != 1)
return; return;
const QModelIndex index = selectedIndexes.first(); const QModelIndex index = selectedIndexes.first();
@@ -692,7 +705,7 @@ void PropertiesWidget::renameSelectedFile()
path_items.removeLast(); path_items.removeLast();
path_items << new_name_last; path_items << new_name_last;
QString new_name = path_items.join("/"); QString new_name = path_items.join("/");
if (Utils::Fs::sameFileNames(old_name, new_name)) { if (old_name == new_name) {
qDebug("Name did not change"); qDebug("Name did not change");
return; return;
} }
@@ -783,7 +796,7 @@ void PropertiesWidget::renameSelectedFile()
void PropertiesWidget::openSelectedFile() void PropertiesWidget::openSelectedFile()
{ {
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0); const QModelIndexList selectedIndexes = m_ui->filesList->selectionModel()->selectedRows(0);
if (selectedIndexes.size() != 1) if (selectedIndexes.size() != 1)
return; return;
openDoubleClickedFile(selectedIndexes.first()); openDoubleClickedFile(selectedIndexes.first());
@@ -798,7 +811,7 @@ void PropertiesWidget::askWebSeed()
QString::fromUtf8("http://www."), &ok); QString::fromUtf8("http://www."), &ok);
if (!ok) return; if (!ok) return;
qDebug("Adding %s web seed", qPrintable(url_seed)); qDebug("Adding %s web seed", qPrintable(url_seed));
if (!listWebSeeds->findItems(url_seed, Qt::MatchFixedString).empty()) { if (!m_ui->listWebSeeds->findItems(url_seed, Qt::MatchFixedString).empty()) {
QMessageBox::warning(this, "qBittorrent", QMessageBox::warning(this, "qBittorrent",
tr("This URL seed is already in the list."), tr("This URL seed is already in the list."),
QMessageBox::Ok); QMessageBox::Ok);
@@ -812,7 +825,7 @@ void PropertiesWidget::askWebSeed()
void PropertiesWidget::deleteSelectedUrlSeeds() void PropertiesWidget::deleteSelectedUrlSeeds()
{ {
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems(); const QList<QListWidgetItem *> selectedItems = m_ui->listWebSeeds->selectedItems();
if (selectedItems.isEmpty()) return; if (selectedItems.isEmpty()) return;
QList<QUrl> urlSeeds; QList<QUrl> urlSeeds;
@@ -826,7 +839,7 @@ void PropertiesWidget::deleteSelectedUrlSeeds()
void PropertiesWidget::copySelectedWebSeedsToClipboard() const void PropertiesWidget::copySelectedWebSeedsToClipboard() const
{ {
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems(); const QList<QListWidgetItem *> selected_items = m_ui->listWebSeeds->selectedItems();
if (selected_items.isEmpty()) if (selected_items.isEmpty())
return; return;
@@ -839,7 +852,7 @@ void PropertiesWidget::copySelectedWebSeedsToClipboard() const
void PropertiesWidget::editWebSeed() void PropertiesWidget::editWebSeed()
{ {
const QList<QListWidgetItem *> selected_items = listWebSeeds->selectedItems(); const QList<QListWidgetItem *> selected_items = m_ui->listWebSeeds->selectedItems();
if (selected_items.size() != 1) if (selected_items.size() != 1)
return; return;
@@ -852,7 +865,7 @@ void PropertiesWidget::editWebSeed()
if (!result) if (!result)
return; return;
if (!listWebSeeds->findItems(new_seed, Qt::MatchFixedString).empty()) { if (!m_ui->listWebSeeds->findItems(new_seed, 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);
@@ -884,10 +897,10 @@ void PropertiesWidget::filterText(const QString &filter)
{ {
PropListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix)); PropListModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::WildcardUnix));
if (filter.isEmpty()) { if (filter.isEmpty()) {
filesList->collapseAll(); m_ui->filesList->collapseAll();
filesList->expand(PropListModel->index(0, 0)); m_ui->filesList->expand(PropListModel->index(0, 0));
} }
else { else {
filesList->expandAll(); m_ui->filesList->expandAll();
} }
} }

View File

@@ -33,7 +33,6 @@
#include <QShortcut> #include <QShortcut>
#include <QWidget> #include <QWidget>
#include "ui_propertieswidget.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
@@ -52,10 +51,18 @@ class LineEdit;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QAction; class QAction;
class QModelIndex;
class QPushButton;
class QTimer; class QTimer;
class QTreeView;
QT_END_NAMESPACE QT_END_NAMESPACE
class PropertiesWidget: public QWidget, private Ui::PropertiesWidget namespace Ui
{
class PropertiesWidget;
}
class PropertiesWidget: public QWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(PropertiesWidget) Q_DISABLE_COPY(PropertiesWidget)
@@ -69,7 +76,7 @@ public:
BitTorrent::TorrentHandle *getCurrentTorrent() const; BitTorrent::TorrentHandle *getCurrentTorrent() const;
TrackerList *getTrackerList() const { return trackerList; } TrackerList *getTrackerList() const { return trackerList; }
PeerListWidget *getPeerList() const { return peersList; } PeerListWidget *getPeerList() const { return peersList; }
QTreeView *getFilesList() const { return filesList; } QTreeView *getFilesList() const;
SpeedWidget *getSpeedWidget() const { return speedWidget; } SpeedWidget *getSpeedWidget() const { return speedWidget; }
protected: protected:
@@ -107,6 +114,7 @@ private:
void openFolder(const QModelIndex &index, bool containing_folder); void openFolder(const QModelIndex &index, bool containing_folder);
private: private:
Ui::PropertiesWidget *m_ui;
TransferListWidget *transferList; TransferListWidget *transferList;
MainWindow *main_window; MainWindow *main_window;
BitTorrent::TorrentHandle *m_torrent; BitTorrent::TorrentHandle *m_torrent;

View File

@@ -28,6 +28,9 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "proplistdelegate.h"
#include <QApplication>
#include <QComboBox> #include <QComboBox>
#include <QModelIndex> #include <QModelIndex>
#include <QPainter> #include <QPainter>
@@ -46,28 +49,27 @@
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "propertieswidget.h" #include "propertieswidget.h"
#include "proplistdelegate.h"
#include "torrentcontentmodelitem.h" #include "torrentcontentmodelitem.h"
namespace { namespace
{
QPalette progressBarDisabledPalette() QPalette progressBarDisabledPalette()
{ {
auto getPalette = []() auto getPalette = []() {
{ QProgressBar bar;
QProgressBar bar; bar.setEnabled(false);
bar.setEnabled(false); QStyleOptionProgressBar opt;
QStyleOptionProgressBar opt; opt.initFrom(&bar);
opt.initFrom(&bar); return opt.palette;
return opt.palette; };
};
static QPalette palette = getPalette(); static QPalette palette = getPalette();
return palette; return palette;
} }
} }
PropListDelegate::PropListDelegate(PropertiesWidget *properties, QObject *parent) PropListDelegate::PropListDelegate(PropertiesWidget *properties)
: QItemDelegate(parent) : QItemDelegate(properties)
, m_properties(properties) , m_properties(properties)
{ {
} }
@@ -76,36 +78,37 @@ void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
{ {
painter->save(); painter->save();
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option); QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
QItemDelegate::drawBackground(painter, opt, index);
switch(index.column()) { switch (index.column()) {
case PCSIZE: case PCSIZE:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case REMAINING: case REMAINING:
QItemDelegate::drawBackground(painter, opt, index);
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)
QStyleOptionProgressBar newopt; break;
qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect; QStyleOptionProgressBar newopt;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); qreal progress = index.data().toDouble() * 100.;
newopt.progress = (int)progress; newopt.rect = opt.rect;
newopt.maximum = 100; newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.minimum = 0; newopt.progress = int(progress);
newopt.textVisible = true; newopt.maximum = 100;
if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) { newopt.minimum = 0;
newopt.state &= ~QStyle::State_Enabled; newopt.textVisible = true;
newopt.palette = progressBarDisabledPalette(); if (index.sibling(index.row(), PRIORITY).data().toInt() == prio::IGNORED) {
} newopt.state &= ~QStyle::State_Enabled;
else newopt.palette = progressBarDisabledPalette();
newopt.state |= QStyle::State_Enabled; }
else {
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
#ifndef QBT_USES_QT5 #ifndef QBT_USES_QT5
QPlastiqueStyle st; QPlastiqueStyle st;
#else #else
@@ -113,33 +116,28 @@ void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
#endif #endif
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0); st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif #endif
} }
else { break;
// Do not display anything if the file is disabled (progress == 0)
QItemDelegate::drawBackground(painter, opt, index);
}
break;
case PRIORITY: { case PRIORITY: {
QItemDelegate::drawBackground(painter, opt, index); QString text = "";
QString text = ""; switch (index.data().toInt()) {
switch (index.data().toInt()) { case prio::MIXED:
case prio::MIXED: text = tr("Mixed", "Mixed (priorities");
text = tr("Mixed", "Mixed (priorities"); break;
break; case prio::IGNORED:
case prio::IGNORED: text = tr("Not downloaded");
text = tr("Not downloaded"); break;
break; case prio::HIGH:
case prio::HIGH: text = tr("High", "High (priority)");
text = tr("High", "High (priority)"); break;
break; case prio::MAXIMUM:
case prio::MAXIMUM: text = tr("Maximum", "Maximum (priority)");
text = tr("Maximum", "Maximum (priority)"); break;
break; default:
default: text = tr("Normal", "Normal (priority)");
text = tr("Normal", "Normal (priority)"); break;
break; }
} QItemDelegate::drawDisplay(painter, opt, option.rect, text);
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
} }
break; break;
default: default:
@@ -151,9 +149,9 @@ void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{ {
QComboBox *combobox = static_cast<QComboBox*>(editor); QComboBox *combobox = static_cast<QComboBox *>(editor);
// Set combobox index // Set combobox index
switch(index.data().toInt()) { switch (index.data().toInt()) {
case prio::IGNORED: case prio::IGNORED:
combobox->setCurrentIndex(0); combobox->setCurrentIndex(0);
break; break;
@@ -182,7 +180,7 @@ QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
if (index.data().toInt() == prio::MIXED) if (index.data().toInt() == prio::MIXED)
return 0; return 0;
QComboBox* editor = new QComboBox(parent); QComboBox *editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus); editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Do not download", "Do not download (priority)")); editor->addItem(tr("Do not download", "Do not download (priority)"));
editor->addItem(tr("Normal", "Normal (priority)")); editor->addItem(tr("Normal", "Normal (priority)"));
@@ -193,11 +191,11 @@ QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewI
void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{ {
QComboBox *combobox = static_cast<QComboBox*>(editor); QComboBox *combobox = static_cast<QComboBox *>(editor);
int value = combobox->currentIndex(); int value = combobox->currentIndex();
qDebug("PropListDelegate: setModelData(%d)", value); qDebug("PropListDelegate: setModelData(%d)", value);
switch(value) { switch (value) {
case 0: case 0:
model->setData(index, prio::IGNORED); // IGNORED model->setData(index, prio::IGNORED); // IGNORED
break; break;

View File

@@ -49,20 +49,20 @@ enum PropColumn
REMAINING REMAINING
}; };
class PropListDelegate : public QItemDelegate class PropListDelegate: public QItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
PropListDelegate(PropertiesWidget *properties = 0, QObject *parent = 0); PropListDelegate(PropertiesWidget *properties);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */, const QModelIndex &index) const override;
public slots: public slots:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex & /* index */) const override;
signals: signals:
void filteredFilesChanged() const; void filteredFilesChanged() const;

View File

@@ -28,6 +28,9 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "trackerlist.h"
#include <QApplication>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QStringList> #include <QStringList>
#include <QMenu> #include <QMenu>
@@ -35,11 +38,11 @@
#include <QAction> #include <QAction>
#include <QColor> #include <QColor>
#include <QDebug> #include <QDebug>
#include <QHeaderView>
#include <QUrl> #include <QUrl>
#include <QMessageBox> #include <QMessageBox>
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
#include <QTableView> #include <QTableView>
#include <QHeaderView>
#endif #endif
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
@@ -52,7 +55,6 @@
#include "trackersadditiondlg.h" #include "trackersadditiondlg.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "trackerlist.h"
TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) { TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) {
// Graphical settings // Graphical settings

View File

@@ -27,6 +27,7 @@
* *
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "trackersadditiondlg.h"
#include <QStringList> #include <QStringList>
#include <QMessageBox> #include <QMessageBox>
@@ -40,21 +41,27 @@
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentry.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "trackersadditiondlg.h" #include "ui_trackersadditiondlg.h"
TrackersAdditionDlg::TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent) TrackersAdditionDlg::TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_ui(new Ui::TrackersAdditionDlg())
, m_torrent(torrent) , m_torrent(torrent)
{ {
setupUi(this); m_ui->setupUi(this);
// Icons // Icons
uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download")); m_ui->uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download"));
}
TrackersAdditionDlg::~TrackersAdditionDlg()
{
delete m_ui;
} }
QStringList TrackersAdditionDlg::newTrackers() const QStringList TrackersAdditionDlg::newTrackers() const
{ {
QStringList cleanTrackers; QStringList cleanTrackers;
foreach (QString url, trackers_list->toPlainText().split("\n")) { foreach (QString url, m_ui->trackers_list->toPlainText().split("\n")) {
url = url.trimmed(); url = url.trimmed();
if (!url.isEmpty()) if (!url.isEmpty())
cleanTrackers << url; cleanTrackers << url;
@@ -64,8 +71,8 @@ QStringList TrackersAdditionDlg::newTrackers() const
void TrackersAdditionDlg::on_uTorrentListButton_clicked() void TrackersAdditionDlg::on_uTorrentListButton_clicked()
{ {
uTorrentListButton->setEnabled(false); m_ui->uTorrentListButton->setEnabled(false);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(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
@@ -78,7 +85,7 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
if (!list_file.open(QFile::ReadOnly)) { if (!list_file.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);
uTorrentListButton->setEnabled(true); m_ui->uTorrentListButton->setEnabled(true);
Utils::Fs::forceRemove(path); Utils::Fs::forceRemove(path);
return; return;
} }
@@ -86,7 +93,7 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
// Load from torrent handle // Load from torrent handle
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 = trackers_list->toPlainText().split("\n"); QStringList tmp = m_ui->trackers_list->toPlainText().split("\n");
foreach (const QString &user_url, tmp) { foreach (const QString &user_url, tmp) {
BitTorrent::TrackerEntry userTracker(user_url); BitTorrent::TrackerEntry userTracker(user_url);
if (!existingTrackers.contains(userTracker)) if (!existingTrackers.contains(userTracker))
@@ -94,15 +101,15 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
} }
// Add new trackers to the list // Add new trackers to the list
if (!trackers_list->toPlainText().isEmpty() && !trackers_list->toPlainText().endsWith("\n")) if (!m_ui->trackers_list->toPlainText().isEmpty() && !m_ui->trackers_list->toPlainText().endsWith("\n"))
trackers_list->insertPlainText("\n"); m_ui->trackers_list->insertPlainText("\n");
int nb = 0; int nb = 0;
while (!list_file.atEnd()) { while (!list_file.atEnd()) {
const QByteArray line = list_file.readLine().trimmed(); const QByteArray line = list_file.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)) {
trackers_list->insertPlainText(line + "\n"); m_ui->trackers_list->insertPlainText(line + "\n");
++nb; ++nb;
} }
} }
@@ -111,7 +118,7 @@ void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path
Utils::Fs::forceRemove(path); Utils::Fs::forceRemove(path);
//To restore the cursor ... //To restore the cursor ...
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true); m_ui->uTorrentListButton->setEnabled(true);
// Display information message if necessary // Display information message if necessary
if (nb == 0) if (nb == 0)
QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok); QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok);
@@ -121,7 +128,7 @@ void TrackersAdditionDlg::getTrackerError(const QString &, const QString &error)
{ {
//To restore the cursor ... //To restore the cursor ...
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true); m_ui->uTorrentListButton->setEnabled(true);
QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok); QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok);
} }

View File

@@ -32,7 +32,6 @@
#define TRACKERSADDITION_H #define TRACKERSADDITION_H
#include <QDialog> #include <QDialog>
#include "ui_trackersadditiondlg.h"
class QString; class QString;
class QStringList; class QStringList;
@@ -42,12 +41,18 @@ namespace BitTorrent
class TorrentHandle; class TorrentHandle;
} }
class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg namespace Ui
{
class TrackersAdditionDlg;
}
class TrackersAdditionDlg : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0); TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0);
~TrackersAdditionDlg();
QStringList newTrackers() const; QStringList newTrackers() const;
static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent); static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent);
@@ -58,6 +63,7 @@ public slots:
void getTrackerError(const QString &, const QString &error); void getTrackerError(const QString &, const QString &error);
private: private:
Ui::TrackersAdditionDlg *m_ui;
BitTorrent::TorrentHandle *const m_torrent; BitTorrent::TorrentHandle *const m_torrent;
}; };

View File

@@ -28,6 +28,8 @@
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org * Contact : chris@qbittorrent.org arnaud@qbittorrent.org
*/ */
#include "rss_imp.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QMenu> #include <QMenu>
#include <QStandardItemModel> #include <QStandardItemModel>
@@ -37,7 +39,6 @@
#include <QDragMoveEvent> #include <QDragMoveEvent>
#include <QDebug> #include <QDebug>
#include "rss_imp.h"
#include "feedlistwidget.h" #include "feedlistwidget.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
@@ -52,6 +53,8 @@
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "addnewtorrentdialog.h" #include "addnewtorrentdialog.h"
#include "ui_rss.h"
namespace Article namespace Article
{ {
enum ArticleRoles enum ArticleRoles
@@ -73,33 +76,33 @@ void RSSImp::displayRSSListMenu(const QPoint &pos)
QMenu myRSSListMenu(this); QMenu myRSSListMenu(this);
QList<QTreeWidgetItem * > selectedItems = m_feedList->selectedItems(); QList<QTreeWidgetItem * > selectedItems = m_feedList->selectedItems();
if (selectedItems.size() > 0) { if (selectedItems.size() > 0) {
myRSSListMenu.addAction(actionUpdate); myRSSListMenu.addAction(m_ui->actionUpdate);
myRSSListMenu.addAction(actionMark_items_read); myRSSListMenu.addAction(m_ui->actionMark_items_read);
myRSSListMenu.addSeparator(); myRSSListMenu.addSeparator();
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager->rootFolder()) { if (m_feedList->getRSSItem(selectedItems.first()) != m_rssManager->rootFolder()) {
myRSSListMenu.addAction(actionRename); myRSSListMenu.addAction(m_ui->actionRename);
myRSSListMenu.addAction(actionDelete); myRSSListMenu.addAction(m_ui->actionDelete);
myRSSListMenu.addSeparator(); myRSSListMenu.addSeparator();
if (m_feedList->isFolder(selectedItems.first())) if (m_feedList->isFolder(selectedItems.first()))
myRSSListMenu.addAction(actionNew_folder); myRSSListMenu.addAction(m_ui->actionNew_folder);
} }
} }
else { else {
myRSSListMenu.addAction(actionDelete); myRSSListMenu.addAction(m_ui->actionDelete);
myRSSListMenu.addSeparator(); myRSSListMenu.addSeparator();
} }
myRSSListMenu.addAction(actionNew_subscription); myRSSListMenu.addAction(m_ui->actionNew_subscription);
if (m_feedList->isFeed(selectedItems.first())) { if (m_feedList->isFeed(selectedItems.first())) {
myRSSListMenu.addSeparator(); myRSSListMenu.addSeparator();
myRSSListMenu.addAction(actionCopy_feed_URL); myRSSListMenu.addAction(m_ui->actionCopy_feed_URL);
} }
} }
else { else {
myRSSListMenu.addAction(actionNew_subscription); myRSSListMenu.addAction(m_ui->actionNew_subscription);
myRSSListMenu.addAction(actionNew_folder); myRSSListMenu.addAction(m_ui->actionNew_folder);
myRSSListMenu.addSeparator(); myRSSListMenu.addSeparator();
myRSSListMenu.addAction(actionUpdate_all_feeds); myRSSListMenu.addAction(m_ui->actionUpdate_all_feeds);
} }
myRSSListMenu.exec(QCursor::pos()); myRSSListMenu.exec(QCursor::pos());
} }
@@ -107,7 +110,7 @@ void RSSImp::displayRSSListMenu(const QPoint &pos)
void RSSImp::displayItemsListMenu(const QPoint &) void RSSImp::displayItemsListMenu(const QPoint &)
{ {
QMenu myItemListMenu(this); QMenu myItemListMenu(this);
QList<QListWidgetItem * > selectedItems = listArticles->selectedItems(); QList<QListWidgetItem * > selectedItems = m_ui->listArticles->selectedItems();
if (selectedItems.size() <= 0) if (selectedItems.size() <= 0)
return; return;
@@ -128,9 +131,9 @@ void RSSImp::displayItemsListMenu(const QPoint &)
break; break;
} }
if (hasTorrent) if (hasTorrent)
myItemListMenu.addAction(actionDownload_torrent); myItemListMenu.addAction(m_ui->actionDownload_torrent);
if (hasLink) if (hasLink)
myItemListMenu.addAction(actionOpen_news_URL); myItemListMenu.addAction(m_ui->actionOpen_news_URL);
if (hasTorrent || hasLink) if (hasTorrent || hasLink)
myItemListMenu.exec(QCursor::pos()); myItemListMenu.exec(QCursor::pos());
} }
@@ -323,7 +326,7 @@ void RSSImp::refreshAllFeeds()
void RSSImp::downloadSelectedTorrents() void RSSImp::downloadSelectedTorrents()
{ {
QList<QListWidgetItem * > selected_items = listArticles->selectedItems(); QList<QListWidgetItem * > selected_items = m_ui->listArticles->selectedItems();
if (selected_items.size() <= 0) if (selected_items.size() <= 0)
return; return;
foreach (QListWidgetItem *item, selected_items) { foreach (QListWidgetItem *item, selected_items) {
@@ -353,7 +356,7 @@ void RSSImp::downloadSelectedTorrents()
// open the url of the selected RSS articles in the Web browser // open the url of the selected RSS articles in the Web browser
void RSSImp::openSelectedArticlesUrls() void RSSImp::openSelectedArticlesUrls()
{ {
QList<QListWidgetItem * > selected_items = listArticles->selectedItems(); QList<QListWidgetItem * > selected_items = m_ui->listArticles->selectedItems();
if (selected_items.size() <= 0) if (selected_items.size() <= 0)
return; return;
foreach (QListWidgetItem *item, selected_items) { foreach (QListWidgetItem *item, selected_items) {
@@ -519,7 +522,7 @@ QListWidgetItem *RSSImp::createArticleListItem(const Rss::ArticlePtr &article)
void RSSImp::populateArticleList(QTreeWidgetItem *item) void RSSImp::populateArticleList(QTreeWidgetItem *item)
{ {
if (!item) { if (!item) {
listArticles->clear(); m_ui->listArticles->clear();
return; return;
} }
@@ -528,9 +531,9 @@ void RSSImp::populateArticleList(QTreeWidgetItem *item)
return; return;
// Clear the list first // Clear the list first
textBrowser->clear(); m_ui->textBrowser->clear();
m_currentArticle = 0; m_currentArticle = 0;
listArticles->clear(); m_ui->listArticles->clear();
qDebug("Getting the list of news"); qDebug("Getting the list of news");
Rss::ArticleList articles; Rss::ArticleList articles;
@@ -542,7 +545,7 @@ void RSSImp::populateArticleList(QTreeWidgetItem *item)
qDebug("Got the list of news"); qDebug("Got the list of news");
foreach (const Rss::ArticlePtr &article, articles) { foreach (const Rss::ArticlePtr &article, articles) {
QListWidgetItem *articleItem = createArticleListItem(article); QListWidgetItem *articleItem = createArticleListItem(article);
listArticles->addItem(articleItem); m_ui->listArticles->addItem(articleItem);
} }
qDebug("Added all news to the GUI"); qDebug("Added all news to the GUI");
} }
@@ -550,7 +553,7 @@ void RSSImp::populateArticleList(QTreeWidgetItem *item)
// display a news // display a news
void RSSImp::refreshTextBrowser() void RSSImp::refreshTextBrowser()
{ {
QList<QListWidgetItem * > selection = listArticles->selectedItems(); QList<QListWidgetItem * > selection = m_ui->listArticles->selectedItems();
if (selection.empty()) return; if (selection.empty()) return;
QListWidgetItem *item = selection.first(); QListWidgetItem *item = selection.first();
Q_ASSERT(item); Q_ASSERT(item);
@@ -601,7 +604,7 @@ void RSSImp::refreshTextBrowser()
html += "<pre>" + description + "</pre>"; html += "<pre>" + description + "</pre>";
} }
html += "</div>"; html += "</div>";
textBrowser->setHtml(html); m_ui->textBrowser->setHtml(html);
article->markAsRead(); article->markAsRead();
item->setData(Article::ColorRole, QVariant(QColor("grey"))); item->setData(Article::ColorRole, QVariant(QColor("grey")));
item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png"))); item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png")));
@@ -614,8 +617,8 @@ void RSSImp::saveSlidersPosition()
{ {
// Remember sliders positions // Remember sliders positions
Preferences *const pref = Preferences::instance(); Preferences *const pref = Preferences::instance();
pref->setRssSideSplitterState(splitterSide->saveState()); pref->setRssSideSplitterState(m_ui->splitterSide->saveState());
pref->setRssMainSplitterState(splitterMain->saveState()); pref->setRssMainSplitterState(m_ui->splitterMain->saveState());
qDebug("Splitters position saved"); qDebug("Splitters position saved");
} }
@@ -624,10 +627,10 @@ void RSSImp::restoreSlidersPosition()
const Preferences *const pref = Preferences::instance(); const Preferences *const pref = Preferences::instance();
const QByteArray stateSide = pref->getRssSideSplitterState(); const QByteArray stateSide = pref->getRssSideSplitterState();
if (!stateSide.isEmpty()) if (!stateSide.isEmpty())
splitterSide->restoreState(stateSide); m_ui->splitterSide->restoreState(stateSide);
const QByteArray stateMain = pref->getRssMainSplitterState(); const QByteArray stateMain = pref->getRssMainSplitterState();
if (!stateMain.isEmpty()) if (!stateMain.isEmpty())
splitterMain->restoreState(stateMain); m_ui->splitterMain->restoreState(stateMain);
} }
void RSSImp::updateItemsInfos(const QList<QTreeWidgetItem *> &items) void RSSImp::updateItemsInfos(const QList<QTreeWidgetItem *> &items)
@@ -701,29 +704,30 @@ void RSSImp::updateRefreshInterval(uint val)
} }
RSSImp::RSSImp(QWidget *parent) RSSImp::RSSImp(QWidget *parent)
: QWidget(parent), : QWidget(parent)
m_rssManager(new Rss::Manager) , m_ui(new Ui::RSS())
, m_rssManager(new Rss::Manager)
{ {
setupUi(this); m_ui->setupUi(this);
// Icons // Icons
actionCopy_feed_URL->setIcon(GuiIconProvider::instance()->getIcon("edit-copy")); m_ui->actionCopy_feed_URL->setIcon(GuiIconProvider::instance()->getIcon("edit-copy"));
actionDelete->setIcon(GuiIconProvider::instance()->getIcon("edit-delete")); m_ui->actionDelete->setIcon(GuiIconProvider::instance()->getIcon("edit-delete"));
actionDownload_torrent->setIcon(GuiIconProvider::instance()->getIcon("download")); m_ui->actionDownload_torrent->setIcon(GuiIconProvider::instance()->getIcon("download"));
actionMark_items_read->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read")); m_ui->actionMark_items_read->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read"));
actionNew_folder->setIcon(GuiIconProvider::instance()->getIcon("folder-new")); m_ui->actionNew_folder->setIcon(GuiIconProvider::instance()->getIcon("folder-new"));
actionNew_subscription->setIcon(GuiIconProvider::instance()->getIcon("list-add")); m_ui->actionNew_subscription->setIcon(GuiIconProvider::instance()->getIcon("list-add"));
actionOpen_news_URL->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl")); m_ui->actionOpen_news_URL->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl"));
actionRename->setIcon(GuiIconProvider::instance()->getIcon("edit-rename")); m_ui->actionRename->setIcon(GuiIconProvider::instance()->getIcon("edit-rename"));
actionUpdate->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); m_ui->actionUpdate->setIcon(GuiIconProvider::instance()->getIcon("view-refresh"));
actionUpdate_all_feeds->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); m_ui->actionUpdate_all_feeds->setIcon(GuiIconProvider::instance()->getIcon("view-refresh"));
newFeedButton->setIcon(GuiIconProvider::instance()->getIcon("list-add")); m_ui->newFeedButton->setIcon(GuiIconProvider::instance()->getIcon("list-add"));
markReadButton->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read")); m_ui->markReadButton->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read"));
updateAllButton->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); m_ui->updateAllButton->setIcon(GuiIconProvider::instance()->getIcon("view-refresh"));
rssDownloaderBtn->setIcon(GuiIconProvider::instance()->getIcon("download")); m_ui->rssDownloaderBtn->setIcon(GuiIconProvider::instance()->getIcon("download"));
settingsButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system")); m_ui->settingsButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system"));
m_feedList = new FeedListWidget(splitterSide, m_rssManager); m_feedList = new FeedListWidget(m_ui->splitterSide, m_rssManager);
splitterSide->insertWidget(0, m_feedList); m_ui->splitterSide->insertWidget(0, m_feedList);
editHotkey = new QShortcut(Qt::Key_F2, m_feedList, 0, 0, Qt::WidgetShortcut); editHotkey = new QShortcut(Qt::Key_F2, m_feedList, 0, 0, Qt::WidgetShortcut);
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile())); connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedRssFile()));
connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile())); connect(m_feedList, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedRssFile()));
@@ -742,33 +746,33 @@ RSSImp::RSSImp(QWidget *parent)
connect(m_rssManager.data(), SIGNAL(feedIconChanged(QString,QString)), SLOT(updateFeedIcon(QString,QString))); connect(m_rssManager.data(), SIGNAL(feedIconChanged(QString,QString)), SLOT(updateFeedIcon(QString,QString)));
connect(m_feedList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(displayRSSListMenu(const QPoint&))); connect(m_feedList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(displayRSSListMenu(const QPoint&)));
connect(listArticles, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(displayItemsListMenu(const QPoint&))); connect(m_ui->listArticles, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(displayItemsListMenu(const QPoint&)));
// Feeds list actions // Feeds list actions
connect(actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedItems())); connect(m_ui->actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedItems()));
connect(actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedRssFile())); connect(m_ui->actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedRssFile()));
connect(actionUpdate, SIGNAL(triggered()), this, SLOT(refreshSelectedItems())); connect(m_ui->actionUpdate, SIGNAL(triggered()), this, SLOT(refreshSelectedItems()));
connect(actionNew_folder, SIGNAL(triggered()), this, SLOT(askNewFolder())); connect(m_ui->actionNew_folder, SIGNAL(triggered()), this, SLOT(askNewFolder()));
connect(actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked())); connect(m_ui->actionNew_subscription, SIGNAL(triggered()), this, SLOT(on_newFeedButton_clicked()));
connect(actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(refreshAllFeeds())); connect(m_ui->actionUpdate_all_feeds, SIGNAL(triggered()), this, SLOT(refreshAllFeeds()));
connect(updateAllButton, SIGNAL(clicked()), SLOT(refreshAllFeeds())); connect(m_ui->updateAllButton, SIGNAL(clicked()), SLOT(refreshAllFeeds()));
connect(actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL())); connect(m_ui->actionCopy_feed_URL, SIGNAL(triggered()), this, SLOT(copySelectedFeedsURL()));
connect(actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked())); connect(m_ui->actionMark_items_read, SIGNAL(triggered()), this, SLOT(on_markReadButton_clicked()));
// News list actions // News list actions
connect(actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openSelectedArticlesUrls())); connect(m_ui->actionOpen_news_URL, SIGNAL(triggered()), this, SLOT(openSelectedArticlesUrls()));
connect(actionDownload_torrent, SIGNAL(triggered()), this, SLOT(downloadSelectedTorrents())); connect(m_ui->actionDownload_torrent, SIGNAL(triggered()), this, SLOT(downloadSelectedTorrents()));
connect(m_feedList, SIGNAL(currentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)), this, SLOT(populateArticleList(QTreeWidgetItem *))); connect(m_feedList, SIGNAL(currentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)), this, SLOT(populateArticleList(QTreeWidgetItem *)));
connect(m_feedList, SIGNAL(foldersAltered(QList<QTreeWidgetItem * >)), this, SLOT(updateItemsInfos(QList<QTreeWidgetItem * >))); connect(m_feedList, SIGNAL(foldersAltered(QList<QTreeWidgetItem * >)), this, SLOT(updateItemsInfos(QList<QTreeWidgetItem * >)));
connect(listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser())); connect(m_ui->listArticles, SIGNAL(itemSelectionChanged()), this, SLOT(refreshTextBrowser()));
connect(listArticles, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(downloadSelectedTorrents())); connect(m_ui->listArticles, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(downloadSelectedTorrents()));
// Restore sliders position // Restore sliders position
restoreSlidersPosition(); restoreSlidersPosition();
// Bind saveSliders slots // Bind saveSliders slots
connect(splitterMain, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSlidersPosition())); connect(m_ui->splitterMain, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSlidersPosition()));
connect(splitterSide, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSlidersPosition())); connect(m_ui->splitterSide, SIGNAL(splitterMoved(int,int)), this, SLOT(saveSlidersPosition()));
qDebug("RSSImp constructed"); qDebug("RSSImp constructed");
} }
@@ -780,6 +784,7 @@ RSSImp::~RSSImp()
delete editHotkey; delete editHotkey;
delete deleteHotkey; delete deleteHotkey;
delete m_feedList; delete m_feedList;
delete m_ui;
qDebug("RSSImp deleted"); qDebug("RSSImp deleted");
} }

View File

@@ -37,15 +37,20 @@
#include "base/rss/rssfolder.h" #include "base/rss/rssfolder.h"
#include "base/rss/rssmanager.h" #include "base/rss/rssmanager.h"
#include "ui_rss.h"
class FeedListWidget; class FeedListWidget;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QListWidgetItem;
class QTreeWidgetItem; class QTreeWidgetItem;
QT_END_NAMESPACE QT_END_NAMESPACE
class RSSImp: public QWidget, public Ui::RSS namespace Ui
{
class RSS;
}
class RSSImp: public QWidget
{ {
Q_OBJECT Q_OBJECT
@@ -92,6 +97,7 @@ private:
static QTreeWidgetItem *createFolderListItem(const Rss::FilePtr &rssFile); static QTreeWidgetItem *createFolderListItem(const Rss::FilePtr &rssFile);
private: private:
Ui::RSS *m_ui;
Rss::ManagerPtr m_rssManager; Rss::ManagerPtr m_rssManager;
FeedListWidget *m_feedList; FeedListWidget *m_feedList;
QListWidgetItem *m_currentArticle; QListWidgetItem *m_currentArticle;

View File

@@ -29,6 +29,8 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "pluginselectdlg.h"
#include <QHeaderView> #include <QHeaderView>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
@@ -50,7 +52,7 @@
#include "pluginsourcedlg.h" #include "pluginsourcedlg.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "autoexpandabledialog.h" #include "autoexpandabledialog.h"
#include "pluginselectdlg.h" #include "ui_pluginselectdlg.h"
enum PluginColumns enum PluginColumns
{ {
@@ -63,31 +65,32 @@ enum PluginColumns
PluginSelectDlg::PluginSelectDlg(SearchEngine *pluginManager, QWidget *parent) PluginSelectDlg::PluginSelectDlg(SearchEngine *pluginManager, QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_ui(new Ui::PluginSelectDlg())
, m_pluginManager(pluginManager) , m_pluginManager(pluginManager)
, m_asyncOps(0) , m_asyncOps(0)
{ {
setupUi(this); m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
#ifdef QBT_USES_QT5 #ifdef QBT_USES_QT5
// This hack fixes reordering of first column with Qt5. // This hack fixes reordering of first column with Qt5.
// https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777 // https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
QTableView unused; QTableView unused;
unused.setVerticalHeader(pluginsTree->header()); unused.setVerticalHeader(m_ui->pluginsTree->header());
pluginsTree->header()->setParent(pluginsTree); m_ui->pluginsTree->header()->setParent(m_ui->pluginsTree);
unused.setVerticalHeader(new QHeaderView(Qt::Horizontal)); unused.setVerticalHeader(new QHeaderView(Qt::Horizontal));
#endif #endif
pluginsTree->setRootIsDecorated(false); m_ui->pluginsTree->setRootIsDecorated(false);
pluginsTree->header()->resizeSection(0, 160); m_ui->pluginsTree->header()->resizeSection(0, 160);
pluginsTree->header()->resizeSection(1, 80); m_ui->pluginsTree->header()->resizeSection(1, 80);
pluginsTree->header()->resizeSection(2, 200); m_ui->pluginsTree->header()->resizeSection(2, 200);
pluginsTree->hideColumn(PLUGIN_ID); m_ui->pluginsTree->hideColumn(PLUGIN_ID);
actionUninstall->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); m_ui->actionUninstall->setIcon(GuiIconProvider::instance()->getIcon("list-remove"));
connect(actionEnable, SIGNAL(toggled(bool)), this, SLOT(enableSelection(bool))); connect(m_ui->actionEnable, SIGNAL(toggled(bool)), this, SLOT(enableSelection(bool)));
connect(pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&))); connect(m_ui->pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&)));
connect(pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(togglePluginState(QTreeWidgetItem*, int))); connect(m_ui->pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(togglePluginState(QTreeWidgetItem*, int)));
loadSupportedSearchPlugins(); loadSupportedSearchPlugins();
@@ -104,6 +107,7 @@ PluginSelectDlg::PluginSelectDlg(SearchEngine *pluginManager, QWidget *parent)
PluginSelectDlg::~PluginSelectDlg() PluginSelectDlg::~PluginSelectDlg()
{ {
emit pluginsChanged(); emit pluginsChanged();
delete m_ui;
} }
void PluginSelectDlg::dropEvent(QDropEvent *event) void PluginSelectDlg::dropEvent(QDropEvent *event)
@@ -159,11 +163,11 @@ void PluginSelectDlg::togglePluginState(QTreeWidgetItem *item, int)
m_pluginManager->enablePlugin(plugin->name, !plugin->enabled); m_pluginManager->enablePlugin(plugin->name, !plugin->enabled);
if (plugin->enabled) { if (plugin->enabled) {
item->setText(PLUGIN_STATE, tr("Yes")); item->setText(PLUGIN_STATE, tr("Yes"));
setRowColor(pluginsTree->indexOfTopLevelItem(item), "green"); setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "green");
} }
else { else {
item->setText(PLUGIN_STATE, tr("No")); item->setText(PLUGIN_STATE, tr("No"));
setRowColor(pluginsTree->indexOfTopLevelItem(item), "red"); setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "red");
} }
} }
@@ -171,14 +175,14 @@ void PluginSelectDlg::displayContextMenu(const QPoint&)
{ {
QMenu myContextMenu(this); QMenu myContextMenu(this);
// Enable/disable pause/start action given the DL state // Enable/disable pause/start action given the DL state
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems(); QList<QTreeWidgetItem *> items = m_ui->pluginsTree->selectedItems();
if (items.isEmpty()) return; if (items.isEmpty()) return;
QString first_id = items.first()->text(PLUGIN_ID); QString first_id = items.first()->text(PLUGIN_ID);
actionEnable->setChecked(m_pluginManager->pluginInfo(first_id)->enabled); m_ui->actionEnable->setChecked(m_pluginManager->pluginInfo(first_id)->enabled);
myContextMenu.addAction(actionEnable); myContextMenu.addAction(m_ui->actionEnable);
myContextMenu.addSeparator(); myContextMenu.addSeparator();
myContextMenu.addAction(actionUninstall); myContextMenu.addAction(m_ui->actionUninstall);
myContextMenu.exec(QCursor::pos()); myContextMenu.exec(QCursor::pos());
} }
@@ -190,8 +194,8 @@ void PluginSelectDlg::on_closeButton_clicked()
void PluginSelectDlg::on_actionUninstall_triggered() void PluginSelectDlg::on_actionUninstall_triggered()
{ {
bool error = false; bool error = false;
foreach (QTreeWidgetItem *item, pluginsTree->selectedItems()) { foreach (QTreeWidgetItem *item, m_ui->pluginsTree->selectedItems()) {
int index = pluginsTree->indexOfTopLevelItem(item); int index = m_ui->pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1); Q_ASSERT(index != -1);
QString id = item->text(PLUGIN_ID); QString id = item->text(PLUGIN_ID);
if (m_pluginManager->uninstallPlugin(id)) { if (m_pluginManager->uninstallPlugin(id)) {
@@ -214,8 +218,8 @@ void PluginSelectDlg::on_actionUninstall_triggered()
void PluginSelectDlg::enableSelection(bool enable) void PluginSelectDlg::enableSelection(bool enable)
{ {
foreach (QTreeWidgetItem *item, pluginsTree->selectedItems()) { foreach (QTreeWidgetItem *item, m_ui->pluginsTree->selectedItems()) {
int index = pluginsTree->indexOfTopLevelItem(item); int index = m_ui->pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1); Q_ASSERT(index != -1);
QString id = item->text(PLUGIN_ID); QString id = item->text(PLUGIN_ID);
m_pluginManager->enablePlugin(id, enable); m_pluginManager->enablePlugin(id, enable);
@@ -233,8 +237,8 @@ void PluginSelectDlg::enableSelection(bool enable)
// Set the color of a row in data model // Set the color of a row in data model
void PluginSelectDlg::setRowColor(int row, QString color) void PluginSelectDlg::setRowColor(int row, QString color)
{ {
QTreeWidgetItem *item = pluginsTree->topLevelItem(row); QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(row);
for (int i = 0; i < pluginsTree->columnCount(); ++i) { for (int i = 0; i < m_ui->pluginsTree->columnCount(); ++i) {
item->setData(i, Qt::ForegroundRole, QVariant(QColor(color))); item->setData(i, Qt::ForegroundRole, QVariant(QColor(color)));
} }
} }
@@ -243,8 +247,8 @@ QList<QTreeWidgetItem*> PluginSelectDlg::findItemsWithUrl(QString url)
{ {
QList<QTreeWidgetItem*> res; QList<QTreeWidgetItem*> res;
for (int i = 0; i < pluginsTree->topLevelItemCount(); ++i) { for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = pluginsTree->topLevelItem(i); QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(i);
if (url.startsWith(item->text(PLUGIN_URL), Qt::CaseInsensitive)) if (url.startsWith(item->text(PLUGIN_URL), Qt::CaseInsensitive))
res << item; res << item;
} }
@@ -254,8 +258,8 @@ QList<QTreeWidgetItem*> PluginSelectDlg::findItemsWithUrl(QString url)
QTreeWidgetItem* PluginSelectDlg::findItemWithID(QString id) QTreeWidgetItem* PluginSelectDlg::findItemWithID(QString id)
{ {
for (int i = 0; i < pluginsTree->topLevelItemCount(); ++i) { for (int i = 0; i < m_ui->pluginsTree->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = pluginsTree->topLevelItem(i); QTreeWidgetItem *item = m_ui->pluginsTree->topLevelItem(i);
if (id == item->text(PLUGIN_ID)) if (id == item->text(PLUGIN_ID))
return item; return item;
} }
@@ -266,25 +270,25 @@ QTreeWidgetItem* PluginSelectDlg::findItemWithID(QString id)
void PluginSelectDlg::loadSupportedSearchPlugins() void PluginSelectDlg::loadSupportedSearchPlugins()
{ {
// Some clean up first // Some clean up first
pluginsTree->clear(); m_ui->pluginsTree->clear();
foreach (QString name, m_pluginManager->allPlugins()) foreach (QString name, m_pluginManager->allPlugins())
addNewPlugin(name); addNewPlugin(name);
} }
void PluginSelectDlg::addNewPlugin(QString pluginName) void PluginSelectDlg::addNewPlugin(QString pluginName)
{ {
QTreeWidgetItem *item = new QTreeWidgetItem(pluginsTree); QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->pluginsTree);
PluginInfo *plugin = m_pluginManager->pluginInfo(pluginName); PluginInfo *plugin = m_pluginManager->pluginInfo(pluginName);
item->setText(PLUGIN_NAME, plugin->fullName); item->setText(PLUGIN_NAME, plugin->fullName);
item->setText(PLUGIN_URL, plugin->url); item->setText(PLUGIN_URL, plugin->url);
item->setText(PLUGIN_ID, plugin->name); item->setText(PLUGIN_ID, plugin->name);
if (plugin->enabled) { if (plugin->enabled) {
item->setText(PLUGIN_STATE, tr("Yes")); item->setText(PLUGIN_STATE, tr("Yes"));
setRowColor(pluginsTree->indexOfTopLevelItem(item), "green"); setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "green");
} }
else { else {
item->setText(PLUGIN_STATE, tr("No")); item->setText(PLUGIN_STATE, tr("No"));
setRowColor(pluginsTree->indexOfTopLevelItem(item), "red"); setRowColor(m_ui->pluginsTree->indexOfTopLevelItem(item), "red");
} }
// Handle icon // Handle icon
if (QFile::exists(plugin->iconPath)) { if (QFile::exists(plugin->iconPath)) {

View File

@@ -32,12 +32,19 @@
#ifndef PLUGINSELECTDLG_H #ifndef PLUGINSELECTDLG_H
#define PLUGINSELECTDLG_H #define PLUGINSELECTDLG_H
#include "ui_pluginselectdlg.h" #include <QDialog>
class QDropEvent; class QDropEvent;
class QTreeWidgetItem;
class SearchEngine; class SearchEngine;
class PluginSelectDlg: public QDialog, private Ui::PluginSelectDlg namespace Ui
{
class PluginSelectDlg;
}
class PluginSelectDlg: public QDialog
{ {
Q_OBJECT Q_OBJECT
@@ -82,6 +89,7 @@ private:
void startAsyncOp(); void startAsyncOp();
void finishAsyncOp(); void finishAsyncOp();
Ui::PluginSelectDlg *m_ui;
SearchEngine *m_pluginManager; SearchEngine *m_pluginManager;
int m_asyncOps; int m_asyncOps;
}; };

View File

@@ -30,14 +30,22 @@
#include "pluginsourcedlg.h" #include "pluginsourcedlg.h"
#include "ui_pluginsourcedlg.h"
PluginSourceDlg::PluginSourceDlg(QWidget *parent) PluginSourceDlg::PluginSourceDlg(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, m_ui(new Ui::PluginSourceDlg())
{ {
setupUi(this); m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
show(); show();
} }
PluginSourceDlg::~PluginSourceDlg()
{
delete m_ui;
}
void PluginSourceDlg::on_localButton_clicked() void PluginSourceDlg::on_localButton_clicked()
{ {
emit askForLocalFile(); emit askForLocalFile();

View File

@@ -32,14 +32,19 @@
#define PLUGINSOURCEDLG_H #define PLUGINSOURCEDLG_H
#include <QDialog> #include <QDialog>
#include "ui_pluginsourcedlg.h"
class PluginSourceDlg: public QDialog, private Ui::PluginSourceDlg namespace Ui
{
class PluginSourceDlg;
}
class PluginSourceDlg: public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PluginSourceDlg(QWidget *parent = 0); explicit PluginSourceDlg(QWidget *parent = 0);
~PluginSourceDlg();
signals: signals:
void askForUrl(); void askForUrl();
@@ -48,6 +53,9 @@ signals:
private slots: private slots:
void on_localButton_clicked(); void on_localButton_clicked();
void on_urlButton_clicked(); void on_urlButton_clicked();
private:
Ui::PluginSourceDlg *m_ui;
}; };
#endif // PLUGINSOURCEDLG_H #endif // PLUGINSOURCEDLG_H

View File

@@ -47,19 +47,15 @@ void SearchListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
painter->save(); painter->save();
QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option); QStyleOptionViewItem opt = QItemDelegate::setOptions(index, option);
switch(index.column()) { QItemDelegate::drawBackground(painter, opt, index);
switch (index.column()) {
case SearchSortModel::SIZE: case SearchSortModel::SIZE:
QItemDelegate::drawBackground(painter, opt, index);
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
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 SearchSortModel::SEEDS: case SearchSortModel::SEEDS:
QItemDelegate::drawBackground(painter, opt, index);
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown"));
break;
case SearchSortModel::LEECHES: case SearchSortModel::LEECHES:
QItemDelegate::drawBackground(painter, opt, index);
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown")); QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown"));
break; break;
@@ -73,5 +69,5 @@ void SearchListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
QWidget *SearchListDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const QWidget *SearchListDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const
{ {
// No editor here // No editor here
return 0; return nullptr;
} }

View File

@@ -36,10 +36,10 @@
class SearchListDelegate: public QItemDelegate class SearchListDelegate: public QItemDelegate
{ {
public: public:
explicit SearchListDelegate(QObject *parent = 0); explicit SearchListDelegate(QObject *parent);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QWidget* createEditor(QWidget*, const QStyleOptionViewItem &, const QModelIndex &) const; QWidget *createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const override;
}; };
#endif #endif

View File

@@ -29,6 +29,8 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "searchwidget.h"
#include <QHeaderView> #include <QHeaderView>
#include <QMessageBox> #include <QMessageBox>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
@@ -45,6 +47,7 @@
#include <QProcess> #include <QProcess>
#include <QDebug> #include <QDebug>
#include <QTextStream> #include <QTextStream>
#include <QTreeView>
#include <iostream> #include <iostream>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -63,18 +66,20 @@
#include "pluginselectdlg.h" #include "pluginselectdlg.h"
#include "searchsortmodel.h" #include "searchsortmodel.h"
#include "searchtab.h" #include "searchtab.h"
#include "searchwidget.h"
#include "ui_searchwidget.h"
#define SEARCHHISTORY_MAXSIZE 50 #define SEARCHHISTORY_MAXSIZE 50
#define URL_COLUMN 5 #define URL_COLUMN 5
SearchWidget::SearchWidget(MainWindow *mainWindow) SearchWidget::SearchWidget(MainWindow *mainWindow)
: QWidget(mainWindow) : QWidget(mainWindow)
, m_ui(new Ui::SearchWidget())
, m_mainWindow(mainWindow) , m_mainWindow(mainWindow)
, m_isNewQueryString(false) , m_isNewQueryString(false)
, m_noSearchResults(true) , m_noSearchResults(true)
{ {
setupUi(this); m_ui->setupUi(this);
QString searchPatternHint; QString searchPatternHint;
QTextStream stream(&searchPatternHint, QIODevice::WriteOnly); QTextStream stream(&searchPatternHint, QIODevice::WriteOnly);
@@ -92,15 +97,15 @@ SearchWidget::SearchWidget(MainWindow *mainWindow)
"Search phrase example, illustrates quotes usage, double quoted" "Search phrase example, illustrates quotes usage, double quoted"
"pair of space delimited words, the whole pair is highlighted") "pair of space delimited words, the whole pair is highlighted")
<< "</p></body></html>" << flush; << "</p></body></html>" << flush;
m_searchPattern->setToolTip(searchPatternHint); m_ui->m_searchPattern->setToolTip(searchPatternHint);
// Icons // Icons
searchButton->setIcon(GuiIconProvider::instance()->getIcon("edit-find")); m_ui->searchButton->setIcon(GuiIconProvider::instance()->getIcon("edit-find"));
downloadButton->setIcon(GuiIconProvider::instance()->getIcon("download")); m_ui->downloadButton->setIcon(GuiIconProvider::instance()->getIcon("download"));
goToDescBtn->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl")); m_ui->goToDescBtn->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl"));
pluginsButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network")); m_ui->pluginsButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network"));
copyURLBtn->setIcon(GuiIconProvider::instance()->getIcon("edit-copy")); m_ui->copyURLBtn->setIcon(GuiIconProvider::instance()->getIcon("edit-copy"));
connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); connect(m_ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
m_searchEngine = new SearchEngine; m_searchEngine = new SearchEngine;
connect(m_searchEngine, SIGNAL(searchStarted()), SLOT(searchStarted())); connect(m_searchEngine, SIGNAL(searchStarted()), SLOT(searchStarted()));
@@ -113,16 +118,16 @@ SearchWidget::SearchWidget(MainWindow *mainWindow)
fillCatCombobox(); fillCatCombobox();
fillPluginComboBox(); fillPluginComboBox();
connect(m_searchPattern, SIGNAL(returnPressed()), searchButton, SLOT(click())); connect(m_ui->m_searchPattern, SIGNAL(returnPressed()), m_ui->searchButton, SLOT(click()));
connect(m_searchPattern, SIGNAL(textEdited(QString)), this, SLOT(searchTextEdited(QString))); connect(m_ui->m_searchPattern, SIGNAL(textEdited(QString)), this, SLOT(searchTextEdited(QString)));
connect(selectPlugin, SIGNAL(currentIndexChanged(int)), this, SLOT(selectMultipleBox(int))); connect(m_ui->selectPlugin, SIGNAL(currentIndexChanged(int)), this, SLOT(selectMultipleBox(int)));
} }
void SearchWidget::fillCatCombobox() void SearchWidget::fillCatCombobox()
{ {
comboCategory->clear(); m_ui->comboCategory->clear();
comboCategory->addItem(SearchEngine::categoryFullName("all"), QVariant("all")); m_ui->comboCategory->addItem(SearchEngine::categoryFullName("all"), QVariant("all"));
comboCategory->insertSeparator(1); m_ui->comboCategory->insertSeparator(1);
using QStrPair = QPair<QString, QString>; using QStrPair = QPair<QString, QString>;
QList<QStrPair> tmpList; QList<QStrPair> tmpList;
@@ -132,17 +137,17 @@ void SearchWidget::fillCatCombobox()
foreach (const QStrPair &p, tmpList) { foreach (const QStrPair &p, tmpList) {
qDebug("Supported category: %s", qPrintable(p.second)); qDebug("Supported category: %s", qPrintable(p.second));
comboCategory->addItem(p.first, QVariant(p.second)); m_ui->comboCategory->addItem(p.first, QVariant(p.second));
} }
} }
void SearchWidget::fillPluginComboBox() void SearchWidget::fillPluginComboBox()
{ {
selectPlugin->clear(); m_ui->selectPlugin->clear();
selectPlugin->addItem(tr("Only enabled"), QVariant("enabled")); m_ui->selectPlugin->addItem(tr("Only enabled"), QVariant("enabled"));
selectPlugin->addItem(tr("All plugins"), QVariant("all")); m_ui->selectPlugin->addItem(tr("All plugins"), QVariant("all"));
selectPlugin->addItem(tr("Select..."), QVariant("multi")); m_ui->selectPlugin->addItem(tr("Select..."), QVariant("multi"));
selectPlugin->insertSeparator(3); m_ui->selectPlugin->insertSeparator(3);
using QStrPair = QPair<QString, QString>; using QStrPair = QPair<QString, QString>;
QList<QStrPair> tmpList; QList<QStrPair> tmpList;
@@ -151,23 +156,24 @@ void SearchWidget::fillPluginComboBox()
std::sort(tmpList.begin(), tmpList.end(), [](const QStrPair &l, const QStrPair &r) { return (l.first < r.first); } ); std::sort(tmpList.begin(), tmpList.end(), [](const QStrPair &l, const QStrPair &r) { return (l.first < r.first); } );
foreach (const QStrPair &p, tmpList) foreach (const QStrPair &p, tmpList)
selectPlugin->addItem(p.first, QVariant(p.second)); m_ui->selectPlugin->addItem(p.first, QVariant(p.second));
} }
QString SearchWidget::selectedCategory() const QString SearchWidget::selectedCategory() const
{ {
return comboCategory->itemData(comboCategory->currentIndex()).toString(); return m_ui->comboCategory->itemData(m_ui->comboCategory->currentIndex()).toString();
} }
QString SearchWidget::selectedPlugin() const QString SearchWidget::selectedPlugin() const
{ {
return selectPlugin->itemData(selectPlugin->currentIndex()).toString(); return m_ui->selectPlugin->itemData(m_ui->selectPlugin->currentIndex()).toString();
} }
SearchWidget::~SearchWidget() SearchWidget::~SearchWidget()
{ {
qDebug("Search destruction"); qDebug("Search destruction");
delete m_searchEngine; delete m_searchEngine;
delete m_ui;
} }
void SearchWidget::downloadTorrent(const QString &siteUrl, const QString &url) void SearchWidget::downloadTorrent(const QString &siteUrl, const QString &url)
@@ -184,16 +190,16 @@ void SearchWidget::tab_changed(int t)
//doesn't have to be available //doesn't have to be available
if (t > -1) { if (t > -1) {
//-1 = no more tab //-1 = no more tab
m_currentSearchTab = m_allTabs.at(tabWidget->currentIndex()); m_currentSearchTab = m_allTabs.at(m_ui->tabWidget->currentIndex());
if (m_currentSearchTab->getCurrentSearchListModel()->rowCount()) { if (m_currentSearchTab->getCurrentSearchListModel()->rowCount()) {
downloadButton->setEnabled(true); m_ui->downloadButton->setEnabled(true);
goToDescBtn->setEnabled(true); m_ui->goToDescBtn->setEnabled(true);
copyURLBtn->setEnabled(true); m_ui->copyURLBtn->setEnabled(true);
} }
else { else {
downloadButton->setEnabled(false); m_ui->downloadButton->setEnabled(false);
goToDescBtn->setEnabled(false); m_ui->goToDescBtn->setEnabled(false);
copyURLBtn->setEnabled(false); m_ui->copyURLBtn->setEnabled(false);
} }
} }
} }
@@ -223,18 +229,18 @@ void SearchWidget::on_pluginsButton_clicked()
void SearchWidget::searchTextEdited(QString) void SearchWidget::searchTextEdited(QString)
{ {
// Enable search button // Enable search button
searchButton->setText(tr("Search")); m_ui->searchButton->setText(tr("Search"));
m_isNewQueryString = true; m_isNewQueryString = true;
} }
void SearchWidget::giveFocusToSearchInput() void SearchWidget::giveFocusToSearchInput()
{ {
m_searchPattern->setFocus(); m_ui->m_searchPattern->setFocus();
} }
QTabWidget *SearchWidget::searchTabs() const QTabWidget *SearchWidget::searchTabs() const
{ {
return tabWidget; return m_ui->tabWidget;
} }
// Function called when we click on search button // Function called when we click on search button
@@ -249,14 +255,14 @@ void SearchWidget::on_searchButton_clicked()
m_searchEngine->cancelSearch(); m_searchEngine->cancelSearch();
if (!m_isNewQueryString) { if (!m_isNewQueryString) {
searchButton->setText(tr("Search")); m_ui->searchButton->setText(tr("Search"));
return; return;
} }
} }
m_isNewQueryString = false; m_isNewQueryString = false;
const QString pattern = m_searchPattern->text().trimmed(); const QString pattern = m_ui->m_searchPattern->text().trimmed();
// No search pattern entered // No search pattern entered
if (pattern.isEmpty()) { if (pattern.isEmpty()) {
QMessageBox::critical(0, tr("Empty search pattern"), tr("Please type a search pattern first")); QMessageBox::critical(0, tr("Empty search pattern"), tr("Please type a search pattern first"));
@@ -269,8 +275,8 @@ void SearchWidget::on_searchButton_clicked()
m_allTabs.append(m_currentSearchTab); m_allTabs.append(m_currentSearchTab);
QString tabName = pattern; QString tabName = pattern;
tabName.replace(QRegExp("&{1}"), "&&"); tabName.replace(QRegExp("&{1}"), "&&");
tabWidget->addTab(m_currentSearchTab, tabName); m_ui->tabWidget->addTab(m_currentSearchTab, tabName);
tabWidget->setCurrentWidget(m_currentSearchTab); m_ui->tabWidget->setCurrentWidget(m_currentSearchTab);
m_currentSearchTab->getCurrentSearchListProxy()->setNameFilter(pattern); m_currentSearchTab->getCurrentSearchListProxy()->setNameFilter(pattern);
QStringList plugins; QStringList plugins;
@@ -295,7 +301,7 @@ void SearchWidget::searchStarted()
{ {
// Update SearchEngine widgets // Update SearchEngine widgets
m_activeSearchTab->setStatus(SearchTab::Status::Ongoing); m_activeSearchTab->setStatus(SearchTab::Status::Ongoing);
searchButton->setText(tr("Stop")); m_ui->searchButton->setText(tr("Stop"));
} }
// Slot called when search is Finished // Slot called when search is Finished
@@ -316,7 +322,7 @@ void SearchWidget::searchFinished(bool cancelled)
m_activeSearchTab->setStatus(SearchTab::Status::Finished); m_activeSearchTab->setStatus(SearchTab::Status::Finished);
m_activeSearchTab = 0; m_activeSearchTab = 0;
searchButton->setText(tr("Search")); m_ui->searchButton->setText(tr("Search"));
} }
void SearchWidget::searchFailed() void SearchWidget::searchFailed()
@@ -363,15 +369,15 @@ void SearchWidget::appendSearchResults(const QList<SearchResult> &results)
m_activeSearchTab->updateResultsCount(); m_activeSearchTab->updateResultsCount();
// Enable clear & download buttons // Enable clear & download buttons
downloadButton->setEnabled(true); m_ui->downloadButton->setEnabled(true);
goToDescBtn->setEnabled(true); m_ui->goToDescBtn->setEnabled(true);
copyURLBtn->setEnabled(true); m_ui->copyURLBtn->setEnabled(true);
} }
void SearchWidget::closeTab(int index) void SearchWidget::closeTab(int index)
{ {
// Search is run for active tab so if user decided to close it, then stop search // Search is run for active tab so if user decided to close it, then stop search
if (!m_activeSearchTab.isNull() && index == tabWidget->indexOf(m_activeSearchTab)) { if (!m_activeSearchTab.isNull() && index == m_ui->tabWidget->indexOf(m_activeSearchTab)) {
qDebug("Closed active search Tab"); qDebug("Closed active search Tab");
if (m_searchEngine->isActive()) if (m_searchEngine->isActive())
m_searchEngine->cancelSearch(); m_searchEngine->cancelSearch();
@@ -381,9 +387,9 @@ void SearchWidget::closeTab(int index)
delete m_allTabs.takeAt(index); delete m_allTabs.takeAt(index);
if (!m_allTabs.size()) { if (!m_allTabs.size()) {
downloadButton->setEnabled(false); m_ui->downloadButton->setEnabled(false);
goToDescBtn->setEnabled(false); m_ui->goToDescBtn->setEnabled(false);
copyURLBtn->setEnabled(false); m_ui->copyURLBtn->setEnabled(false);
} }
} }
@@ -391,19 +397,21 @@ void SearchWidget::closeTab(int index)
void SearchWidget::on_downloadButton_clicked() void SearchWidget::on_downloadButton_clicked()
{ {
//QModelIndexList selectedIndexes = currentSearchTab->getCurrentTreeView()->selectionModel()->selectedIndexes(); //QModelIndexList selectedIndexes = currentSearchTab->getCurrentTreeView()->selectionModel()->selectedIndexes();
QModelIndexList selectedIndexes = m_allTabs.at(tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes =
m_allTabs.at(m_ui->tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {
if (index.column() == SearchSortModel::NAME) if (index.column() == SearchSortModel::NAME)
m_allTabs.at(tabWidget->currentIndex())->downloadItem(index); m_allTabs.at(m_ui->tabWidget->currentIndex())->downloadItem(index);
} }
} }
void SearchWidget::on_goToDescBtn_clicked() void SearchWidget::on_goToDescBtn_clicked()
{ {
QModelIndexList selectedIndexes = m_allTabs.at(tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes =
m_allTabs.at(m_ui->tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {
if (index.column() == SearchSortModel::NAME) { if (index.column() == SearchSortModel::NAME) {
QSortFilterProxyModel *model = m_allTabs.at(tabWidget->currentIndex())->getCurrentSearchListProxy(); QSortFilterProxyModel *model = m_allTabs.at(m_ui->tabWidget->currentIndex())->getCurrentSearchListProxy();
const QString descUrl = model->data(model->index(index.row(), SearchSortModel::DESC_LINK)).toString(); const QString descUrl = model->data(model->index(index.row(), SearchSortModel::DESC_LINK)).toString();
if (!descUrl.isEmpty()) if (!descUrl.isEmpty())
QDesktopServices::openUrl(QUrl::fromEncoded(descUrl.toUtf8())); QDesktopServices::openUrl(QUrl::fromEncoded(descUrl.toUtf8()));
@@ -414,11 +422,12 @@ void SearchWidget::on_goToDescBtn_clicked()
void SearchWidget::on_copyURLBtn_clicked() void SearchWidget::on_copyURLBtn_clicked()
{ {
QStringList urls; QStringList urls;
QModelIndexList selectedIndexes = m_allTabs.at(tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes =
m_allTabs.at(m_ui->tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {
if (index.column() == SearchSortModel::NAME) { if (index.column() == SearchSortModel::NAME) {
QSortFilterProxyModel *model = m_allTabs.at(tabWidget->currentIndex())->getCurrentSearchListProxy(); QSortFilterProxyModel *model = m_allTabs.at(m_ui->tabWidget->currentIndex())->getCurrentSearchListProxy();
const QString descUrl = model->data(model->index(index.row(), SearchSortModel::DESC_LINK)).toString(); const QString descUrl = model->data(model->index(index.row(), SearchSortModel::DESC_LINK)).toString();
if (!descUrl.isEmpty()) if (!descUrl.isEmpty())
urls << descUrl.toUtf8(); urls << descUrl.toUtf8();

View File

@@ -34,15 +34,21 @@
#include <QList> #include <QList>
#include <QPointer> #include <QPointer>
#include <QWidget>
#include "ui_searchwidget.h" class QTabWidget;
class MainWindow; class MainWindow;
class SearchEngine; class SearchEngine;
struct SearchResult; struct SearchResult;
class SearchTab; class SearchTab;
class SearchWidget: public QWidget, private Ui::SearchWidget namespace Ui
{
class SearchWidget;
}
class SearchWidget: public QWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(SearchWidget) Q_DISABLE_COPY(SearchWidget)
@@ -82,6 +88,7 @@ private:
QString selectedCategory() const; QString selectedCategory() const;
QString selectedPlugin() const; QString selectedPlugin() const;
Ui::SearchWidget *m_ui;
SearchEngine *m_searchEngine; SearchEngine *m_searchEngine;
QPointer<SearchTab> m_currentSearchTab; // Selected tab QPointer<SearchTab> m_currentSearchTab; // Selected tab
QPointer<SearchTab> m_activeSearchTab; // Tab with running search QPointer<SearchTab> m_activeSearchTab; // Tab with running search

View File

@@ -29,21 +29,26 @@
*/ */
#include "speedlimitdlg.h" #include "speedlimitdlg.h"
#include "ui_bandwidth_limit.h"
#include "base/unicodestrings.h" #include "base/unicodestrings.h"
SpeedLimitDialog::SpeedLimitDialog(QWidget *parent): QDialog(parent) SpeedLimitDialog::SpeedLimitDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::bandwidth_dlg())
{ {
setupUi(this); m_ui->setupUi(this);
qDebug("Bandwidth allocation dialog creation"); qDebug("Bandwidth allocation dialog creation");
// Connect to slots // Connect to slots
connect(bandwidthSlider, SIGNAL(valueChanged(int)), this, SLOT(updateSpinValue(int))); connect(m_ui->bandwidthSlider, SIGNAL(valueChanged(int)), this, SLOT(updateSpinValue(int)));
connect(spinBandwidth, SIGNAL(valueChanged(int)), this, SLOT(updateSliderValue(int))); connect(m_ui->spinBandwidth, SIGNAL(valueChanged(int)), this, SLOT(updateSliderValue(int)));
move(Utils::Misc::screenCenter(this)); move(Utils::Misc::screenCenter(this));
} }
SpeedLimitDialog::~SpeedLimitDialog() SpeedLimitDialog::~SpeedLimitDialog()
{ {
qDebug("Deleting bandwidth allocation dialog"); qDebug("Deleting bandwidth allocation dialog");
delete m_ui;
} }
// -2: if cancel // -2: if cancel
@@ -69,31 +74,31 @@ void SpeedLimitDialog::updateSpinValue(int val) const
{ {
qDebug("Called updateSpinValue with %d", val); qDebug("Called updateSpinValue with %d", val);
if (val <= 0) { if (val <= 0) {
spinBandwidth->setValue(0); m_ui->spinBandwidth->setValue(0);
spinBandwidth->setSpecialValueText(QString::fromUtf8(C_INFINITY)); m_ui->spinBandwidth->setSpecialValueText(QString::fromUtf8(C_INFINITY));
spinBandwidth->setSuffix(QString::fromUtf8("")); m_ui->spinBandwidth->setSuffix(QString::fromUtf8(""));
} }
else { else {
spinBandwidth->setValue(val); m_ui->spinBandwidth->setValue(val);
spinBandwidth->setSuffix(" " + tr("KiB/s")); m_ui->spinBandwidth->setSuffix(" " + tr("KiB/s"));
} }
} }
void SpeedLimitDialog::updateSliderValue(int val) const void SpeedLimitDialog::updateSliderValue(int val) const
{ {
if (val <= 0) { if (val <= 0) {
spinBandwidth->setValue(0); m_ui->spinBandwidth->setValue(0);
spinBandwidth->setSpecialValueText(QString::fromUtf8(C_INFINITY)); m_ui->spinBandwidth->setSpecialValueText(QString::fromUtf8(C_INFINITY));
spinBandwidth->setSuffix(QString::fromUtf8("")); m_ui->spinBandwidth->setSuffix(QString::fromUtf8(""));
} }
if (val > bandwidthSlider->maximum()) if (val > m_ui->bandwidthSlider->maximum())
bandwidthSlider->setMaximum(val); m_ui->bandwidthSlider->setMaximum(val);
bandwidthSlider->setValue(val); m_ui->bandwidthSlider->setValue(val);
} }
long SpeedLimitDialog::getSpeedLimit() const long SpeedLimitDialog::getSpeedLimit() const
{ {
long val = bandwidthSlider->value(); long val = m_ui->bandwidthSlider->value();
if (val > 0) if (val > 0)
return val; return val;
return -1; return -1;
@@ -109,7 +114,7 @@ void SpeedLimitDialog::setupDialog(long max_slider, long val) const
// than torrent rate limit. // than torrent rate limit.
if (val > max_slider) if (val > max_slider)
max_slider = val; max_slider = val;
bandwidthSlider->setMaximum(max_slider); m_ui->bandwidthSlider->setMaximum(max_slider);
bandwidthSlider->setValue(val); m_ui->bandwidthSlider->setValue(val);
updateSpinValue(val); updateSpinValue(val);
} }

View File

@@ -33,11 +33,15 @@
#include <QDialog> #include <QDialog>
#include <QList> #include <QList>
#include "ui_bandwidth_limit.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
class SpeedLimitDialog : public QDialog, private Ui_bandwidth_dlg { namespace Ui
{
class bandwidth_dlg;
}
class SpeedLimitDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit SpeedLimitDialog(QWidget *parent=0); explicit SpeedLimitDialog(QWidget *parent=0);
@@ -49,6 +53,9 @@ protected slots:
void updateSliderValue(int val) const; void updateSliderValue(int val) const;
long getSpeedLimit() const; long getSpeedLimit() const;
void setupDialog(long max_slider, long val) const; void setupDialog(long max_slider, long val) const;
private:
Ui::bandwidth_dlg *m_ui;
}; };
#endif #endif

View File

@@ -30,6 +30,7 @@
#include "statusbar.h" #include "statusbar.h"
#include <QApplication>
#include <QStatusBar> #include <QStatusBar>
#include <QFrame> #include <QFrame>
#include <QLabel> #include <QLabel>

View File

@@ -41,40 +41,41 @@
class TorrentContentModelFile; class TorrentContentModelFile;
class TorrentContentModel: public QAbstractItemModel { class TorrentContentModel: public QAbstractItemModel
Q_OBJECT {
Q_OBJECT
public: public:
TorrentContentModel(QObject *parent = 0); TorrentContentModel(QObject *parent = 0);
~TorrentContentModel(); ~TorrentContentModel();
void updateFilesProgress(const QVector<qreal> &fp); void updateFilesProgress(const QVector<qreal> &fp);
void updateFilesPriorities(const QVector<int> &fprio); void updateFilesPriorities(const QVector<int> &fprio);
QVector<int> getFilePriorities() const; QVector<int> getFilePriorities() const;
bool allFiltered() const; bool allFiltered() const;
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
TorrentContentModelItem::ItemType itemType(const QModelIndex& index) const; TorrentContentModelItem::ItemType itemType(const QModelIndex& index) const;
int getFileIndex(const QModelIndex& index); int getFileIndex(const QModelIndex& index);
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags(const QModelIndex& index) const; virtual Qt::ItemFlags flags(const QModelIndex& index) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex& index) const; virtual QModelIndex parent(const QModelIndex& index) const;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
void clear(); void clear();
void setupModelData(const BitTorrent::TorrentInfo &info); void setupModelData(const BitTorrent::TorrentInfo &info);
signals: signals:
void filteredFilesChanged(); void filteredFilesChanged();
public slots: public slots:
void selectAll(); void selectAll();
void selectNone(); void selectNone();
private: private:
TorrentContentModelFolder* m_rootItem; TorrentContentModelFolder *m_rootItem;
QVector<TorrentContentModelFile*> m_filesIndex; QVector<TorrentContentModelFile *> m_filesIndex;
}; };
#endif // TORRENTCONTENTMODEL_H #endif // TORRENTCONTENTMODEL_H

View File

@@ -32,43 +32,48 @@
#include "torrentcontentmodelfolder.h" #include "torrentcontentmodelfolder.h"
TorrentContentModelFile::TorrentContentModelFile(const QString &fileName, qulonglong fileSize, TorrentContentModelFile::TorrentContentModelFile(const QString &fileName, qulonglong fileSize,
TorrentContentModelFolder* parent, int file_index) TorrentContentModelFolder *parent, int fileIndex)
: TorrentContentModelItem(parent) : TorrentContentModelItem(parent)
, m_fileIndex(file_index) , m_fileIndex(fileIndex)
{ {
Q_ASSERT(parent); Q_ASSERT(parent);
m_name = fileName; m_name = fileName;
// Do not display incomplete extensions // Do not display incomplete extensions
if (m_name.endsWith(".!qB")) if (m_name.endsWith(".!qB"))
m_name.chop(4); m_name.chop(4);
m_size = fileSize; m_size = fileSize;
} }
int TorrentContentModelFile::fileIndex() const int TorrentContentModelFile::fileIndex() const
{ {
return m_fileIndex; return m_fileIndex;
} }
void TorrentContentModelFile::setPriority(int new_prio, bool update_parent) void TorrentContentModelFile::setPriority(int newPriority, bool updateParent)
{ {
Q_ASSERT(new_prio != prio::MIXED); Q_ASSERT(newPriority != prio::MIXED);
if (m_priority == new_prio) if (m_priority == newPriority)
return; return;
m_priority = new_prio; m_priority = newPriority;
// Update parent // Update parent
if (update_parent) if (updateParent)
m_parentItem->updatePriority(); m_parentItem->updatePriority();
} }
void TorrentContentModelFile::setProgress(qreal progress) void TorrentContentModelFile::setProgress(qreal progress)
{ {
m_progress = progress; m_progress = progress;
m_remaining = (qulonglong)(m_size * (1.0 - m_progress)); m_remaining = static_cast<qulonglong>(m_size * (1.0 - m_progress));
Q_ASSERT(m_progress <= 1.); Q_ASSERT(m_progress <= 1.);
}
TorrentContentModelItem::ItemType TorrentContentModelFile::itemType() const
{
return FileType;
} }

View File

@@ -33,19 +33,19 @@
#include "torrentcontentmodelitem.h" #include "torrentcontentmodelitem.h"
class TorrentContentModelFile : public TorrentContentModelItem class TorrentContentModelFile: public TorrentContentModelItem
{ {
public: public:
TorrentContentModelFile(const QString &fileName, qulonglong fileSize, TorrentContentModelFile(const QString &fileName, qulonglong fileSize,
TorrentContentModelFolder* parent, int file_index); TorrentContentModelFolder *parent, int fileIndex);
int fileIndex() const; int fileIndex() const;
void setPriority(int new_prio, bool update_parent = true); void setPriority(int newPriority, bool updateParent = true) override;
void setProgress(qreal progress); void setProgress(qreal progress);
ItemType itemType() const { return FileType; } ItemType itemType() const override;
private: private:
int m_fileIndex; int m_fileIndex;
}; };
#endif // TORRENTCONTENTMODELFILE_H #endif // TORRENTCONTENTMODELFILE_H

View File

@@ -31,136 +31,139 @@
#include <QDebug> #include <QDebug>
#include "torrentcontentmodelfolder.h" #include "torrentcontentmodelfolder.h"
TorrentContentModelFolder::TorrentContentModelFolder(const QString& name, TorrentContentModelFolder* parent) TorrentContentModelFolder::TorrentContentModelFolder(const QString &name, TorrentContentModelFolder *parent)
: TorrentContentModelItem(parent) : TorrentContentModelItem(parent)
{ {
Q_ASSERT(parent); Q_ASSERT(parent);
m_name = name; m_name = name;
// Do not display incomplete extensions // Do not display incomplete extensions
if (m_name.endsWith(".!qB")) if (m_name.endsWith(".!qB"))
m_name.chop(4); m_name.chop(4);
} }
TorrentContentModelFolder::TorrentContentModelFolder(const QList<QVariant>& data) TorrentContentModelFolder::TorrentContentModelFolder(const QList<QVariant> &data)
: TorrentContentModelItem(0) : TorrentContentModelItem(0)
{ {
Q_ASSERT(data.size() == NB_COL); Q_ASSERT(data.size() == NB_COL);
m_itemData = data; m_itemData = data;
} }
TorrentContentModelFolder::~TorrentContentModelFolder() TorrentContentModelFolder::~TorrentContentModelFolder()
{ {
qDeleteAll(m_childItems); qDeleteAll(m_childItems);
}
TorrentContentModelItem::ItemType TorrentContentModelFolder::itemType() const
{
return FolderType;
} }
void TorrentContentModelFolder::deleteAllChildren() void TorrentContentModelFolder::deleteAllChildren()
{ {
Q_ASSERT(isRootItem()); Q_ASSERT(isRootItem());
qDeleteAll(m_childItems); qDeleteAll(m_childItems);
m_childItems.clear(); m_childItems.clear();
} }
const QList<TorrentContentModelItem*>& TorrentContentModelFolder::children() const const QList<TorrentContentModelItem *> &TorrentContentModelFolder::children() const
{ {
return m_childItems; return m_childItems;
} }
void TorrentContentModelFolder::appendChild(TorrentContentModelItem* item) void TorrentContentModelFolder::appendChild(TorrentContentModelItem *item)
{ {
Q_ASSERT(item); Q_ASSERT(item);
m_childItems.append(item); m_childItems.append(item);
// Update own size // Update own size
if (item->itemType() == FileType) if (item->itemType() == FileType)
increaseSize(item->size()); increaseSize(item->size());
} }
TorrentContentModelItem* TorrentContentModelFolder::child(int row) const TorrentContentModelItem *TorrentContentModelFolder::child(int row) const
{ {
return m_childItems.value(row, 0); return m_childItems.value(row, 0);
} }
TorrentContentModelFolder* TorrentContentModelFolder::childFolderWithName(const QString& name) const TorrentContentModelFolder *TorrentContentModelFolder::childFolderWithName(const QString &name) const
{ {
foreach (TorrentContentModelItem* child, m_childItems) { foreach (TorrentContentModelItem *child, m_childItems)
if (child->itemType() == FolderType && child->name() == name) if ((child->itemType() == FolderType) && (child->name() == name))
return static_cast<TorrentContentModelFolder*>(child); return static_cast<TorrentContentModelFolder *>(child);
} return 0;
return 0;
} }
int TorrentContentModelFolder::childCount() const int TorrentContentModelFolder::childCount() const
{ {
return m_childItems.count(); return m_childItems.count();
} }
// Only non-root folders use this function // Only non-root folders use this function
void TorrentContentModelFolder::updatePriority() void TorrentContentModelFolder::updatePriority()
{ {
if (isRootItem()) if (isRootItem())
return; return;
Q_ASSERT(!m_childItems.isEmpty()); Q_ASSERT(!m_childItems.isEmpty());
// If all children have the same priority // If all children have the same priority
// then the folder should have the same // then the folder should have the same
// priority // priority
const int prio = m_childItems.first()->priority(); const int prio = m_childItems.first()->priority();
for (int i=1; i<m_childItems.size(); ++i) { for (int i = 1; i < m_childItems.size(); ++i) {
if (m_childItems.at(i)->priority() != prio) { if (m_childItems.at(i)->priority() != prio) {
setPriority(prio::MIXED); setPriority(prio::MIXED);
return; return;
}
} }
} // All child items have the same priority
// All child items have the same priority // Update own if necessary
// Update own if necessary setPriority(prio);
setPriority(prio);
} }
void TorrentContentModelFolder::setPriority(int new_prio, bool update_parent) void TorrentContentModelFolder::setPriority(int newPriority, bool updateParent)
{ {
if (m_priority == new_prio) if (m_priority == newPriority)
return; return;
m_priority = new_prio; m_priority = newPriority;
// Update parent priority // Update parent priority
if (update_parent) if (updateParent)
m_parentItem->updatePriority(); m_parentItem->updatePriority();
// Update children // Update children
if (m_priority != prio::MIXED) { if (m_priority != prio::MIXED)
foreach (TorrentContentModelItem* child, m_childItems) foreach (TorrentContentModelItem *child, m_childItems)
child->setPriority(m_priority, false); child->setPriority(m_priority, false);
}
} }
void TorrentContentModelFolder::recalculateProgress() void TorrentContentModelFolder::recalculateProgress()
{ {
qreal tProgress = 0; qreal tProgress = 0;
qulonglong tSize = 0; qulonglong tSize = 0;
qulonglong tRemaining = 0; qulonglong tRemaining = 0;
foreach (TorrentContentModelItem* child, m_childItems) { foreach (TorrentContentModelItem *child, m_childItems) {
if (child->priority() != prio::IGNORED) { if (child->priority() != prio::IGNORED) {
if (child->itemType() == FolderType) if (child->itemType() == FolderType)
static_cast<TorrentContentModelFolder*>(child)->recalculateProgress(); static_cast<TorrentContentModelFolder *>(child)->recalculateProgress();
tProgress += child->progress() * child->size(); tProgress += child->progress() * child->size();
tSize += child->size(); tSize += child->size();
tRemaining += child->remaining(); tRemaining += child->remaining();
}
} }
}
if (!isRootItem() && tSize > 0) { if (!isRootItem() && (tSize > 0)) {
m_progress = tProgress / tSize; m_progress = tProgress / tSize;
m_remaining = tRemaining; m_remaining = tRemaining;
Q_ASSERT(m_progress <= 1.); Q_ASSERT(m_progress <= 1.);
} }
} }
void TorrentContentModelFolder::increaseSize(qulonglong delta) void TorrentContentModelFolder::increaseSize(qulonglong delta)
{ {
if (isRootItem()) if (isRootItem())
return; return;
m_size += delta; m_size += delta;
m_parentItem->increaseSize(delta); m_parentItem->increaseSize(delta);
} }

View File

@@ -33,34 +33,34 @@
#include "torrentcontentmodelitem.h" #include "torrentcontentmodelitem.h"
class TorrentContentModelFolder : public TorrentContentModelItem class TorrentContentModelFolder: public TorrentContentModelItem
{ {
public: public:
// Folder constructor // Folder constructor
TorrentContentModelFolder(const QString& name, TorrentContentModelFolder* parent); TorrentContentModelFolder(const QString& name, TorrentContentModelFolder* parent);
// Invisible root item constructor // Invisible root item constructor
TorrentContentModelFolder(const QList<QVariant>& data); TorrentContentModelFolder(const QList<QVariant>& data);
~TorrentContentModelFolder(); ~TorrentContentModelFolder();
ItemType itemType() const { return FolderType; } ItemType itemType() const override;
void increaseSize(qulonglong delta); void increaseSize(qulonglong delta);
void recalculateProgress(); void recalculateProgress();
void updatePriority(); void updatePriority();
void setPriority(int new_prio, bool update_parent = true); void setPriority(int newPriority, bool updateParent = true) override;
void deleteAllChildren(); void deleteAllChildren();
const QList<TorrentContentModelItem*>& children() const; const QList<TorrentContentModelItem*>& children() const;
void appendChild(TorrentContentModelItem* item); void appendChild(TorrentContentModelItem* item);
TorrentContentModelItem* child(int row) const; TorrentContentModelItem* child(int row) const;
TorrentContentModelFolder* childFolderWithName(const QString& name) const; TorrentContentModelFolder* childFolderWithName(const QString& name) const;
int childCount() const; int childCount() const;
private: private:
QList<TorrentContentModelItem*> m_childItems; QList<TorrentContentModelItem*> m_childItems;
}; };
#endif // TORRENTCONTENTMODELFOLDER_H #endif // TORRENTCONTENTMODELFOLDER_H

View File

@@ -34,36 +34,39 @@
#include "torrentcontentmodelfolder.h" #include "torrentcontentmodelfolder.h"
#include <QDebug> #include <QDebug>
TorrentContentModelItem::TorrentContentModelItem(TorrentContentModelFolder* parent) TorrentContentModelItem::TorrentContentModelItem(TorrentContentModelFolder *parent)
: m_parentItem(parent) : m_parentItem(parent)
, m_size(0) , m_size(0)
, m_remaining(0) , m_remaining(0)
, m_priority(prio::NORMAL) , m_priority(prio::NORMAL)
, m_progress(0) , m_progress(0)
{ {
} }
TorrentContentModelItem::~TorrentContentModelItem() TorrentContentModelItem::~TorrentContentModelItem() = default;
bool TorrentContentModelItem::isRootItem() const
{ {
return !m_parentItem;
} }
QString TorrentContentModelItem::name() const QString TorrentContentModelItem::name() const
{ {
Q_ASSERT(!isRootItem()); Q_ASSERT(!isRootItem());
return m_name; return m_name;
} }
void TorrentContentModelItem::setName(const QString& name) void TorrentContentModelItem::setName(const QString &name)
{ {
Q_ASSERT(!isRootItem()); Q_ASSERT(!isRootItem());
m_name = name; m_name = name;
} }
qulonglong TorrentContentModelItem::size() const qulonglong TorrentContentModelItem::size() const
{ {
Q_ASSERT(!isRootItem()); Q_ASSERT(!isRootItem());
return m_size; return m_size;
} }
qreal TorrentContentModelItem::progress() const qreal TorrentContentModelItem::progress() const
@@ -83,45 +86,45 @@ qulonglong TorrentContentModelItem::remaining() const
int TorrentContentModelItem::priority() const int TorrentContentModelItem::priority() const
{ {
Q_ASSERT(!isRootItem()); Q_ASSERT(!isRootItem());
return m_priority; return m_priority;
} }
int TorrentContentModelItem::columnCount() const int TorrentContentModelItem::columnCount() const
{ {
return NB_COL; return NB_COL;
} }
QVariant TorrentContentModelItem::data(int column) const QVariant TorrentContentModelItem::data(int column) const
{ {
if (isRootItem()) if (isRootItem())
return m_itemData.value(column); return m_itemData.value(column);
switch(column) { switch (column) {
case COL_NAME: case COL_NAME:
return m_name; return m_name;
case COL_PRIO: case COL_PRIO:
return m_priority; return m_priority;
case COL_PROGRESS: case COL_PROGRESS:
return progress(); return progress();
case COL_SIZE: case COL_SIZE:
return m_size; return m_size;
case COL_REMAINING: case COL_REMAINING:
return remaining(); return remaining();
default: default:
Q_ASSERT(false); Q_ASSERT(false);
return QVariant(); return QVariant();
} }
} }
int TorrentContentModelItem::row() const int TorrentContentModelItem::row() const
{ {
if (m_parentItem) if (m_parentItem)
return m_parentItem->children().indexOf(const_cast<TorrentContentModelItem*>(this)); return m_parentItem->children().indexOf(const_cast<TorrentContentModelItem *>(this));
return 0; return 0;
} }
TorrentContentModelFolder* TorrentContentModelItem::parent() const TorrentContentModelFolder *TorrentContentModelItem::parent() const
{ {
return m_parentItem; return m_parentItem;
} }

View File

@@ -34,48 +34,70 @@
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
namespace prio { namespace prio
enum FilePriority {IGNORED=0, NORMAL=1, HIGH=6, MAXIMUM=7, MIXED=-1}; {
enum FilePriority
{
IGNORED=0,
NORMAL=1,
HIGH=6,
MAXIMUM=7,
MIXED=-1
};
} }
class TorrentContentModelFolder; class TorrentContentModelFolder;
class TorrentContentModelItem { class TorrentContentModelItem
{
public: public:
enum TreeItemColumns {COL_NAME, COL_SIZE, COL_PROGRESS, COL_PRIO, COL_REMAINING, NB_COL}; enum TreeItemColumns
enum ItemType { FileType, FolderType }; {
COL_NAME,
COL_SIZE,
COL_PROGRESS,
COL_PRIO,
COL_REMAINING,
NB_COL
};
TorrentContentModelItem(TorrentContentModelFolder* parent); enum ItemType
virtual ~TorrentContentModelItem(); {
FileType,
FolderType
};
inline bool isRootItem() const { return !m_parentItem; } TorrentContentModelItem(TorrentContentModelFolder *parent);
TorrentContentModelFolder* parent() const; virtual ~TorrentContentModelItem();
virtual ItemType itemType() const = 0;
QString name() const; bool isRootItem() const;
void setName(const QString& name); TorrentContentModelFolder *parent() const;
virtual ItemType itemType() const = 0;
qulonglong size() const; QString name() const;
qreal progress() const; void setName(const QString &name);
qulonglong remaining() const;
int priority() const; qulonglong size() const;
virtual void setPriority(int new_prio, bool update_parent = true) = 0; qreal progress() const;
qulonglong remaining() const;
int columnCount() const; int priority() const;
QVariant data(int column) const; virtual void setPriority(int newPriority, bool updateParent = true) = 0;
int row() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;
protected: protected:
TorrentContentModelFolder* m_parentItem; TorrentContentModelFolder *m_parentItem;
// Root item members // Root item members
QList<QVariant> m_itemData; QList<QVariant> m_itemData;
// Non-root item members // Non-root item members
QString m_name; QString m_name;
qulonglong m_size; qulonglong m_size;
qulonglong m_remaining; qulonglong m_remaining;
int m_priority; int m_priority;
qreal m_progress; qreal m_progress;
}; };
#endif // TORRENTCONTENTMODELITEM_H #endif // TORRENTCONTENTMODELITEM_H

View File

@@ -74,6 +74,7 @@ void TransferListSortModel::disableTrackerFilter()
bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
switch (sortColumn()) { switch (sortColumn()) {
case TorrentModel::TR_CATEGORY:
case TorrentModel::TR_NAME: { case TorrentModel::TR_NAME: {
QVariant vL = left.data(); QVariant vL = left.data();
QVariant vR = right.data(); QVariant vR = right.data();

View File

@@ -5,14 +5,12 @@
<file>searchengine/nova/nova2.py</file> <file>searchengine/nova/nova2.py</file>
<file>searchengine/nova/novaprinter.py</file> <file>searchengine/nova/novaprinter.py</file>
<file>searchengine/nova/socks.py</file> <file>searchengine/nova/socks.py</file>
<file>searchengine/nova/engines/btdb.png</file>
<file>searchengine/nova/engines/btdb.py</file>
<file>searchengine/nova/engines/demonoid.png</file> <file>searchengine/nova/engines/demonoid.png</file>
<file>searchengine/nova/engines/demonoid.py</file> <file>searchengine/nova/engines/demonoid.py</file>
<file>searchengine/nova/engines/extratorrent.png</file>
<file>searchengine/nova/engines/extratorrent.py</file>
<file>searchengine/nova/engines/legittorrents.png</file> <file>searchengine/nova/engines/legittorrents.png</file>
<file>searchengine/nova/engines/legittorrents.py</file> <file>searchengine/nova/engines/legittorrents.py</file>
<file>searchengine/nova/engines/mininova.png</file>
<file>searchengine/nova/engines/mininova.py</file>
<file>searchengine/nova/engines/piratebay.png</file> <file>searchengine/nova/engines/piratebay.png</file>
<file>searchengine/nova/engines/piratebay.py</file> <file>searchengine/nova/engines/piratebay.py</file>
<file>searchengine/nova/engines/torlock.png</file> <file>searchengine/nova/engines/torlock.png</file>
@@ -24,14 +22,12 @@
<file>searchengine/nova3/novaprinter.py</file> <file>searchengine/nova3/novaprinter.py</file>
<file>searchengine/nova3/sgmllib3.py</file> <file>searchengine/nova3/sgmllib3.py</file>
<file>searchengine/nova3/socks.py</file> <file>searchengine/nova3/socks.py</file>
<file>searchengine/nova3/engines/btdb.png</file>
<file>searchengine/nova3/engines/btdb.py</file>
<file>searchengine/nova3/engines/demonoid.png</file> <file>searchengine/nova3/engines/demonoid.png</file>
<file>searchengine/nova3/engines/demonoid.py</file> <file>searchengine/nova3/engines/demonoid.py</file>
<file>searchengine/nova3/engines/extratorrent.png</file>
<file>searchengine/nova3/engines/extratorrent.py</file>
<file>searchengine/nova3/engines/legittorrents.png</file> <file>searchengine/nova3/engines/legittorrents.png</file>
<file>searchengine/nova3/engines/legittorrents.py</file> <file>searchengine/nova3/engines/legittorrents.py</file>
<file>searchengine/nova3/engines/mininova.png</file>
<file>searchengine/nova3/engines/mininova.py</file>
<file>searchengine/nova3/engines/piratebay.png</file> <file>searchengine/nova3/engines/piratebay.png</file>
<file>searchengine/nova3/engines/piratebay.py</file> <file>searchengine/nova3/engines/piratebay.py</file>
<file>searchengine/nova3/engines/torlock.png</file> <file>searchengine/nova3/engines/torlock.png</file>

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