Compare commits

..

134 Commits

Author SHA1 Message Date
sledgehammer999
0795320086 Bump to 3.3.4 2016-03-29 23:21:35 +03:00
sledgehammer999
bd6877a0bd Update Changelog. 2016-03-29 23:19:29 +03:00
sledgehammer999
b47292c39f Bump API_VERSION and API_VERSION_MIN. 2016-03-29 23:17:30 +03:00
sledgehammer999
3d2b1b876b Fix public key commit again. 2016-03-28 22:20:15 +03:00
sledgehammer999
843472e663 Fix newlines in previous commit. 2016-03-28 22:20:15 +03:00
sledgehammer999
e952e3167e Add public key used for signing source tarballs and binaries. 2016-03-28 22:20:14 +03:00
sledgehammer999
67f05edf71 Sync translations from Transifex and run lupdate. 2016-03-28 22:19:37 +03:00
Eugene Shalygin
3ab76cb985 cmake: make it compile on OS X 2016-03-28 21:30:14 +03:00
Eugene Shalygin
509d97b0ad cmake: fix compilation 2016-03-28 21:30:13 +03:00
Chocobo1
67ae08df3c Minor cleanups in RSS 2016-03-28 21:30:13 +03:00
Chocobo1
38de6b1e41 Remove border around execution log 2016-03-28 21:30:13 +03:00
ngosang
72b179805d [Search engine] Added TorLock search engine. Fix #5012 2016-03-28 21:30:13 +03:00
ngosang
beeda5e0b2 Potential fix for crash. Closes #4990, #4905, #4864, #4855, #4818, #4726, #4648 2016-03-28 00:14:55 +03:00
sledgehammer999
356db2f2f7 Fix periodic latency spikes on Windows with WiFi connections. Closes #4209. 2016-03-27 23:57:29 +03:00
Chocobo1
19acbf587f Beautify messages
And some refactor
2016-03-27 23:57:23 +03:00
Chocobo1
5eee3f7357 confirmshutdowndlg.ui cleanup
shutdownconfirm.cpp cleanup
2016-03-27 23:57:18 +03:00
sledgehammer999
8f990d5d7e Fix previous commit. 2016-03-27 23:57:12 +03:00
d3fault
f0ec94c31c Add 'never show again' checkbox/pref to auto-exit confirm dialog 2016-03-27 23:57:06 +03:00
sledgehammer999
e37dfa96f9 Fix commit about Esperanto. Closes #4999 again. 2016-03-27 23:57:01 +03:00
Chocobo1
4b48db3273 Fix "caja" file manager opens the file instead of opens the directory. Closes #5003.
Now it opens the directory correctly.
2016-03-27 23:56:56 +03:00
ngosang
545002a809 [Search engine] Added TorLock search engine 2016-03-27 23:56:50 +03:00
ngosang
e2e9470e10 [Search engine] Change URL getting mechanism in BTDigg 2016-03-27 23:56:45 +03:00
ngosang
0bf1abba6a [Search engine] Fix Python 2 encoding problems 2016-03-27 23:56:39 +03:00
Vladimir Golovnev (Glassez)
24165856e9 Add new Coding Style rules for some c++11 features 2016-03-27 23:56:34 +03:00
sledgehammer999
657f0640b9 Fix selection of Esperanto locale. Closes #4999. 2016-03-27 23:56:28 +03:00
sledgehammer999
faffefc4ff Implement file logger. 2016-03-27 23:56:23 +03:00
sledgehammer999
0619aacf1f Add ability to filter log messages by type. 2016-03-27 23:56:17 +03:00
sledgehammer999
9edbbb6473 Minor log widget code optimizations. 2016-03-27 23:56:12 +03:00
sledgehammer999
87ee720c0c Follow project coding style. Issue #2192. 2016-03-27 23:56:06 +03:00
ngosang
b7ca036bc3 [WebUI] Add missing string 2016-03-27 23:56:01 +03:00
Eugene Shalygin
c4442c98b9 update uncrustify config 2016-03-27 23:55:55 +03:00
Chocobo1
fee8036a7a TravisCI: switch to Trusty image. Closes #4953.
Drop workaround for gcc-4.8
2016-03-27 23:55:50 +03:00
sledgehammer999
94bd4308c7 Fix selection of Portuguese translation files.
Qt returns "Brazil" as country of QLocale("pt") instead of "Portugal". This conflicts with QLocale("pt_BR").
Rename the pt.ts to pt_PT and put a language mapping in tx's config for that locale.
Closes #4776.
2016-03-27 23:55:45 +03:00
sledgehammer999
5e5785435a Potential fix for crash. Closes #4607. 2016-03-27 23:55:39 +03:00
sledgehammer999
d7f02a7ee7 Don't display warning when folder named stayed the same.
Copy some code from AddNewTorrent dialog and beautify a bit.
Closes #4970.
2016-03-27 23:55:34 +03:00
sledgehammer999
e6480f9dff Correctly hide useless columns in AddNewTorrent file treeview. Closes #4955. 2016-03-27 23:55:28 +03:00
buinsky
e9f6cfc2e8 WebUI: Change the order of the values of speed labels 2016-03-27 23:55:23 +03:00
buinsky
4f68d263d4 GUI: Change the order of the values of speed labels 2016-03-27 23:55:17 +03:00
buinsky
6a672472a2 Refactoring of StatusBar::refreshStatusBar function 2016-03-27 23:55:12 +03:00
buinsky
3590ac2997 Follow project coding style. Issue #2192. 2016-03-27 23:55:06 +03:00
Chocobo1
98fe5e11dd Add appveyor support 2016-03-27 23:55:01 +03:00
sledgehammer999
9df5c0292b Sync translations from Transifex and run lupdate. 2016-03-27 23:54:55 +03:00
funkydude
1f2d25a1ff Move some URLs overs to https 2016-03-27 23:54:50 +03:00
sledgehammer999
f1dd7a091c Always delete partfile (libtorrent 1.1.x). 2016-03-27 23:54:44 +03:00
Eugene Shalygin
5457bde8d0 Use correct piece size while calling torrent_info::map_block()
This should fix crashes. Issue #4597
2016-03-27 23:54:39 +03:00
Eugene Shalygin
2e325d9506 Revert "Temporarily revert PR #2885 (filename column in peers view)."
This reverts commit 69d52a06d7.
2016-03-27 23:54:34 +03:00
sledgehammer999
0264a7bf58 Don't display Country option in PeerListWidget header menu when country resolution is disabled. 2016-03-27 23:54:29 +03:00
thalieht
32fe930b88 add toggle columns for peerlist 2016-03-27 23:54:23 +03:00
Chocobo1
72883ffb73 Fix additional space in comment label 2016-03-27 23:54:13 +03:00
Chocobo1
f2c24dd8c3 Fix tab icons messed up in Ubuntu 16. Closes #4929.
Fixup of 1c25603686.
The following settings default are different between DEs, so they must set explicitly:
movement, flow, isWarpping, resizeMode, viewMode
2016-03-27 23:19:35 +03:00
Eugene Shalygin
8904139c6d cmake: fix library namaes for Qt5 version of QtSingleApplication
At least on Gentoo they start with 'Qt5', not 'Qt'
2016-03-27 23:16:27 +03:00
Eugene Shalygin
571f46886f cmake: copy qt version requirements from autotools
Set versions 4.8.0 and 5.2.0 as minimal required for Qt4 and Qt5.
2016-03-27 23:16:15 +03:00
Eugene Shalygin
0cd691e167 cmake: fix man installation
Fix copy-n-paste error: for non-GUI build we have to install
qbittorrent-nox.1, but not qbittorrent.1.
2016-03-27 23:15:51 +03:00
UnDifferential
4f65e2d468 Support SSL certificate bundles. Issue #4896. 2016-03-27 23:15:31 +03:00
Eugene Shalygin
35981f6ef5 cmake: fix systemd unit install path 2016-03-27 23:15:14 +03:00
sledgehammer999
dc493880f3 Fix commit 83e931766f (wrong conflict resolution). 2016-03-05 19:50:12 +02:00
Ben Lau
872e78ca21 add a remaining column to the torrent content model 2016-03-05 19:46:21 +02:00
Eugene Shalygin
7b601796d7 fix cmake build 2016-03-05 19:46:13 +02:00
Chocobo1
09ef552aea Add tip about adding quotation marks around parameter 2016-03-05 19:46:07 +02:00
Chocobo1
b4c9cae0d1 Save "Run external program" input as is. Closes #4830. 2016-03-05 19:43:31 +02:00
Eugene Shalygin
6d2a0ae83b add basic cmake support 2016-03-05 19:43:22 +02:00
Chocobo1
238a925000 Put comment_lbl in QScrollArea. Closes #4881. 2016-03-05 19:43:14 +02:00
Eugene Shalygin
cce01cfb8e Fix typos in speed label text
Qt's tr() ate 'true' parameter, which was supposed to be for
friendlyUnit()
2016-03-05 19:43:07 +02:00
Chocobo1
b2db1972f3 Create helper function to get libtorrent version 2016-03-05 19:42:59 +02:00
Chocobo1
8b851fe2b9 Create helper function to get boost version 2016-03-05 19:42:51 +02:00
Chocobo1
e3c9488fb0 Create helper function to get OS name 2016-03-05 19:42:43 +02:00
Chocobo1
c27fb110f8 Comment out "List of linked Modules" section in windows crash report.
It is bloating the crash report with unhelpful messages.
2016-03-05 19:42:34 +02:00
Dan Seminara
ec61f24099 Do not try to parse request message when content-length is 0 2016-03-05 19:42:27 +02:00
Boris Nagaev
130ee5a71e configure: pass variable QMAKE_LRELEASE to qmake 2016-03-05 19:42:20 +02:00
Boris Nagaev
cb3e7e6bd6 winconf-mingw.pri: clean library names
* remove versions from libraries,
  * remove *.dll from libraries
    (actual name of library files can be "libfoo.a")
2016-03-05 19:42:12 +02:00
Boris Nagaev
7fd65d5428 winconf.pri: change example library path for build
Path with "<", ">" causes errors when building in MXE.
http://mxe.cc/

Removing or commenting out these hardcoded paths is not
desirable, as they serve as a guide (on what to edit) for
the newcomers that want to build on windows.

See https://github.com/qbittorrent/qBittorrent/pull/4824#issuecomment-186936960
2016-03-05 19:42:05 +02:00
Boris Nagaev
442f521bf5 winconf.pri: use mask "win32-g++*"
Mask "win32-g++" doesn't match MXE.
Mask "win32-g++*" match MXE.
See http://stackoverflow.com/a/14523545
See http://mxe.cc
2016-03-05 19:41:57 +02:00
Douman
016052aea1 Align search engine url getting mechanism. Closes #4778
1. Switch to retrieve_url instead of low-level HTTPConnection module usage
2016-03-05 19:41:48 +02:00
Chocobo1
411982e2b0 Follow project coding style. Issue #2192. 2016-03-05 19:41:20 +02:00
Chocobo1
4b93ccd4e4 Simplify statement 2016-03-05 19:41:20 +02:00
Chocobo1
6603a8947a Fix malformed date header in email. Closes #4828. 2016-03-05 19:41:20 +02:00
sledgehammer999
031e354577 Partially revert decfae7b8a. Newer libtorrent RC_1_1 has added back the missing variables. 2016-03-05 19:41:19 +02:00
Chocobo1
ec7fb331e0 Remove additional vertical spacer.
Fixup of 843f7ed.
2016-03-05 19:41:19 +02:00
Chocobo1
a232b77104 Simplify resize actions for AddNewTorrentDialog 2016-03-05 19:41:19 +02:00
Chocobo1
8c11245469 Disable cell selection in advanced options. Fixup of a8b39475. 2016-03-05 19:41:18 +02:00
Chocobo1
60857d3b8e Put links into table 2016-03-05 19:41:18 +02:00
Chocobo1
2fe6b76968 Set icon directly in .ui file 2016-03-05 19:41:18 +02:00
Chocobo1
6c7350fce0 Make authors email clickable & selectable 2016-03-05 19:41:17 +02:00
Chocobo1
c770f4d0bc about_imp.h: Use larger qbt icon
Shrink title size
2016-03-05 19:41:17 +02:00
Chocobo1
876e96911f Fix weird left panel icon layout
Remove Advanced page border
2016-03-05 19:41:17 +02:00
Chocobo1
5620fd120e Let OS handle DPI scaling for now. Should let Qt do the work when it's more mature.
Opt-in to the high DPI pixmap support
Closes #2963.
2016-03-05 19:41:17 +02:00
Chocobo1
ea7f6046b4 Follow project coding style. Issue #2192. 2016-03-05 19:41:16 +02:00
Chocobo1
fddac5d679 Move statusbar styelsheet to where it belongs 2016-03-05 19:41:16 +02:00
Chocobo1
2c4bc68af1 Use short date in addnewtorrentdialog
Correction "Free disk space" to "Free space on disk"
2016-03-05 19:41:16 +02:00
Chocobo1
7676f49612 Cleanup addnewtorrentdialog.ui
Reorder Torrent info column
Rename "Save as" to "Save at"
2016-03-05 19:41:15 +02:00
Chocobo1
e879279019 Cleanup about.ui
Shorten text in "Libraries" tab, to avoid About dialog being too wide
Make QLabels in Libraries tab selectable
Use QGroupBox in "Author" tabs
Rename "Thanks to" to "Special Thanks"
Rename "Translation" to "Translators"
Remove borders in "Thanks to", "Translation", "License" tabs
2016-03-05 19:41:15 +02:00
Chocobo1
84b7680718 Cleanup about_imp.h
Fix dialog too narrow on highDPI screens
Remove IRC link
Rewrite about text to support RTL languages better
Follow project coding style. Issue #2192.
2016-03-05 19:41:15 +02:00
sledgehammer999
be180140a3 Don't merge trackers for private torrents. Closes #2928. 2016-03-05 19:41:14 +02:00
Chocobo1
c051c279d4 Set "Show splash screen on start up" option default to off 2016-03-05 19:41:14 +02:00
Chocobo1
964dcc4d8a Fix potential race condition. Closes #4742. 2016-03-05 19:41:14 +02:00
Vladimir Golovnev (Glassez)
64cf93b889 Fix crash caused by AdvancedSettings::addRow() 2016-03-05 19:41:13 +02:00
Vladimir Golovnev (Glassez)
365737afe1 Save resume data using QSaveFile (Qt5 only) 2016-03-05 19:41:13 +02:00
Vladimir Golovnev (qlassez)
2cf14f0120 Optimize Session::startupTorrents()
Reduce queue size by starting up initial items (torrents) when they are detected
2016-03-05 19:41:13 +02:00
Vladimir Golovnev (qlassez)
46bb25ba9f Switch to using previous fastresume file names 2016-03-05 19:41:12 +02:00
Vladimir Golovnev (qlassez)
ee5a72c570 Perform fastresume data saving in separate thread
Closes #4315
2016-03-05 19:41:12 +02:00
Chocobo1
18b56f4d0a Add "Paused torrents only" option for "Hide zero and infinity values" 2016-03-05 19:41:12 +02:00
Chocobo1
f626276218 Fix total values for "Seeds" & "Peers" 2016-03-05 19:41:11 +02:00
Chocobo1
e28554f85c Simplify common paint actions 2016-03-05 19:41:11 +02:00
Chocobo1
a0a3447b2e Remove unused destructor
move get state text to its own function
2016-03-05 19:41:11 +02:00
Chocobo1
4049ca7308 Follow project coding style. Issue #2192. 2016-03-05 19:41:11 +02:00
Chocobo1
c28151ba92 Add "Hide zero values" option. Closes #3543. 2016-03-05 19:41:10 +02:00
buinsky
81e1a050a2 WebAPI: Add "Added on" and "Completion on" fields to query/torrents query response 2016-03-05 19:41:10 +02:00
Vladimir Golovnev (Glassez)
34d5824c4a Manage save path in one place 2016-03-05 19:41:10 +02:00
Vladimir Golovnev (Glassez)
bb875df400 Create MagnetUri object from BC link or HASH string 2016-03-05 19:41:09 +02:00
ngosang
61f47d366a [Web UI] Minor changes in CSS styles 2016-03-05 19:41:09 +02:00
ngosang
8347eb157d Check WebUI username and password length. Closes #4191 2016-03-05 19:41:09 +02:00
Vladimir Golovnev (Glassez)
dd22c9b138 Fix upgrade corrupted fastresume file 2016-03-05 19:41:08 +02:00
thalieht
7f6ad55042 Unlock first column in peerlist too
I just copy pasted from trackerlist (tested ofc). No idea if it can be
done differently.
2016-03-05 19:41:08 +02:00
Vladimir Golovnev (Glassez)
55b06ab9ba Add libtorrent v1.1 basic support 2016-03-05 19:41:08 +02:00
Vladimir Golovnev (Glassez)
21f0a5eb76 Prepare for libtorrent v1.1 support 2016-03-05 19:41:07 +02:00
Vladimir Golovnev (Glassez)
16ed11623f Don't use wide strings in libtorrent 2016-03-05 19:41:07 +02:00
buinsky
c184cf8d7d WebUI: Submit the label in the new label dialog on pressing enter key 2016-03-05 19:41:07 +02:00
buinsky
6a90214eb2 WebUI: Repair translation 2016-03-05 19:41:06 +02:00
buinsky
226ec0610a WebUI: Fix JavaScript exception on WebUI load 2016-03-05 19:41:06 +02:00
ngosang
694bd7cb95 Download more pieces in "Download first and last pieces first" feature 2016-03-05 19:41:06 +02:00
ngosang
9e807e7151 [search engine] Update PirateBay plugin. 2016-03-05 19:41:06 +02:00
ngosang
78fe7fcf9d [Web UI] Fix max_ratio precision. Closes #4707 2016-03-05 19:41:05 +02:00
Chocobo1
c2465f931e Add #include guard 2016-03-05 19:41:05 +02:00
Chocobo1
8d50325961 Try to concat most of the string at compile time
The lesser unsafe code in signal handler the better
Add license
2016-03-05 19:41:05 +02:00
Chocobo1
570a651a59 Simplify signal handler
Try to use signal-safe functions as much as possible
Closes #3995
Define sys_signame[] ourselves on linux
2016-03-05 19:41:04 +02:00
sledgehammer999
0eaa2aeef2 Fix loading *.magnet files from watched folders. Closes #4701. 2016-03-05 19:41:04 +02:00
Chocobo1
2c7e309493 Add header to differentiate settings 2016-03-05 19:41:04 +02:00
Chocobo1
ded3cf5798 Rename setRow() to addRow()
Use template to avoid code duplication
2016-03-05 19:41:03 +02:00
Chocobo1
4edac3e974 Set parent object for advancedsettings
Remove borders
Resize column size correctly
2016-03-05 19:41:03 +02:00
Chocobo1
53885fb5e4 Move code from advancedsettings.h to its own cpp file 2016-03-05 19:41:03 +02:00
Chocobo1
3942c095f6 Add license in advancedsettings.h 2016-03-05 19:41:02 +02:00
Jesse Connop
94be3b930d Always update native session's announce_ip setting 2016-03-05 19:41:02 +02:00
Artem S. Tashkinov
09bc14cc57 Let's check if the torrent file can actually be opened before passing it to libtorrent
If we don't have enough permissions, libtorrent will spew a pretty useless, irrelevant and almost wrong message: "Failed to load the torrent: torrent file is not a dictionary"
2016-03-05 19:41:02 +02:00
sledgehammer999
51b93b4284 Fix splash screen staying on top of all windows. Closes #1391. 2016-03-05 19:41:01 +02:00
211 changed files with 67393 additions and 49526 deletions

View File

@@ -14,18 +14,10 @@ env:
- lt_branch=RC_1_0 qt=5 gui=false
- lt_branch=RC_1_0 qt=4 gui=true
- lt_branch=RC_1_0 qt=4 gui=false
global:
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
- coverity_branch: coverity_scan
matrix:
exclude:
- os: linux
env: lt_branch=RC_1_0 qt=5 gui=true
- os: linux
env: lt_branch=RC_1_0 qt=5 gui=false
branches:
except:
- search_encoding_windows
@@ -37,87 +29,116 @@ notifications:
on_failure: change
# container-based builds
sudo: false
#sudo: false
# TODO: osx builder does not enable cache yet, see: https://github.com/travis-ci/travis-ci/issues/4011
cache:
directories:
- $HOME/.ccache
#cache:
#directories:
#- $HOME/.ccache
# opt-in Ubuntu Trusty
sudo: required
dist: trusty
addons:
coverity_scan:
project:
name: "qbittorrent/qBittorrent"
description: "Build submitted via Travis CI"
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf && echo QMAKE_CC=$CC >> conf.pri && echo QMAKE_CXX=$CXX >> conf.pri"
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf"
build_command: make
branch_pattern: $coverity_branch
notification_email: sledgehammer999@qbittorrent.org
apt:
sources:
#sources:
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
- ubuntu-toolchain-r-test
- boost-latest
#- ubuntu-toolchain-r-test
#- boost-latest
packages:
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
- autoconf
- automake
- colormake
- g++-4.8
- libssl-dev
- libboost1.55-dev
- libboost-system1.55-dev
- libqt4-dev
# Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
- libboost-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-rasterbar6
before_install:
# Only allow specific build for coverity scan, others will stop
# only allow specific build for coverity scan, others will stop
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" = "RC_1_0" -a "$gui" = true ]; then exit ; fi
- shopt -s expand_aliases
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then export CC=gcc-4.8 CXX=g++-4.8 ; fi
- if [ "$TRAVIS_BRANCH" != "$coverity_branch" -a "$TRAVIS_OS_NAME" = "linux" ]; then dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" ; fi
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- libt_path="$HOME/libt_install"
#- libt_path="$HOME/libt_install"
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
- qbt_path="$HOME/qbt_install"
- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
- qbtconf="$qbtconf --prefix="$qbt_path" --with-qt4 PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
# Options for specific branches
# Also setup a virtual display for after_success target when gui == true
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ;
elif [ "$TRAVIS_OS_NAME" = "linux" ]; 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 ;
# options for specific branches
- if [ "$qt" = 4 ]; then qbtconf="$qbtconf --with-qt4" ; fi
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# ccache
#if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
#dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" && export use_ccache=true ;
#ccache -V && ccache --show-stats && ccache --zero-stats ;
#fi ;
# 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 ;
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
qbtconf="$qbtconf --disable-qt-dbus" ;
fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then qbtconf="$qbtconf --disable-qt-dbus" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 4 ]; then brew install qt; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 5 ]; then qbtconf="$qbtconf --with-qt4=no" ; fi
# Print settings
# print settings
- echo $lt_branch
- echo $gui
- echo $ltconf
- echo $qbtconf
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache -V && ccache --show-stats && ccache --zero-stats ; fi
install:
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd libtorrent && ./autotool.sh && ./configure $ltconf && make install && cd "$TRAVIS_BUILD_DIR" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null && brew install colormake libtorrent-rasterbar ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 ; fi
- |
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
#if [ "$lt_branch" != "dist" ]; then
#cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ;
#cd libtorrent && ./autotool.sh && ./configure $ltconf && make install ;
#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 ;
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# dependencies
brew update > /dev/null && brew install colormake libtorrent-rasterbar ;
# Qt
if [ "$qt" = 4 ]; then brew install qt ; fi ;
if [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 ; fi ;
fi
script:
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # Skip usual build when running coverity scan
- ./bootstrap.sh && ./configure $qbtconf
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then echo "QMAKE_CC=$CC" >> conf.pri && echo "QMAKE_CXX=$CXX" >> conf.pri ; fi
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # skip usual build when running coverity scan
- cd "$TRAVIS_BUILD_DIR" && ./bootstrap.sh && ./configure $qbtconf
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs/' src/Makefile ; fi
- make && make install
after_success:
- if [ "$gui" = true ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" && export LD_PRELOAD="$libt_path/lib/libtorrent-rasterbar.so:$LD_PRELOAD" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "src/$qbt_exe.app/Contents/MacOS" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS" ; fi
- ./$qbt_exe --version
after_script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache --show-stats ; fi
- if [ "$use_ccache" = true ]; then ccache --show-stats ; fi

View File

@@ -3,6 +3,7 @@ host = https://www.transifex.com
[qbittorrent.qbittorrent_v3_3_x]
file_filter = src/lang/qbittorrent_<lang>.ts
lang_map = pt: pt_PT
source_file = src/lang/qbittorrent_en.ts
source_lang = en
type = QT

53
520EC6F6.asc Normal file
View File

@@ -0,0 +1,53 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQINBFb4b5EBEADTUsyDNPWdujfhx4ncy52MNdkw6EI8W7kxWypHCOr0EAcr9Xaq
lGbstcu0v//f5E5Wvi7gNK7DJkgky4L1GKIufe61e9HXLI8Ekju0j1ojkgR8m4md
BhIkQNB897xItKCYCtnHm/51dKxYDtSCdJ4a9RbfKyH6YqvM8R+s4jD472mvekHm
lMeek+Tv85Thx2IR6NpUUaJZOUskhMpuhZDjLzI78ZWlnielxoGKysMke7iAekiL
2scQYRmC/IrHIgf/mmaAvwJf82a3GqYRfC49RFBXaPAAGCJzu3WWqTrmLe9yRdB4
HpQ6d8D28HpWfnLTfR9bkBHdy6/8dche4wCNbBXkvjoqMUTA7hgzPmJFW0GE8Omp
SnFEj+8WVLCkBTp1zEdiYUzD30Itn/4YnzEQdExOnQRrKo5E9vLbdom8VXWrmO7v
jXU4u8o9FDbYcMOmje2LFW1UQhP3pfMeIA/nKRfJivgz+76jtg3OU53tKKeHWHfa
z88Mn+9QmBbC6l7/d308K9wlERwsv6uMFNrD1mIRIPW3Bvzgzc/nR/IfmIhizg+C
vJvm1eAOnRTnMZAggW0dK65tclu3hL198IySvEcZ7TRdC6Wzqbzg+SgIn/JiWoKz
dVAuGn4TD0D1R9SlGoGMFQUvHZAPsPCQHjyPsGwtxiqFJSvVE10id2lwSQARAQAB
tG5zbGVkZ2VoYW1tZXI5OTkgKFVzZWQgZm9yIHNpZ25pbmcgcUJpdHRvcnJlbnQg
c291cmNlIHRhcmJhbGxzIGFuZCBiaW5hcmllcy4pIDxzbGVkZ2VoYW1tZXI5OTlA
cWJpdHRvcnJlbnQub3JnPokCNwQTAQgAIQUCVvhvkQIbAwULCQgHAgYVCAkKCwIE
FgIDAQIeAQIXgAAKCRChrMrkUg7G9vW/EAC/A9yt5F3fW4yvLz3ZGPmWUQw1ah6B
o8c8khCh9JATCTwoV+1ZAxEKMfFW8AQ52MwX34S45Fi1Ik860yD5Ea7HMg2dpq/1
sZ1dK9LHVlSXHUTZi8dVUntxryz1hR5JS7UUvGtclHrjNTB1jkbiPN2LrcURg+K7
WSJ+jaX9Wt6hzGwuAe4QqXQF1DRHK945hOe2by1VmV7IXtz4xXUIzVdbUI/fFuaV
26ImAHKLuNRHO0DxGZX6f56T+zG+tEaERiHls8w7I3HIKAjTEHQwytB10tq/IRpK
Rg1uDAutOIHe2Gm9XxOX5wwFiEGB0Dq2TxLjMZ42n/PaNk0JOvJkbmkM/NtInvOx
xk807cpIgUf6CrBgavQ1DxSKL+OqowTQ3aNT1Cg8VI8yf0hOwXU/CyRe6o55+T8M
d+FZk2eILJEeyJ1O7GdW8L7QK6vVIep/eFmuXkXE8kpnud9X84Fzh1mK7dzblWnS
2SUB6vXQhnsIgGfp1maYLYVDK08BdSh4eg6kNVcyx/6/t8PUZjO5tDNhKflyddt/
vRDxET8nULIKEct7g3X4w87klj9hxZzGz6zyv8JBdJYuwxfsZJ58M/vGZOrmdllb
T9tC5AtcvaKLDulvkLzHvIgVCyk29fzGAJNb7pqoZP4oht8StTBlo+pVqfeE7m6/
u45vAoX6l1sVlbkCDQRW+G+RARAAsR1sFdq8cUZUbYAiwP1ERdzKfhZx4qQCqUZs
D1/fka7jttqAd2rCHWPtFmy2KTZVcNeWq9+9zG3jvykpapXhZ+r2/H13NE2FrTy8
AcuQgCaXfnD6fR8cifJwSYeEDRZ6vJEuIv2Vn/ZJVkhM+M72LliNfkh/E+VIlybX
OQm1sazCUAd+EMUT6/uEitJy3n2JlDK2ctkpO06pb647nC3dgtyU0aKto8ol3da3
eWLzkoOzq9IQTHZ7x89ptVO+I8vR0itSV0Clt0Ab7AL3jwI8hZUvx5q3YmZrRrZy
fmdhG61jyvaD9eM0dPZcmoLMEv23KaLHRY1+Mwf5a8kfzRgYQLimEsWt3NljmhQd
04Fm1eTXBJ30TMSnJoIa0W1MQ36J7TFYxE44ySCL6uB2woHiqq6ydznbjtyTaHF7
Fx2K2vZhHQT+V1B22XzuwBoVYqH4Q+Zw7f9yqDd1NU7+SwcUtapqqHM+OERRvR2s
ddsttVMIC5e75MlhvRCvskm6yCxFYKnZLTIRW6W/xYfksXRwupk3TICRN9fmiDxx
uZZRMurpV365kYowHUW2uY/dQTLazKCC1/folsmMIhqumhijkK81/vYSFPk3NmXi
v6HljMCd0vmzJRdwzWgaLoFoazErmPiGkmQSup6BjJjE0CHldpvmLaGkWkbP7zWu
VeYV79EAEQEAAYkCHwQYAQgACQUCVvhvkQIbDAAKCRChrMrkUg7G9hSQD/9WcAID
FPTt/pcrVIGZ8/1EVrhuVlyC0UNKnZuCnbggr1UKs67ivhkv6lXBi4sS1VZoKUQZ
xII+VnPzQGDDYUfkwExOSkZBqN0Tm3Ly1/xgAr46V4F6vZPs8D+fpvmXaHs4CrhA
LVgWN3kYOtd5a5z0tomVLOKlh7iip0UBIx+j2CV2lqowL/OG7AXcq6iSbH4gEr7z
G4wbPnRrvSqZ5oM0nGhXgYGG8HE4jaaRG6TBdWL6YcLKZmtueSIOGQAmqNwT3Vnw
2kDfk+KNHQPTTZQ9KMUE/4c6mcoYIDDPo9POlf6ShvxipdLGKUFUz+MAkv5Oci4/
I3RMnDdgfAzsppFlQ5IN3laipTcViPu9SLIzXcf1TMi4f9x3Lqm4r9x6KcLX8uC1
ncOHqrqBnI5mm45EhJTahyFEGd+eTvsOSThdUEgkdRSln72cymh+iisdGSEMXinw
nS/6WOYnW9a6s1J90ql7P/qA3sA7RA1AHRL5tKdYVM+2OFU1asLWRbdLVmQR2dgM
3+0u3HBjkKd3/8iGcP1CfLvur9eh4FHkVYKIepz5ALBGdUQbc96p+Le4VFedvxDR
Wz/9MJI/oK6ij90b6LF2eR47oTyYRGzaeMk4WjmHCqlK/mUABng9n+U3H2OzihYy
x8m1+aYiYlKfcKWm1nKQG0hF04Axq5AYezjKvA==
=Zdwm
-----END PGP PUBLIC KEY BLOCK-----

65
CMakeLists.txt Normal file
View File

@@ -0,0 +1,65 @@
cmake_minimum_required(VERSION 3.2)
cmake_policy(VERSION 3.2)
project(qBittorrent VERSION 3.4.0.0)
set(VER_MAJOR ${qBittorrent_VERSION_MAJOR})
set(VER_MINOR ${qBittorrent_VERSION_MINOR})
set(VER_BUGFIX ${qBittorrent_VERSION_PATCH})
set(VER_BUILD ${qBittorrent_VERSION_TWEAK})
set(VER_STATUS "alpha") # Should be empty for stable releases!
# Don't touch the rest part
set(PROJECT_VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUGFIX}")
if (NOT VER_BUILD EQUAL 0)
set(PROJECT_VERSION "${PROJECT_VERSION}.${VER_BUILD}")
endif()
set(PROJECT_VERSION "${PROJECT_VERSION}${VER_STATUS}")
add_definitions(-DVERSION_MAJOR=${VER_MAJOR})
add_definitions(-DVERSION_MINOR=${VER_MINOR})
add_definitions(-DVERSION_BUGFIX=${VER_BUGFIX})
add_definitions(-DVERSION_BUILD=${VER_BUILD})
# os2 {
# DEFINES += VERSION=\'\"v$${PROJECT_VERSION}\"\'
# } else {
add_definitions(-DVERSION="v${PROJECT_VERSION}")
# }
list(APPEND CMAKE_MODULE_PATH ${qBittorrent_SOURCE_DIR}/cmake/Modules)
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og")
if (UNIX AND NOT APPLE)
include(GNUInstallDirs)
endif (UNIX AND NOT APPLE)
if(WIN32)
include(winconf)
endif(WIN32)
# we need options here, because they are used not only in "src" subdir, but in the "dist" dir too
include(CMakeDependentOption)
option(QT5 "Compile using Qt5" ON)
option(SYSTEM_QTSINGLEAPPLICATION
"Use the system qtsingleapplication library or shipped one otherwise")
cmake_dependent_option(SYSTEM_QJSON
"Use the shipped qjson library or the system one (Qt4 only)" OFF "NOT QT5" OFF)
option(GUI "Allows to disable GUI for headless running. Disables QtDBus and the GeoIP Database" ON)
option(WEBUI "Allows to disable the WebUI." ON)
if (WIN32)
option(STACKTRACE_WIN "")
else (WIN32)
cmake_dependent_option(SYSTEMD "Install the systemd service file (headless only)" OFF
"NOT GUI" OFF)
cmake_dependent_option(DBUS "Enable use of QtDBus (GUI only)" ON "GUI" OFF)
endif(WIN32)
add_subdirectory(src)
add_subdirectory(dist)

View File

@@ -211,6 +211,67 @@ a += "b"
+ "d";
```
* Initializers
We allow brace enclosed initializers only for aggregates and arrays/containers.<br />
Brace enclosed initializer MUST be used with equality sign if it follows the variable declaration.<br />
Brace enclosed initializer MUST be additionally enclosed in parentheses if it is used in constructor initialization list.<br />
Some valid use cases:
```c++
// aggregate
Person john = { "John", "Smith", 21 };
Person *john = new Person { "John", "Smith", 21 };
// array
int array[] = { 1, 2, 3, 4 };
// container
QHash<QString, QString> map = {
{ "key1", "value1" },
{ "key2", "value2" }
);
// member array
SomeClass::SomeClass(BaseClass *parent)
: BaseClass(parent)
, m_someArrayMember({ 1, 2, 3, 4 })
{
}
// return from function
Person getPersonByName(const QString &name)
{
// do something
return { name, surname, age };
}
// function argument
doSomething({ name, surname, age }, someOtherArg);
```
* **auto** keyword
We allow the use of the **auto** keyword only where it doesn't break the readability of the code (i.e. either we can gather enough information about the type from the right part of the expression, or we do not need to know the exact type), or where it is strictly necessary (for example, to compute the type of a lambda, etc.).<br />
Some valid use cases:
```c++
template <typename List>
void doSomethingWithList(const List &list)
{
foreach (const auto &item, list) {
// we don't know item type here so we use 'auto' keyword
// do something with item
}
}
for (auto it = container.begin(), end = container.end(); it != end; ++it) {
// we don't need to know the exact iterator type,
// because all iterators have the same interface
}
auto spinBox = static_cast<QSpinBox*>(sender());
// we know the variable type based on the right-hand expression
```
* Space around operations eg `a = b + c` or `a=b+c`:
Before and after the assignment there should be a space. One exception could be: for loops.
@@ -225,5 +286,5 @@ for (int a=0; a<b; ++b) {
* Method definitions aren't allowed in header files
###8. Not covered above###
### 9. Not covered above###
If something isn't covered above, just follow the same style the file you are editing has. If that particular detail isn't present in the file you are editing, then use whatever the rest of the project uses.

View File

@@ -1,3 +1,64 @@
* Tue Mar 29 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.4
- FEATURE: Download more pieces in "Download first and last pieces first" feature (ngosang)
- FEATURE: Unlock first column in peerlist too (thalieht)
- FEATURE: Add "Hide zero values" option. Closes #3543. (Chocobo1)
- FEATURE: Add a "remaining" column to the torrent content model (Ben Lau)
- FEATURE: Allow to toggle columns in peerlist (thalieht)
- FEATURE: Add ability to filter log messages by type. (sledgehammer999)
- FEATURE: Add ability to write the log to file. (sledgehammer999)
- FEATURE: Add 'never show again' checkbox/pref to auto-exit confirm dialog (d3fault, sledgehammer999, Chocobo1)
- PERFORMANCE: Perform fastresume data saving in separate thread (glassez)
- PERFORMANCE: Optimize session startup (glassez)
- BUGFIX: Save resume data using QSaveFile (Qt5 only). This reduces chances of corrupt files. (glassez)
- BUGFIX: Check torrent file permissions before opening (birdie-github)
- BUGFIX: Always update native session's announce_ip setting (Jesse Connop)
- BUGFIX: Fix loading *.magnet files from watched folders. Closes #4701. (sledgehammer999)
- BUGFIX: Fix upgrade corrupted fastresume file (glassez)
- BUGFIX: Fix total values for "Seeds" & "Peers" (Chocobo1)
- BUGFIX: Fix potential race condition. Closes #4742. (Chocobo1)
- BUGFIX: Don't merge trackers for private torrents. Closes #2928. (sledgehammer999)
- BUGFIX: Fix double buttons in "Add New Torrent" dialog. (Chocobo1)
- BUGFIX: Fix malformed date header in email. Closes #4828. (Chocobo1)
- BUGFIX: Save "Run external program" input as is. Closes #4830. (Chocobo1)
- BUGFIX: Enable "filename" column in peers list again. Crash is fixed now. (Eugene Shalygin)
- BUGFIX: Don't display warning when folder name stayed the same after rename. (sledgehammer999)
- BUGFIX: Fix selection of Portuguese translation files. (sledgehammer999)
- BUGFIX: Fix selection of Esperanto locale. Closes #4999. (sledgehammer999)
- BUGFIX: Fix "caja" file manager opens the file instead of opens the directory. Closes #5003. (Chocobo1)
- BUGFIX: Fix periodic latency spikes on Windows with WiFi connections. Closes #4209. (sledgehammer999)
- BUGFIX: Potentially fix a random crash coming from the sidepanel (sledgehammer999, ngosang)
- WEBUI: Fix max_ratio precision. Closes #4707 (ngosang)
- WEBUI: Fix JavaScript exception on WebUI load (buinsky)
- WEBUI: Fix translation (buinsky)
- WEBUI: Submit the label in the new label dialog on pressing enter key (buinsky)
- WEBUI: Check WebUI username and password length. Closes #4191 (ngosang)
- WEBUI: Minor changes in CSS styles (ngosang)
- WEBUI: Add "Added on" and "Completion on" fields to query/torrents query response (buinsky)
- WEBUI: Do not try to parse request message when content-length is 0 (Dan Seminara)
- WEBUI: Support SSL certificate bundles. Issue #4896. (UnDifferential)
- WEBUI: Change the order of the values of speed labels (buinsky)
- WEBUI: Bump WebUI API_VERSION
- SEARCH: Update PirateBay plugin. (ngosang)
- SEARCH: Added TorLock search engine (ngosang)
- COSMETIC: Fix splash screen staying on top of all windows. Closes #1391. (sledgehammer999)
- COSMETIC: Rearrange advanced settings (Chocobo1)
- COSMETIC: Cleanup "about" dialog (Chocobo1)
- COSMETIC: Cleanup "Add New Torrent" dialog (Chocobo1)
- COSMETIC: Use short date in addnewtorrentdialog (Chocobo1)
- COSMETIC: Change "Free disk space" to "Free space on disk" (Chocobo1)
- COSMETIC: Let OS handle DPI scaling for now. Should let Qt do the work when it's more mature. (Chocobo1)
- COSMETIC: Put comment_lbl in QScrollArea. Closes #4881. (Chocobo1)
- COSMETIC: Change the order of the values of speed labels (buinsky)
- COSMETIC: Cleanup the Log tab (Chocobo1)
- COSMETIC: Cleanup the RSS tab (Chocobo1)
- OTHER: Set "Show splash screen on start up" option default to off (Chocobo1)
- OTHER: Support for cross-compilation with MXE (Boris Nagaev)
- OTHER: Add basic (and unofficial) cmake support (Eugene Shalygin)
- OTHER: Move some URLs overs to https (funkydude)
- OTHER: Add appveyor support (Chocobo1)
- OTHER: TravisCI: switch to Trusty image. Closes #4953. (Chocobo1)
- OTHER: Many other internal code restructuring, cleaning and fixing.
* Thu Jan 21 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.3
- BUGFIX: Temporarily disable "filename" column of peers view. It has a bug that causes frequent crashes. See issue #4597.
- WEBUI: Move style of dynamic table header to CSS (buinsky)

View File

@@ -1,7 +1,8 @@
qBittorrent - A BitTorrent client in Qt
------------------------------------------
[![Build Status](https://travis-ci.org/qbittorrent/qBittorrent.svg?branch=master)](https://travis-ci.org/qbittorrent/qBittorrent)
[![TravisCI Status](https://travis-ci.org/qbittorrent/qBittorrent.svg?branch=master)](https://travis-ci.org/qbittorrent/qBittorrent)
[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/qbittorrent/qBittorrent?branch=master&svg=true)](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
[![Coverity Status](https://scan.coverity.com/projects/5494/badge.svg)](https://scan.coverity.com/projects/5494)
********************************
### Description:
@@ -13,7 +14,7 @@ out there. qBittorrent is fast, stable and provides unicode
support as well as many features.
This product includes GeoLite data created by MaxMind, available from
http://maxmind.com/
https://www.maxmind.com/
### Installation:
For installation, follow the instructions from INSTALL file, but simple:
@@ -26,6 +27,12 @@ qbittorrent
will install and execute qBittorrent hopefully without any problem.
### Public key:
Starting from v3.3.4 all source tarballs and binaries are signed.<br />
The key currently used is 4096R/[520EC6F6](https://pgp.mit.edu/pks/lookup?op=get&search=0xA1ACCAE4520EC6F6) with fingerprint `F4A5FD201B117B1C2AB590E2A1ACCAE4520EC6F6`.<br />
You can also download it from [here](https://github.com/qbittorrent/qBittorrent/raw/master/520EC6F6.asc).<br />
### Misc:
For more information please visit:
http://www.qbittorrent.org

52
appveyor.yml Normal file
View File

@@ -0,0 +1,52 @@
version: '{branch}-{build}'
# Do not build on tags (GitHub only)
skip_tags: true
os: Visual Studio 2015
environment:
REPO_DIR: &REPO_DIR c:\qbittorrent
CACHE_DIR: &CACHE_DIR c:\qbt_cache
QBT_VER_URL: http://builds.shiki.hu/appveyor/version
QBT_LIB_URL: http://builds.shiki.hu/appveyor/qbt_libraries.7z
# project directory
clone_folder: *REPO_DIR
# cache size should < 100MB (after compressing with fastest option):
# see: https://www.appveyor.com/docs/build-cache#save-update-cache-before-build-finishes
cache:
- *CACHE_DIR
install:
# check if library needs update
- appveyor DownloadFile "%QBT_VER_URL%" -FileName "c:\version_new" && SET /P newVersion=<"c:\version_new"
- IF EXIST "%CACHE_DIR%\version" (SET /P oldVersion=<"%CACHE_DIR%\version")
- IF NOT EXIST "%CACHE_DIR%\version" (SET updateCache=1)
- IF NOT "%oldVersion%" == "%newVersion%" (SET updateCache=1)
# update library
- IF "%updateCache%" == "1" (ECHO "--- Will redownload libraries ---" &&
RMDIR /S /Q "%CACHE_DIR%" & MKDIR "%CACHE_DIR%" &&
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
COPY "c:\version_new" "%CACHE_DIR%\version")
# Qt stay compressed in cache
- 7z x "%CACHE_DIR%\qt5_32.7z" -o"c:\qbt" > nul
before_build:
# setup env
- CALL "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
# setup project
- COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%"
- COPY /Y "%CACHE_DIR%\winconf-msvc.pri" "%REPO_DIR%"
# workarounds
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
build_script:
- cd "%REPO_DIR%"
- qmake qbittorrent.pro && cd src && qmake src.pro
- jom -j2 -f Makefile.Release
test: off

View File

@@ -0,0 +1,93 @@
# - Try to find libtorrent-rasterbar
#
# If not using pkg-config, you can pre-set LibtorrentRasterbar_CUSTOM_DEFINITIONS
# for definitions unrelated to Boost's separate compilation (which are already
# decided by the LibtorrentRasterbar_USE_STATIC_LIBS variable).
#
# Once done this will define
# LibtorrentRasterbar_FOUND - System has libtorrent-rasterbar
# LibtorrentRasterbar_INCLUDE_DIRS - The libtorrent-rasterbar include directories
# LibtorrentRasterbar_LIBRARIES - The libraries needed to use libtorrent-rasterbar
# LibtorrentRasterbar_DEFINITIONS - Compiler switches required for using libtorrent-rasterbar
# LibtorrentRasterbar_OPENSSL_ENABLED - libtorrent-rasterbar uses and links against OpenSSL
find_package(Threads REQUIRED)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_LIBTORRENT_RASTERBAR QUIET libtorrent-rasterbar)
endif()
if(LibtorrentRasterbar_USE_STATIC_LIBS)
set(LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
if(PC_LIBTORRENT_RASTERBAR_FOUND)
set(LibtorrentRasterbar_DEFINITIONS ${PC_LIBTORRENT_RASTERBAR_CFLAGS})
else()
if(LibtorrentRasterbar_CUSTOM_DEFINITIONS)
set(LibtorrentRasterbar_DEFINITIONS ${LibtorrentRasterbar_CUSTOM_DEFINITIONS})
else()
# Without pkg-config, we can't possibly figure out the correct build flags.
# libtorrent is very picky about those. Let's take a set of defaults and
# hope that they apply. If not, you the user are on your own.
set(LibtorrentRasterbar_DEFINITIONS
-DTORRENT_USE_OPENSSL
-DTORRENT_DISABLE_GEO_IP
-DBOOST_ASIO_ENABLE_CANCELIO
-DUNICODE -D_UNICODE -D_FILE_OFFSET_BITS=64)
endif()
if(NOT LibtorrentRasterbar_USE_STATIC_LIBS)
list(APPEND LibtorrentRasterbar_DEFINITIONS
-DTORRENT_LINKING_SHARED
-DBOOST_SYSTEM_DYN_LINK -DBOOST_CHRONO_DYN_LINK)
endif()
endif()
message(STATUS "libtorrent definitions: ${LibtorrentRasterbar_DEFINITIONS}")
find_path(LibtorrentRasterbar_INCLUDE_DIR libtorrent
HINTS ${PC_LIBTORRENT_RASTERBAR_INCLUDEDIR} ${PC_LIBTORRENT_RASTERBAR_INCLUDE_DIRS}
PATH_SUFFIXES libtorrent-rasterbar)
find_library(LibtorrentRasterbar_LIBRARY NAMES torrent-rasterbar libtorrent
HINTS ${PC_LIBTORRENT_RASTERBAR_LIBDIR} ${PC_LIBTORRENT_RASTERBAR_LIBRARY_DIRS})
if(LibtorrentRasterbar_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
endif()
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIR})
if(NOT Boost_SYSTEM_FOUND OR NOT Boost_CHRONO_FOUND OR NOT Boost_RANDOM_FOUND)
find_package(Boost REQUIRED COMPONENTS date_time system chrono random thread)
set(LibtorrentRasterbar_LIBRARIES
${LibtorrentRasterbar_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
set(LibtorrentRasterbar_INCLUDE_DIRS
${LibtorrentRasterbar_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
endif()
list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRasterbar_ENCRYPTION_INDEX)
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
find_package(OpenSSL REQUIRED)
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} ${OPENSSL_LIBRARIES})
set(LibtorrentRasterbar_INCLUDE_DIRS ${LibtorrentRasterbar_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS})
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LibtorrentRasterbar_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(LibtorrentRasterbar DEFAULT_MSG
LibtorrentRasterbar_LIBRARY
LibtorrentRasterbar_INCLUDE_DIR
Boost_SYSTEM_FOUND
Boost_CHRONO_FOUND
Boost_RANDOM_FOUND)
mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
LibtorrentRasterbar_ENCRYPTION_INDEX)

View File

@@ -0,0 +1,81 @@
# - Try to find the QtSingleApplication includes and library
# which defines
#
# QTSINGLEAPPLICATION_FOUND - system has QtSingleApplication
# QTSINGLEAPPLICATION_INCLUDE_DIR - where to find header QtSingleApplication
# QTSINGLEAPPLICATION_LIBRARIES - the libraries to link against to use QtSingleApplication
# QTSINGLEAPPLICATION_LIBRARY - where to find the QtSingleApplication library (not for general use)
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
#
# Redistribution and use is allowed according to the terms of the FreeBSD license.
SET(QTSINGLEAPPLICATION_FOUND FALSE)
IF(QT4_FOUND)
message(STATUS "Looking for Qt4 single application library")
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication
# standard locations
/usr/include
/usr/include/QtSolutions
# qt4 location except mac's frameworks
"${QT_INCLUDE_DIR}/QtSolutions"
# mac's frameworks
${FRAMEWORK_INCLUDE_DIR}/QtSolutions
)
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
NAMES ${QTSINGLEAPPLICATION_NAMES}
PATHS ${QT_LIBRARY_DIR}
)
ELSEIF(Qt5Widgets_FOUND)
message(STATUS "Looking for Qt5 single application library")
FOREACH(TOP_INCLUDE_PATH in ${Qt5Widgets_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication ${TOP_INCLUDE_PATH}/QtSolutions)
IF(QTSINGLEAPPLICATION_INCLUDE_DIR)
BREAK()
ENDIF()
ENDFOREACH()
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
GET_TARGET_PROPERTY(QT5_WIDGETSLIBRARY Qt5::Widgets LOCATION)
GET_FILENAME_COMPONENT(QT5_WIDGETSLIBRARYPATH ${QT5_WIDGETSLIBRARY} PATH)
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
NAMES ${QTSINGLEAPPLICATION_NAMES}
PATHS ${QT5_WIDGETSLIBRARYPATH}
)
ENDIF()
IF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
SET(QTSINGLEAPPLICATION_LIBRARIES ${QTSINGLEAPPLICATION_LIBRARY})
SET(QTSINGLEAPPLICATION_FOUND TRUE)
IF (CYGWIN)
IF(BUILD_SHARED_LIBS)
# No need to define QTSINGLEAPPLICATION_USE_DLL here, because it's default for Cygwin.
ELSE(BUILD_SHARED_LIBS)
SET (QTSINGLEAPPLICATION_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
ENDIF(BUILD_SHARED_LIBS)
ENDIF (CYGWIN)
ENDIF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
IF (QTSINGLEAPPLICATION_FOUND)
IF (NOT QtSingleApplication_FIND_QUIETLY)
MESSAGE(STATUS "Found QtSingleApplication: ${QTSINGLEAPPLICATION_LIBRARY}")
MESSAGE(STATUS " includes: ${QTSINGLEAPPLICATION_INCLUDE_DIR}")
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
ELSE (QTSINGLEAPPLICATION_FOUND)
IF (QtSingleApplication_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
ENDIF (QtSingleApplication_FIND_REQUIRED)
ENDIF (QTSINGLEAPPLICATION_FOUND)
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)

View File

@@ -0,0 +1,23 @@
#######
# Find systemd service dir
# sets variables
# SYSTEMD_FOUND
# SYSTEMD_SERVICES_INSTALL_DIR
if (NOT SYSTEMD_FOUND)
pkg_check_modules(SYSTEMD "systemd")
endif(NOT SYSTEMD_FOUND)
if (SYSTEMD_FOUND AND "${SYSTEMD_SERVICES_INSTALL_DIR}" STREQUAL "")
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
--variable=systemdsystemunitdir systemd
OUTPUT_VARIABLE SYSTEMD_SERVICES_INSTALL_DIR)
string(REGEX REPLACE "[ \t\n]+" "" SYSTEMD_SERVICES_INSTALL_DIR
"${SYSTEMD_SERVICES_INSTALL_DIR}")
elseif (NOT SYSTEMD_FOUND AND SYSTEMD_SERVICES_INSTALL_DIR)
message (FATAL_ERROR "Variable SYSTEMD_SERVICES_INSTALL_DIR is\
defined, but we can't find systemd using pkg-config")
endif()
if (SYSTEMD_FOUND)
message(STATUS "systemd services install dir: ${SYSTEMD_SERVICES_INSTALL_DIR}")
endif(SYSTEMD_FOUND)

View File

@@ -0,0 +1,38 @@
macro(configure_msvc_runtime)
if(MSVC)
# Default to statically-linked runtime.
if("${MSVC_RUNTIME}" STREQUAL "")
set(MSVC_RUNTIME "static")
endif()
# Set compiler options.
set(variables
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELWITHDEBINFO
)
if(${MSVC_RUNTIME} STREQUAL "static")
message(STATUS
"MSVC -> forcing use of statically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
endif()
endforeach()
else()
message(STATUS
"MSVC -> forcing use of dynamically-linked runtime."
)
foreach(variable ${variables})
if(${variable} MATCHES "/MT")
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
endif()
endforeach()
endif()
endif()
endmacro()

View File

@@ -0,0 +1,16 @@
# - macro similar to target_link_libraries, which links Qt components
# names of the components are pased in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
# and the macro links Qt4 ones if QT4_FOUND is set or Qt5 ones if not
macro (target_link_qt_components target)
if (QT4_FOUND)
foreach(_cmp ${ARGN})
list(APPEND _QT_CMPNTS "Qt4::Qt${_cmp}")
endforeach()
else (QT4_FOUND)
foreach(_cmp ${ARGN})
list(APPEND _QT_CMPNTS "Qt5::${_cmp}")
endforeach()
endif (QT4_FOUND)
target_link_libraries(${target} ${_QT_CMPNTS})
endmacro()

View File

@@ -0,0 +1,14 @@
if (STACKTRACE_WIN)
if ("${WINXXBITS}" NOT STREQUAL "Win64")
add_compile_options(-fno-omit-frame-pointer)
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
link_libraries(libdbghelp -Wl,--export-all-symbols)
endif (STACKTRACE_WIN)
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
link_libraries(-Wl,--dynamicbase)
endif()
# LIBS += libadvapi32 libshell32 libuser32
# LIBS += libcrypto.dll libssl.dll libwsock32 libws2_32 libz libiconv.dll
# LIBS += libpowrprof

View File

@@ -0,0 +1,21 @@
if (STACKTRACE_WIN)
if ("${WINXXBITS}" STREQUAL "Win64")
add_compile_options(-Zi)
else ("${WINXXBITS}" STREQUAL "Win64")
# i686 arch requires frame pointer preservation
add_compile_options(-Oy-)
endif ("${WINXXBITS}" STREQUAL "Win64")
link_libraries(dbghelp.lib)
endif (STACKTRACE_WIN)
# Enable Wide characters
add_definitions(-DTORRENT_USE_WPATH)
if (NOT QT5)
# Qt4 does not detect it itself
add_definitions(-DQ_COMPILER_INITIALIZER_LISTS)
endif (NOT QT5)
include(MacroConfigureMSVCRuntime)
set(MSVC_RUNTIME "dynamic")
configure_msvc_runtime()

View File

@@ -0,0 +1,86 @@
# Settings for compiling qBittorrent on Windows
# We want to link with static version of
# libtorrent
set(LibtorrentRasterbar_USE_STATIC_LIBS True)
set(LibtorrentRasterbar_CUSTOM_DEFINITIONS
-DBOOST_ALL_NO_LIB -DBOOST_ASIO_HASH_MAP_BUCKETS=1021
-DBOOST_ASIO_SEPARATE_COMPILATION
-DBOOST_EXCEPTION_DISABLE
-DBOOST_SYSTEM_STATIC_LINK=1
-DTORRENT_USE_OPENSSL
-DUNICODE
-D_UNICODE
-DWIN32
-D_WIN32
-DWIN32_LEAN_AND_MEAN
-D_WIN32_WINNT=0x0501
-D_WIN32_IE=0x0500
-D_CRT_SECURE_NO_DEPRECATE
-D_SCL_SECURE_NO_DEPRECATE
-D__USE_W32_SOCKETS
-D_FILE_OFFSET_BITS=64)
# and boost
set(Boost_USE_STATIC_LIBS True)
# set(Boost_USE_STATIC_RUNTIME True)
# Here we assume that all required libraries are installed into the same prefix
# with usual unix subdirectories (bin, lib, include)
# if so, we just need to set CMAKE_SYSTEM_PREFIX_PATH
# If it is not the case, individual paths need to be specified manually (see below)
set(COMMON_INSTALL_PREFIX "c:/usr")
list(APPEND CMAKE_SYSTEM_PREFIX_PATH "${COMMON_INSTALL_PREFIX}")
# If two version of Qt are installed, separate prefixes are needed most likely
set(QT4_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt4")
set(QT5_INSTALL_PREFIX "${COMMON_INSTALL_PREFIX}/lib/qt5")
# it is safe to set Qt dirs even if their files are directly in the prefix
# Qt4
if(NOT QT5)
LIST(APPEND CMAKE_PROGRAM_PATH "${QT4_INSTALL_PREFIX}/bin/")
endif(NOT QT5)
# Qt5
set(Qt5_DIR "${QT5_INSTALL_PREFIX}/lib/cmake/Qt5")
# And now we can set specific values for the Boost and libtorrent libraries.
# The following values are generated from the paths listed above just for an example
# they have to be set to actual locations
# Boost
# set(BOOST_ROOT "${COMMON_INSTALL_PREFIX}")
# set(Boost_version_suffix "1_59")
# if a link like boost-version/boost -> boost was created or the boost directory was renamed in the same way,
# the following needs adjustment
# set(BOOST_INCLUDEDIR "${COMMON_INSTALL_PREFIX}/include/boost-${Boost_version_suffix}")
# set(BOOST_LIBRARYDIR "${COMMON_INSTALL_PREFIX}/lib/")
# libtorrent
# set(PC_LIBTORRENT_RASTERBAR_INCLUDEDIR "${COMMON_INSTALL_PREFIX}")
# set(PC_LIBTORRENT_RASTERBAR_LIBDIR "${COMMON_INSTALL_PREFIX}/lib")
set(AUTOGEN_TARGETS_FOLDER "generated")
set(CMAKE_INSTALL_BINDIR ".")
# Test 32/64 bits
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
message(STATUS "Target is 64 bits")
if (WIN32)
set(WINXXBITS Win64)
endif(WIN32)
else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
message(STATUS "Target is 32 bits")
if (WIN32)
set(WINXXBITS Win32)
endif(WIN32)
endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
if (MSVC)
include(winconf-msvc)
else (MSVC)
include(winconf-mingw)
endif (MSVC)

2
configure vendored
View File

@@ -8345,7 +8345,7 @@ fi
$as_echo "$as_me: Running qmake to generate the makefile..." >&6;}
CONFDIR="$( cd "$( dirname "$0" )" && pwd )"
$QT_QMAKE -r $CONFDIR/qbittorrent.pro
$QT_QMAKE -r $CONFDIR/qbittorrent.pro "QMAKE_LRELEASE=$QMAKE_LRELEASE"
ret="$?"

View File

@@ -261,7 +261,7 @@ AS_IF([test "x$enable_systemd" = "xyes"],
AC_MSG_NOTICE([Running qmake to generate the makefile...])
CONFDIR="$( cd "$( dirname "$0" )" && pwd )"
$QT_QMAKE -r [$CONFDIR]/qbittorrent.pro
$QT_QMAKE -r [$CONFDIR]/qbittorrent.pro "QMAKE_LRELEASE=$QMAKE_LRELEASE"
ret="$?"

10
dist/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,10 @@
if (APPLE)
add_subdirectory(mac)
else (APPLE)
if (UNIX)
add_subdirectory(unix)
endif (UNIX)
if (WIN32)
add_subdirectory(windows)
endif (WIN32)
endif (APPLE)

0
dist/mac/CMakeLists.txt vendored Normal file
View File

2
dist/mac/Info.plist vendored
View File

@@ -45,7 +45,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.3.3</string>
<string>3.3.4</string>
<key>CFBundleSignature</key>
<string>qBit</string>
<key>CFBundleExecutable</key>

2
dist/mac/bundle.cmake vendored Normal file
View File

@@ -0,0 +1,2 @@
include(BundleUtilities)
fixup_bundle("$ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX}/qbittorrent.app" "" "")

37
dist/unix/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,37 @@
if (SYSTEMD)
find_package(Systemd)
if (SYSTEMD_FOUND)
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
configure_file(systemd/qbittorrent-nox.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service
DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}
COMPONENT data)
endif(SYSTEMD_FOUND)
endif(SYSTEMD)
if (GUI)
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
else (GUI)
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
endif (GUI)
install(FILES ${MAN_FILES}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
COMPONENT doc)
install(DIRECTORY menuicons/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
FILES_MATCHING PATTERN "*.png")
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.png
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps/
COMPONENT data)
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qBittorrent.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
COMPONENT data)
install(FILES qBittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/
COMPONENT data)

1
dist/windows/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1 @@
install(FILES qt.conf DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@@ -19,7 +19,7 @@ XPStyle on
!define CSIDL_APPDATA '0x1A' ;Application Data path
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
!define PROG_VERSION "3.3.3"
!define PROG_VERSION "3.3.4"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)

View File

@@ -1,2 +1,5 @@
[Paths]
Translations = translations
[Platforms]
WindowsArguments = dpiawareness=1

109
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,109 @@
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroLinkQtComponents)
find_package(LibtorrentRasterbar REQUIRED)
include_directories(SYSTEM ${LibtorrentRasterbar_INCLUDE_DIRS})
add_compile_options(${LibtorrentRasterbar_DEFINITIONS})
# Boost
set(Boost_USE_MULTITHREADED ON)
find_package(Boost 1.35 REQUIRED COMPONENTS system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
# Qt
if (QT5)
add_definitions(-DQBT_USES_QT5)
list(APPEND QBT_QT_COMPONENTS Core Network Xml)
if (GUI)
list (APPEND QBT_QT_COMPONENTS Concurrent Gui Widgets)
endif (GUI)
if (DBUS)
list (APPEND QBT_QT_COMPONENTS DBus)
endif (DBUS)
find_package(Qt5 5.2.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
else (QT5)
list(APPEND QBT_QT_COMPONENTS QtCore QtNetwork QtXml)
if (GUI)
list (APPEND QBT_QT_COMPONENTS QtGui)
endif (GUI)
if (DBUS)
list (APPEND QBT_QT_COMPONENTS QtDBus)
endif (DBUS)
find_package(Qt4 4.8.0 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
include(${QT_USE_FILE})
endif (QT5)
set(CMAKE_AUTOMOC True)
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# defines
add_definitions(-DQT_NO_CAST_TO_ASCII)
# Fast concatenation (Qt >= 4.6)
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
if (WIN32)
add_definitions(-DNOMINMAX)
endif (WIN32)
if (NOT GUI)
add_definitions(-DDISABLE_GUI -DDISABLE_COUNTRIES_RESOLUTION)
endif (NOT GUI)
if (NOT WEBUI)
add_definitions(-DDISABLE_WEBUI)
endif (NOT WEBUI)
if (STACKTRACE_WIN)
add_definitions(-DSTACKTRACE_WIN)
endif(STACKTRACE_WIN)
# nogui {
# TARGET = qbittorrent-nox
# } else {
# CONFIG(static) {
# DEFINES += QBT_STATIC_QT
# QTPLUGIN += qico
# }
# TARGET = qbittorrent
# }
if (UNIX AND NOT APPLE)
add_compile_options(-Wformat -Wformat-security)
endif ()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Project is built in DEBUG mode.")
else (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Project is built in RELEASE mode.")
message(STATUS "Disabling debug output.")
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(QBT_USE_GUI ${GUI})
set(QBT_USE_WEBUI ${WEBUI})
set(QBT_USES_QT5 ${QT5})
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
add_subdirectory(base)
if (SYSTEM_QTSINGLEAPPLICATION)
find_package(QtSingleApplication REQUIRED)
include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIR})
else (SYSTEM_QTSINGLEAPPLICATION)
include_directories(app/qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
if (GUI)
add_subdirectory(gui)
endif (GUI)
if (WEBUI)
add_subdirectory(webui)
endif (WEBUI)
add_subdirectory(app)

187
src/app/CMakeLists.txt Normal file
View File

@@ -0,0 +1,187 @@
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS
application.h
filelogger.h
)
set(QBT_APP_SOURCES
application.cpp
filelogger.cpp
main.cpp
)
# translations
file(GLOB QBT_TS_FILES ../lang/*.ts)
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${QBT_QM_FILES_BINARY_DIR}")
if (QT5)
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
else (QT5)
qt4_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
endif (QT5)
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang.qrc" ABSOLUTE)
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang.qrc" ABSOLUTE)
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../" ABSOLUTE)
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES GENERATED True)
foreach(qm_file ${QBT_QM_FILES})
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES OBJECT_DEPENDS ${qm_file})
endforeach()
set(QBT_APP_RESOURCES
../icons.qrc
"${_lang_qrc_dst}"
)
# With AUTORCC rcc is ran by cmake before language files are generated,
# and thus we call rcc explicitly
if (QT5)
qt5_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
else (QT5)
qt4_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
endif (QT5)
if (WIN32)
if (MINGW)
list (APPEND QBT_APP_SOURCES ../qbittorrent_mingw.rc)
else (MINGW)
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
endif (MINGW)
endif (WIN32)
if (UNIX)
list(APPEND QBT_APP_HEADERS stacktrace.h)
endif (UNIX)
if (STACKTRACE_WIN)
list(APPEND QBT_APP_HEADERS stacktrace_win.h)
if (GUI)
list(APPEND QBT_APP_HEADERS stacktrace_win_dlg.h)
endif (GUI)
endif (STACKTRACE_WIN)
# usesystemqtsingleapplication {
# nogui {
# CONFIG += qtsinglecoreapplication
# } else {
# CONFIG += qtsingleapplication
# }
# } else {
# nogui {
# include(qtsingleapplication/qtsinglecoreapplication.pri)
# } else {
# include(qtsingleapplication/qtsingleapplication.pri)
# }
# }
# upgrade code
list(APPEND QBT_APP_HEADERS upgrade.h)
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
if (GUI)
set(QBT_TARGET_NAME qbittorrent)
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
include_directories(../gui
${CMAKE_CURRENT_BINARY_DIR}/../gui
)
else (GUI)
set(QBT_TARGET_NAME qbittorrent-nox)
endif (GUI)
if (WEBUI)
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
endif (WEBUI)
# we have to include resources into the bundle
if (APPLE)
set(OSX_RES_SRC_DIR "${qBittorrent_SOURCE_DIR}/dist/mac")
list(APPEND QBT_APP_RESOURCE_SOURCE
"${OSX_RES_SRC_DIR}/qt.conf"
"${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
"${OSX_RES_SRC_DIR}/qbittorrent_mac.icns")
set_source_files_properties(
"${OSX_RES_SRC_DIR}/qt.conf"
"${OSX_RES_SRC_DIR}/qBitTorrentDocument.icns"
"${OSX_RES_SRC_DIR}/qbittorrent_mac.icns"
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
set(QT_TR_DIR "${qBittorrent_SOURCE_DIR}/dist/qt-translations")
set(QT_TRANSLATIONS
${QT_TR_DIR}/qt_ar.qm
${QT_TR_DIR}/qt_bg.qm
${QT_TR_DIR}/qt_ca.qm
${QT_TR_DIR}/qt_cs.qm
${QT_TR_DIR}/qt_da.qm
${QT_TR_DIR}/qt_de.qm
${QT_TR_DIR}/qt_es.qm
${QT_TR_DIR}/qt_eu.qm
${QT_TR_DIR}/qt_fi.qm
${QT_TR_DIR}/qt_fr.qm
${QT_TR_DIR}/qt_gl.qm
${QT_TR_DIR}/qt_he.qm
${QT_TR_DIR}/qt_hu.qm
${QT_TR_DIR}/qt_it.qm
${QT_TR_DIR}/qt_ja.qm
${QT_TR_DIR}/qt_ko.qm
${QT_TR_DIR}/qt_lt.qm
${QT_TR_DIR}/qt_nl.qm
${QT_TR_DIR}/qt_pl.qm
${QT_TR_DIR}/qt_pt.qm
${QT_TR_DIR}/qt_pt_BR.qm
${QT_TR_DIR}/qt_ru.qm
${QT_TR_DIR}/qt_sk.qm
${QT_TR_DIR}/qt_sv.qm
${QT_TR_DIR}/qt_tr.qm
${QT_TR_DIR}/qt_uk.qm
${QT_TR_DIR}/qt_zh_CN.qm
${QT_TR_DIR}/qt_zh_TW.qm
)
list(APPEND QBT_APP_RESOURCE_SOURCE ${QT_TRANSLATIONS})
set_source_files_properties(${QT_TRANSLATIONS}
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
endif (APPLE)
add_executable(${QBT_TARGET_NAME} ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
set_target_properties(${QBT_TARGET_NAME}
PROPERTIES
AUTOUIC True
MACOSX_BUNDLE True
)
if (GUI AND WIN32)
set_target_properties(${QBT_TARGET_NAME} PROPERTIES WIN32_EXECUTABLE True)
endif (GUI AND WIN32)
target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES})
if (SYSTEM_QTSINGLEAPPLICATION)
target_link_libraries(${QBT_TARGET_NAME} ${QTSINGLEAPPLICATION_LIBRARIES})
else (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(qtsingleapplication)
target_link_libraries(${QBT_TARGET_NAME} qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
if (APPLE)
set(qbt_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")
set_target_properties(${QBT_TARGET_NAME} PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "${qbt_BUNDLE_NAME}"
MACOSX_BUNDLE_INFO_PLIST ${qBittorrent_SOURCE_DIR}/dist/mac/Info.plist
)
endif (APPLE)
# installation
install(TARGETS ${QBT_TARGET_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION .
COMPONENT runtime)
if (APPLE)
install(SCRIPT ${OSX_RES_SRC_DIR}/bundle.cmake)
endif (APPLE)

View File

@@ -14,8 +14,14 @@ usesystemqtsingleapplication {
}
}
HEADERS += $$PWD/application.h
SOURCES += $$PWD/application.cpp
HEADERS += \
$$PWD/application.h \
$$PWD/filelogger.h
SOURCES += \
$$PWD/application.cpp \
$$PWD/filelogger.cpp \
$$PWD/main.cpp
unix: HEADERS += $$PWD/stacktrace.h
strace_win {
@@ -26,7 +32,5 @@ strace_win {
}
}
SOURCES += $$PWD/main.cpp
# upgrade code
HEADERS += $$PWD/upgrade.h

View File

@@ -58,6 +58,7 @@
#endif
#include "application.h"
#include "filelogger.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/utils/fs.h"
@@ -92,7 +93,9 @@ Application::Application(const QString &id, int &argc, char **argv)
setApplicationName("qBittorrent");
initializeTranslation();
#ifndef DISABLE_GUI
setStyleSheet("QStatusBar::item { border-width: 0; }");
#ifdef QBT_USES_QT5
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
#endif // QBT_USES_QT5
setQuitOnLastWindowClosed(false);
#ifdef Q_OS_WIN
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
@@ -101,10 +104,22 @@ Application::Application(const QString &id, int &argc, char **argv)
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
configure();
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
}
void Application::configure()
{
bool fileLogEnabled = Preferences::instance()->fileLogEnabled();
if (fileLogEnabled && !m_fileLogger)
m_fileLogger = new FileLogger;
else if (!fileLogEnabled)
delete m_fileLogger;
}
void Application::processMessage(const QString &message)
{
QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts);
@@ -186,7 +201,14 @@ void Application::allTorrentsFinished()
else if (shutdown)
action = ShutdownAction::Shutdown;
if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
if ((action == ShutdownAction::None) && (!pref->dontConfirmAutoExit())) {
if (!ShutdownConfirmDlg::askForConfirmation(action))
return;
}
else { //exit and shutdown
if (!ShutdownConfirmDlg::askForConfirmation(action))
return;
}
// Actually shut down
if (suspend || hibernate || shutdown) {
@@ -464,6 +486,7 @@ void Application::cleanup()
#endif
Net::DownloadManager::freeInstance();
Preferences::freeInstance();
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
#ifndef DISABLE_GUI

View File

@@ -56,6 +56,8 @@ typedef QtSingleCoreApplication BaseApplication;
class WebUI;
#endif
class FileLogger;
namespace BitTorrent
{
class TorrentHandle;
@@ -83,6 +85,7 @@ protected:
#endif
private slots:
void configure();
void processMessage(const QString &message);
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
void allTorrentsFinished();
@@ -103,6 +106,9 @@ private:
QPointer<WebUI> m_webui;
#endif
// FileLog
QPointer<FileLogger> m_fileLogger;
QTranslator m_qtTranslator;
QTranslator m_translator;
QStringList m_paramsQueue;

174
src/app/filelogger.cpp Normal file
View File

@@ -0,0 +1,174 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include <QDir>
#include <QFile>
#include <QTextStream>
#include "filelogger.h"
#include "base/preferences.h"
#include "base/logger.h"
#include "base/utils/fs.h"
namespace
{
enum FileLogAgeType
{
DAYS,
MONTHS,
YEARS
};
}
FileLogger::FileLogger()
: m_logFile(nullptr)
{
m_flusher.setInterval(0);
m_flusher.setSingleShot(true);
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
configure();
const Logger* const logger = Logger::instance();
foreach (const Log::Msg& msg, logger->getMessages())
addLogMessage(msg);
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
}
FileLogger::~FileLogger()
{
if (!m_logFile) return;
closeLogFile();
delete m_logFile;
}
void FileLogger::configure()
{
const Preferences* const pref = Preferences::instance();
QString tmpPath = Utils::Fs::fromNativePath(pref->fileLogPath());
QDir dir(tmpPath);
dir.mkpath(tmpPath);
tmpPath = dir.absoluteFilePath("qbittorrent.log");
if (tmpPath != m_path) {
m_path = tmpPath;
if (m_logFile) {
closeLogFile();
delete m_logFile;
}
m_logFile = new QFile(m_path);
openLogFile();
}
m_backup = pref->fileLogBackup();
m_size = pref->fileLogMaxSize();
if (pref->fileLogDeleteOld()) {
QDateTime date = QDateTime::currentDateTime();
switch (static_cast<FileLogAgeType>(pref->fileLogAgeType())) {
case DAYS:
date = date.addDays(pref->fileLogAge());
break;
case MONTHS:
date = date.addMonths(pref->fileLogAge());
break;
default:
date = date.addYears(pref->fileLogAge());
}
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
if (file.lastModified() < date)
break;
Utils::Fs::forceRemove(file.absoluteFilePath());
}
}
}
void FileLogger::addLogMessage(const Log::Msg &msg)
{
if (!m_logFile) return;
QTextStream str(m_logFile);
switch (msg.type) {
case Log::INFO:
str << "(I) ";
break;
case Log::WARNING:
str << "(W) ";
break;
case Log::CRITICAL:
str << "(C) ";
break;
default:
str << "(N) ";
}
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
if (m_backup && (m_logFile->size() >= (m_size * 1024 * 1024))) {
closeLogFile();
int counter = 0;
QString backupLogFilename = m_path + ".bak";
while (QFile::exists(backupLogFilename)) {
++counter;
backupLogFilename = m_path + ".bak" + QString::number(counter);
}
QFile::rename(m_path, backupLogFilename);
openLogFile();
}
else {
m_flusher.start();
}
}
void FileLogger::flushLog()
{
if (m_logFile)
m_logFile->flush();
}
void FileLogger::openLogFile()
{
if (!m_logFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)
|| !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
delete m_logFile;
m_logFile = nullptr;
Logger::instance()->addMessage(tr("An error occured while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
}
}
void FileLogger::closeLogFile()
{
m_flusher.stop();
m_logFile->close();
}

68
src/app/filelogger.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef FILELOGGER_H
#define FILELOGGER_H
#include <QObject>
#include <QTimer>
class QFile;
namespace Log
{
struct Msg;
}
class FileLogger : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(FileLogger)
public:
FileLogger();
~FileLogger();
private slots:
void configure();
void addLogMessage(const Log::Msg &msg);
void flushLog();
private:
void openLogFile();
void closeLogFile();
bool m_backup;
int m_size;
QString m_path;
QFile *m_logFile;
QTimer m_flusher;
};
#endif // FILELOGGER_H

View File

@@ -79,10 +79,15 @@ Q_IMPORT_PLUGIN(qico)
// Signal handlers
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
void sigintHandler(int);
void sigtermHandler(int);
void sigsegvHandler(int);
void sigabrtHandler(int);
void sigNormalHandler(int signum);
void sigAbnormalHandler(int signum);
// sys_signame[] is only defined in BSD
const char *sysSigName[] = {
"", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL",
"SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP",
"SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
"SIGPWR", "SIGUNUSED"
};
#endif
struct QBtCommandLineParameters
@@ -210,6 +215,21 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
#if defined(Q_OS_WIN) && defined(QBT_USES_QT5)
// This affects only Windows apparently and Qt5.
// When QNetworkAccessManager is instantiated it regularly starts polling
// the network interfaces to see what's available and their status.
// This polling creates jitter and high ping with wifi interfaces.
// So here we disable it for lack of better measure.
// It will also spew this message in the console: QObject::startTimer: Timers cannot have negative intervals
// For more info see:
// 1. https://github.com/qbittorrent/qBittorrent/issues/4209
// 2. https://bugreports.qt.io/browse/QTBUG-40332
// 3. https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#endif
#ifndef DISABLE_GUI
if (!upgrade()) return EXIT_FAILURE;
#else
@@ -240,10 +260,10 @@ int main(int argc, char *argv[])
#endif
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
signal(SIGABRT, sigabrtHandler);
signal(SIGTERM, sigtermHandler);
signal(SIGINT, sigintHandler);
signal(SIGSEGV, sigsegvHandler);
signal(SIGINT, sigNormalHandler);
signal(SIGTERM, sigNormalHandler);
signal(SIGABRT, sigAbnormalHandler);
signal(SIGSEGV, sigAbnormalHandler);
#endif
return app->exec(params.torrents);
@@ -303,58 +323,41 @@ QBtCommandLineParameters parseCommandLine()
}
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
void sigintHandler(int)
void sigNormalHandler(int signum)
{
signal(SIGINT, 0);
qDebug("Catching SIGINT, exiting cleanly");
qApp->exit();
}
void sigtermHandler(int)
{
signal(SIGTERM, 0);
qDebug("Catching SIGTERM, exiting cleanly");
qApp->exit();
}
void sigsegvHandler(int)
{
signal(SIGABRT, 0);
signal(SIGSEGV, 0);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
std::cerr << "\n\n*************************************************************\n";
std::cerr << "Catching SIGSEGV, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
std::cerr << "qBittorrent version: " << VERSION << std::endl;
print_stacktrace();
#else
const char str1[] = "Catching signal: ";
const char *sigName = sysSigName[signum];
const char str2[] = "\nExiting cleanly\n";
write(STDERR_FILENO, str1, strlen(str1));
write(STDERR_FILENO, sigName, strlen(sigName));
write(STDERR_FILENO, str2, strlen(str2));
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
signal(signum, SIG_DFL);
qApp->exit(); // unsafe, but exit anyway
}
void sigAbnormalHandler(int signum)
{
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
const char str1[] = "\n\n*************************************************************\nCatching signal: ";
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"
"qBittorrent version: " VERSION "\n";
write(STDERR_FILENO, str1, strlen(str1));
write(STDERR_FILENO, sigName, strlen(sigName));
write(STDERR_FILENO, str2, strlen(str2));
print_stacktrace(); // unsafe
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
#ifdef STACKTRACE_WIN
StraceDlg dlg;
StraceDlg dlg; // unsafe
dlg.setStacktraceString(straceWin::getBacktrace());
dlg.exec();
#endif
#endif
raise(SIGSEGV);
#endif // STACKTRACE_WIN
signal(signum, SIG_DFL);
raise(signum);
}
void sigabrtHandler(int)
{
signal(SIGABRT, 0);
signal(SIGSEGV, 0);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
std::cerr << "\n\n*************************************************************\n";
std::cerr << "Catching SIGABRT, please report a bug at http://bug.qbittorrent.org\nand provide the following backtrace:\n";
std::cerr << "qBittorrent version: " << VERSION << std::endl;
print_stacktrace();
#else
#ifdef STACKTRACE_WIN
StraceDlg dlg;
dlg.setStacktraceString(straceWin::getBacktrace());
dlg.exec();
#endif
#endif
raise(SIGABRT);
}
#endif
#endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
#ifndef DISABLE_GUI
void showSplashScreen()
@@ -365,9 +368,9 @@ void showSplashScreen()
painter.setPen(QPen(Qt::white));
painter.setFont(QFont("Arial", 22, QFont::Black));
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
QSplashScreen *splash = new QSplashScreen(splash_img, Qt::WindowStaysOnTopHint);
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
QSplashScreen *splash = new QSplashScreen(splash_img);
splash->show();
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
qApp->processEvents();
}
#endif

View File

@@ -0,0 +1,32 @@
set(QBT_QTSINGLEAPPLICATION_HEADERS
qtlocalpeer.h
)
set(QBT_QTSINGLEAPPLICATION_SOURCES
qtlocalpeer.cpp
)
if (GUI)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
else (GUI)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
endif (GUI)
add_library(qtsingleapplication ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
if (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt4::QtNetwork)
else (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt5::Network)
endif (QT4_FOUND)
if (GUI)
if (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt4::QtGui)
else (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt5::Widgets)
endif(QT4_FOUND)
endif (GUI)

View File

@@ -18,6 +18,9 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef STACKTRACE_WIN_H
#define STACKTRACE_WIN_H
#include <windows.h>
#include <dbghelp.h>
#include <stdio.h>
@@ -256,9 +259,9 @@ const QString straceWin::getBacktrace()
}
}
logStream << "\n\nList of linked Modules:\n";
EnumModulesContext modulesContext(hProcess, logStream);
SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
//logStream << "\n\nList of linked Modules:\n";
//EnumModulesContext modulesContext(hProcess, logStream);
//SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
logStream << "```";
return log;
}
@@ -266,3 +269,5 @@ const QString straceWin::getBacktrace()
#pragma warning(pop)
#pragma optimize("g", on)
#endif
#endif // STACKTRACE_WIN_H

View File

@@ -1,51 +1,77 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 The qBittorrent project
*
* 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 STACKTRACE_WIN_DLG_H
#define STACKTRACE_WIN_DLG_H
#include <QTextStream>
#include <QClipboard>
#include "boost/version.hpp"
#include "libtorrent/version.hpp"
#include <QString>
#include <QDialog>
#include "base/utils/misc.h"
#include "ui_stacktrace_win_dlg.h"
class StraceDlg: public QDialog, private Ui::errorDialog
class StraceDlg : public QDialog, private Ui::errorDialog
{
Q_OBJECT
public:
StraceDlg(QWidget* parent = 0): QDialog(parent)
StraceDlg(QWidget* parent = 0)
: QDialog(parent)
{
setupUi(this);
}
~StraceDlg()
{
}
void setStacktraceString(const QString& trace)
{
QString htmlStr;
QTextStream outStream(&htmlStr);
outStream << "<p align=center><b><font size=7 color=red>" <<
"qBittorrent has crashed" <<
"</font></b></p>" <<
"<font size=4>" <<
"<p>" <<
"Please report a bug at <a href=\"http://bugs.qbittorrent.org\">" <<
"http://bugs.qbittorrent.org</a>" <<
" and provide the following backtrace." <<
"</p>" <<
"</font>" <<
"<br/><hr><br/>" <<
"<p align=center><font size=4>qBittorrent version: " << VERSION <<
"<br/>Libtorrent version: " << LIBTORRENT_VERSION <<
"<br/>Qt version: " << QT_VERSION_STR <<
"<br/>Boost version: " << QString::number(BOOST_VERSION / 100000) << '.' <<
QString::number((BOOST_VERSION / 100) % 1000) << '.' <<
QString::number(BOOST_VERSION % 100) << "</font></p><br/>"
"<pre><code>" <<
trace <<
"</code></pre>" <<
"<br/><hr><br/><br/>";
// try to call Qt function as less as possible
QString htmlStr = QString(
"<p align=center><b><font size=7 color=red>"
"qBittorrent has crashed"
"</font></b></p>"
"<font size=4><p>"
"Please file a bug report at "
"<a href=\"http://bugs.qbittorrent.org\">http://bugs.qbittorrent.org</a> "
"and provide the following information:"
"</p></font>"
"<br/><hr><br/>"
"<p align=center><font size=4>"
"qBittorrent version: " VERSION "<br/>"
"Libtorrent version: %1<br/>"
"Qt version: " QT_VERSION_STR "<br/>"
"Boost version: %2<br/>"
"OS version: %3"
"</font></p><br/>"
"<pre><code>%4</code></pre>"
"<br/><hr><br/><br/>")
.arg(Utils::Misc::libtorrentVersionString())
.arg(Utils::Misc::boostVersionString())
.arg(Utils::Misc::osName())
.arg(trace);
errorText->setHtml(htmlStr);
}

View File

@@ -36,6 +36,7 @@
#include <QString>
#include <QDir>
#include <QFile>
#include <QRegExp>
#ifndef DISABLE_GUI
#include <QMessageBox>
#endif
@@ -76,7 +77,7 @@ bool userAcceptsUpgrade()
return false;
}
bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent, int &maxPrio)
bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent = QVariantHash())
{
QFile file1(filepath);
if (!file1.open(QIODevice::ReadOnly))
@@ -88,24 +89,37 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent,
libtorrent::lazy_entry fastOld;
libtorrent::error_code ec;
libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
if ((fastOld.type() != libtorrent::lazy_entry::dict_t) && !ec) return false;
if (ec || (fastOld.type() != libtorrent::lazy_entry::dict_t)) return false;
libtorrent::entry fastNew;
fastNew = fastOld;
int priority = fastOld.dict_find_int_value("qBt-queuePosition");
if (priority > maxPrio)
maxPrio = priority;
bool v3_3 = false;
int queuePosition = 0;
QString outFilePath = filepath;
QRegExp rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
if (rx.indexIn(filepath) != -1) {
// old v3.3.x format
queuePosition = rx.cap(2).toInt();
v3_3 = true;
outFilePath.replace(QRegExp("\\.\\d+$"), "");
}
else {
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
fastNew["qBt-name"] = Utils::String::toStdString(oldTorrent.value("name").toString());
fastNew["qBt-tempPathDisabled"] = false;
}
fastNew["qBt-name"] = Utils::String::toStdString(oldTorrent.value("name").toString());
fastNew["qBt-tempPathDisabled"] = false;
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
QFile file2(QString("%1.%2").arg(filepath).arg(priority > 0 ? priority : 0));
QFile file2(outFilePath);
QVector<char> out;
libtorrent::bencode(std::back_inserter(out), fastNew);
if (file2.open(QIODevice::WriteOnly)) {
if (file2.write(&out[0], out.size()) == out.size()) {
Utils::Fs::forceRemove(filepath);
if (v3_3)
Utils::Fs::forceRemove(filepath);
return true;
}
}
@@ -118,26 +132,35 @@ bool upgrade(bool ask = true)
// Move RSS cookies to common storage
Preferences::instance()->moveRSSCookies();
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
QDir backupFolderDir(backupFolderPath);
// ****************************************************************************************
// Silently converts old v3.3.x .fastresume files
QStringList backupFiles_3_3 = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted);
foreach (const QString &backupFile, backupFiles_3_3)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// ****************************************************************************************
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
QString oldResumeFilename = oldResumeSettings->fileName();
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();
delete oldResumeSettings;
bool oldResumeWasEmpty = oldResumeData.isEmpty();
if (oldResumeWasEmpty)
if (oldResumeData.isEmpty()) {
Utils::Fs::forceRemove(oldResumeFilename);
return true;
}
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
QDir backupFolderDir(backupFolderPath);
QStringList backupFiles = backupFolderDir.entryList(QStringList() << QLatin1String("*.fastresume"), QDir::Files, QDir::Unsorted);
if (backupFiles.isEmpty() && oldResumeWasEmpty) return true;
if (ask && !userAcceptsUpgrade()) return false;
int maxPrio = 0;
QStringList backupFiles = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (QString backupFile, backupFiles) {
if (rx.indexIn(backupFile) != -1) {
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash(), maxPrio))
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash()))
oldResumeData.remove(rx.cap(1));
else
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
@@ -162,7 +185,10 @@ bool upgrade(bool ask = true)
resumeData["qBt-seedStatus"] = oldTorrent.value("seed").toBool();
resumeData["qBt-tempPathDisabled"] = false;
QString filename = QString("%1.fastresume.%2").arg(hash).arg(++maxPrio);
int queuePosition = oldTorrent.value("priority", 0).toInt();
resumeData["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
QString filename = QString("%1.fastresume").arg(hash);
QString filepath = backupFolderDir.absoluteFilePath(filename);
QFile resumeFile(filepath);
@@ -173,17 +199,13 @@ bool upgrade(bool ask = true)
}
}
if (!oldResumeWasEmpty) {
int counter = 0;
QString backupResumeFilename = oldResumeFilename + ".bak";
while (QFile::exists(backupResumeFilename)) {
++counter;
backupResumeFilename = oldResumeFilename + ".bak" + QString::number(counter);
}
QFile::rename(oldResumeFilename, backupResumeFilename);
int counter = 0;
QString backupResumeFilename = oldResumeFilename + ".bak";
while (QFile::exists(backupResumeFilename)) {
++counter;
backupResumeFilename = oldResumeFilename + ".bak" + QString::number(counter);
}
QFile::rename(oldResumeFilename, backupResumeFilename);
return true;
}

131
src/base/CMakeLists.txt Normal file
View File

@@ -0,0 +1,131 @@
find_package(ZLIB REQUIRED)
include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
set(QBT_BASE_HEADERS
bittorrent/cachestatus.h
bittorrent/infohash.h
bittorrent/magneturi.h
bittorrent/peerinfo.h
bittorrent/private/bandwidthscheduler.h
bittorrent/private/filterparserthread.h
bittorrent/private/resumedatasavingmanager.h
bittorrent/private/speedmonitor.h
bittorrent/private/statistics.h
bittorrent/session.h
bittorrent/sessionstatus.h
bittorrent/torrentcreatorthread.h
bittorrent/torrenthandle.h
bittorrent/torrentinfo.h
bittorrent/tracker.h
bittorrent/trackerentry.h
http/connection.h
http/irequesthandler.h
http/requestparser.h
http/responsebuilder.h
http/responsegenerator.h
http/server.h
http/types.h
net/dnsupdater.h
net/downloadhandler.h
net/downloadmanager.h
net/geoipmanager.h
net/portforwarder.h
net/private/geoipdatabase.h
net/reverseresolution.h
net/smtp.h
rss/private/rssparser.h
rss/rssarticle.h
rss/rssdownloadrule.h
rss/rssdownloadrulelist.h
rss/rssfeed.h
rss/rssfile.h
rss/rssfolder.h
rss/rssmanager.h
utils/fs.h
utils/gzip.h
utils/misc.h
utils/string.h
filesystemwatcher.h
iconprovider.h
logger.h
preferences.h
qinisettings.h
scanfoldersmodel.h
searchengine.h
settingsstorage.h
torrentfilter.h
tristatebool.h
types.h
unicodestrings.h
)
set(QBT_BASE_SOURCES
bittorrent/cachestatus.cpp
bittorrent/infohash.cpp
bittorrent/magneturi.cpp
bittorrent/peerinfo.cpp
bittorrent/private/bandwidthscheduler.cpp
bittorrent/private/filterparserthread.cpp
bittorrent/private/resumedatasavingmanager.cpp
bittorrent/private/speedmonitor.cpp
bittorrent/private/statistics.cpp
bittorrent/session.cpp
bittorrent/sessionstatus.cpp
bittorrent/torrentcreatorthread.cpp
bittorrent/torrenthandle.cpp
bittorrent/torrentinfo.cpp
bittorrent/tracker.cpp
bittorrent/trackerentry.cpp
http/connection.cpp
http/requestparser.cpp
http/responsebuilder.cpp
http/responsegenerator.cpp
http/server.cpp
net/dnsupdater.cpp
net/downloadhandler.cpp
net/downloadmanager.cpp
net/geoipmanager.cpp
net/portforwarder.cpp
net/private/geoipdatabase.cpp
net/reverseresolution.cpp
net/smtp.cpp
rss/private/rssparser.cpp
rss/rssarticle.cpp
rss/rssdownloadrule.cpp
rss/rssdownloadrulelist.cpp
rss/rssfeed.cpp
rss/rssfile.cpp
rss/rssfolder.cpp
rss/rssmanager.cpp
utils/fs.cpp
utils/gzip.cpp
utils/misc.cpp
utils/string.cpp
filesystemwatcher.cpp
iconprovider.cpp
logger.cpp
preferences.cpp
scanfoldersmodel.cpp
searchengine.cpp
settingsstorage.cpp
torrentfilter.cpp
tristatebool.cpp
)
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
target_link_libraries(qbt_base ${ZLIB_LIBRARIES} ${LibtorrentRasterbar_LIBRARIES})
target_link_qt_components(qbt_base Core Network Xml)
if (QT4_FOUND)
if (GUI)
target_link_libraries(qbt_base Qt4::QtGui)
endif (GUI)
else (QT4_FOUND)
if (GUI)
target_link_libraries(qbt_base Qt5::Gui Qt5::Widgets)
endif (GUI)
endif (QT4_FOUND)
if (APPLE)
find_library(IOKit_LIBRARY IOKit)
find_library(Carbon_LIBRARY Carbon)
target_link_libraries(qbt_base ${Carbon_LIBRARY} ${IOKit_LIBRARY})
endif (APPLE)

View File

@@ -36,6 +36,7 @@ HEADERS += \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/bittorrent/private/resumedatasavingmanager.h \
$$PWD/rss/rssmanager.h \
$$PWD/rss/rssfeed.h \
$$PWD/rss/rssfolder.h \
@@ -87,6 +88,7 @@ SOURCES += \
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
$$PWD/rss/rssmanager.cpp \
$$PWD/rss/rssfeed.cpp \
$$PWD/rss/rssfolder.cpp \

View File

@@ -26,6 +26,7 @@
* exception statement from your version.
*/
#include <libtorrent/version.hpp>
#include "cachestatus.h"
using namespace BitTorrent;
@@ -50,7 +51,11 @@ qreal CacheStatus::readRatio() const
int CacheStatus::jobQueueLength() const
{
#if LIBTORRENT_VERSION_NUM < 10100
return m_nativeStatus.job_queue_length;
#else
return m_nativeStatus.queued_jobs;
#endif
}
int CacheStatus::averageJobTime() const

View File

@@ -29,8 +29,8 @@
#ifndef BITTORRENT_CACHESTATUS_H
#define BITTORRENT_CACHESTATUS_H
#include <libtorrent/disk_io_thread.hpp>
#include <QtGlobal>
#include <libtorrent/disk_io_thread.hpp>
namespace BitTorrent
{

View File

@@ -26,6 +26,10 @@
* exception statement from your version.
*/
#include <QByteArray>
#include <QRegExp>
#include <QStringList>
#include <libtorrent/bencode.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/magnet_uri.hpp>
@@ -33,17 +37,45 @@
#include "base/utils/string.h"
#include "magneturi.h"
namespace
{
QString bcLinkToMagnet(QString bcLink)
{
QByteArray rawBc = bcLink.toUtf8();
rawBc = rawBc.mid(8); // skip bc://bt/
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
QStringList parts = QString(rawBc).split("/");
if (parts.size() != 5) return QString();
QString filename = parts.at(1);
QString hash = parts.at(3);
QString magnet = "magnet:?xt=urn:btih:" + hash;
magnet += "&dn=" + filename;
return magnet;
}
}
namespace libt = libtorrent;
using namespace BitTorrent;
MagnetUri::MagnetUri(const QString &url)
MagnetUri::MagnetUri(const QString &source)
: m_valid(false)
, m_url(url)
, m_url(source)
{
if (url.isEmpty()) return;
if (source.isEmpty()) return;
if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Creating magnet link from bc link");
m_url = bcLinkToMagnet(source);
}
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
m_url = "magnet:?xt=urn:btih:" + source;
}
libt::error_code ec;
libt::parse_magnet_uri(url.toUtf8().constData(), m_addTorrentParams, ec);
libt::parse_magnet_uri(m_url.toUtf8().constData(), m_addTorrentParams, ec);
if (ec) return;
m_valid = true;

View File

@@ -43,7 +43,7 @@ namespace BitTorrent
class MagnetUri
{
public:
explicit MagnetUri(const QString &url = QString());
explicit MagnetUri(const QString &source = QString());
bool isValid() const;
InfoHash hash() const;

View File

@@ -408,3 +408,8 @@ QString PeerInfo::flagsDescription() const
{
return m_flagsDescription;
}
int PeerInfo::downloadingPieceIndex() const
{
return m_nativeInfo.downloading_piece_index;
}

View File

@@ -100,6 +100,7 @@ namespace BitTorrent
#ifndef DISABLE_COUNTRIES_RESOLUTION
QString country() const;
#endif
int downloadingPieceIndex() const;
private:
void calcRelevance(const TorrentHandle *torrent);

View File

@@ -0,0 +1,65 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 <QDebug>
#ifdef QBT_USES_QT5
#include <QSaveFile>
#else
#include <QFile>
#endif
#include "base/logger.h"
#include "base/utils/fs.h"
#include "resumedatasavingmanager.h"
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
: m_resumeDataDir(resumeFolderPath)
{
}
void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data) const
{
QString filename = QString("%1.fastresume").arg(infoHash);
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
qDebug() << "Saving resume data in" << filepath;
#ifdef QBT_USES_QT5
QSaveFile resumeFile(filepath);
#else
QFile resumeFile(filepath);
#endif
if (resumeFile.open(QIODevice::WriteOnly)) {
resumeFile.write(data);
#ifdef QBT_USES_QT5
if (!resumeFile.commit()) {
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
.arg(filepath).arg(resumeFile.errorString()), Log::WARNING);
}
#endif
}
}

View File

@@ -0,0 +1,50 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
*
* 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 RESUMEDATASAVINGMANAGER_H
#define RESUMEDATASAVINGMANAGER_H
#include <QObject>
#include <QByteArray>
#include <QDir>
class ResumeDataSavingManager: public QObject
{
Q_OBJECT
public:
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots:
void saveResumeData(QString infoHash, QByteArray data) const;
private:
QDir m_resumeDataDir;
};
#endif // RESUMEDATASAVINGMANAGER_H

View File

@@ -43,10 +43,13 @@ using namespace BitTorrent;
#include <QTimer>
#include <QProcess>
#include <QCoreApplication>
#include <QThread>
#include <queue>
#include <vector>
#include <boost/bind.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/lazy_entry.hpp>
#include <libtorrent/bencode.hpp>
@@ -76,6 +79,7 @@ using namespace BitTorrent;
#include "private/filterparserthread.h"
#include "private/statistics.h"
#include "private/bandwidthscheduler.h"
#include "private/resumedatasavingmanager.h"
#include "trackerentry.h"
#include "tracker.h"
#include "magneturi.h"
@@ -91,23 +95,13 @@ namespace libt = libtorrent;
using namespace BitTorrent;
static bool readFile(const QString &path, QByteArray &buf);
static bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, MagnetUri &magnetUri);
static bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri);
static void torrentQueuePositionUp(const libt::torrent_handle &handle);
static void torrentQueuePositionDown(const libt::torrent_handle &handle);
static void torrentQueuePositionTop(const libt::torrent_handle &handle);
static void torrentQueuePositionBottom(const libt::torrent_handle &handle);
// AddTorrentParams
AddTorrentParams::AddTorrentParams()
: disableTempPath(false)
, sequential(false)
, ignoreShareRatio(false)
, skipChecking(false)
{
}
// Session
Session *Session::m_instance = 0;
@@ -201,6 +195,11 @@ Session::Session(QObject *parent)
connect(&m_networkManager, SIGNAL(configurationRemoved(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
connect(&m_networkManager, SIGNAL(configurationChanged(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
m_ioThread = new QThread(this);
m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath);
m_resumeDataSavingManager->moveToThread(m_ioThread);
connect(m_ioThread, SIGNAL(finished()), m_resumeDataSavingManager, SLOT(deleteLater()));
m_ioThread->start();
m_resumeDataTimer->start();
// initialize PortForwarder instance
@@ -278,6 +277,9 @@ Session::~Session()
qDebug("Deleting the session");
delete m_nativeSession;
m_ioThread->quit();
m_ioThread->wait();
m_resumeFolderLock.close();
m_resumeFolderLock.remove();
}
@@ -358,16 +360,19 @@ void Session::setSessionSettings()
sessionSettings.active_lsd_limit = -1;
// Outgoing ports
#if LIBTORRENT_VERSION_NUM < 10100
sessionSettings.outgoing_ports = std::make_pair(pref->outgoingPortsMin(), pref->outgoingPortsMax());
#else
sessionSettings.outgoing_port = pref->outgoingPortsMin();
sessionSettings.num_outgoing_ports = pref->outgoingPortsMax() - pref->outgoingPortsMin();
#endif
// Ignore limits on LAN
qDebug() << "Ignore limits on LAN" << pref->getIgnoreLimitsOnLAN();
sessionSettings.ignore_limits_on_local_network = pref->getIgnoreLimitsOnLAN();
// Include overhead in transfer limits
sessionSettings.rate_limit_ip_overhead = pref->includeOverheadInLimits();
// IP address to announce to trackers
QString announce_ip = pref->getNetworkAddress();
if (!announce_ip.isEmpty())
sessionSettings.announce_ip = Utils::String::toStdString(announce_ip);
sessionSettings.announce_ip = Utils::String::toStdString(pref->getNetworkAddress());
// Super seeding
sessionSettings.strict_super_seeding = pref->isSuperSeedingEnabled();
// * Max Half-open connections
@@ -708,7 +713,7 @@ void Session::handleDownloadFailed(const QString &url, const QString &reason)
void Session::handleRedirectedToMagnet(const QString &url, const QString &magnetUri)
{
addTorrent_impl(addDataFromParams(m_downloadedTorrents.take(url)), MagnetUri(magnetUri));
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(magnetUri));
}
void Session::switchToAlternativeMode(bool alternative)
@@ -720,7 +725,7 @@ void Session::switchToAlternativeMode(bool alternative)
void Session::handleDownloadFinished(const QString &url, const QString &filePath)
{
emit downloadFromUrlFinished(url);
addTorrent_impl(addDataFromParams(m_downloadedTorrents.take(url)), MagnetUri(), TorrentInfo::loadFromFile(filePath));
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(), TorrentInfo::loadFromFile(filePath));
Utils::Fs::forceRemove(filePath); // remove temporary file
}
@@ -785,7 +790,11 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles)
QStringList unwantedFiles;
if (torrent->hasMetadata())
unwantedFiles = torrent->absoluteFilePathsUnwanted();
#if LIBTORRENT_VERSION_NUM < 10100
m_nativeSession->remove_torrent(torrent->nativeHandle());
#else
m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_partfile);
#endif
// Remove unwanted and incomplete files
foreach (const QString &unwantedFile, unwantedFiles) {
qDebug("Removing unwanted file: %s", qPrintable(unwantedFile));
@@ -934,42 +943,14 @@ TorrentStatusReport Session::torrentStatusReport() const
return m_torrentStatusReport;
}
// source - .torrent file path/url or magnet uri (hash for preloaded torrent)
// source - .torrent file path/url or magnet uri
bool Session::addTorrent(QString source, const AddTorrentParams &params)
{
InfoHash hash = source;
if (hash.isValid() && m_loadedMetadata.contains(hash)) {
// Adding preloaded torrent
m_loadedMetadata.remove(hash);
libt::torrent_handle handle = m_nativeSession->find_torrent(hash);
--m_extraLimit;
try {
handle.auto_managed(false);
handle.pause();
}
catch (std::exception &) {}
adjustLimits();
// use common 2nd step of torrent addition
libt::add_torrent_alert *alert = new libt::add_torrent_alert(handle, libt::add_torrent_params(), libt::error_code());
m_addingTorrents.insert(hash, addDataFromParams(params));
handleAddTorrentAlert(alert);
delete alert;
return true;
MagnetUri magnetUri(source);
if (magnetUri.isValid()) {
return addTorrent_impl(params, magnetUri);
}
if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
source = Utils::Misc::bcLinkToMagnet(source);
}
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
source = "magnet:?xt=urn:btih:" + source;
}
if (Utils::Misc::isUrl(source)) {
else if (Utils::Misc::isUrl(source)) {
Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
// Launch downloader
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
@@ -978,11 +959,8 @@ bool Session::addTorrent(QString source, const AddTorrentParams &params)
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString)));
m_downloadedTorrents[handler->url()] = params;
}
else if (source.startsWith("magnet:", Qt::CaseInsensitive)) {
return addTorrent_impl(addDataFromParams(params), MagnetUri(source));
}
else {
return addTorrent_impl(addDataFromParams(params), MagnetUri(), TorrentInfo::loadFromFile(source));
return addTorrent_impl(params, MagnetUri(), TorrentInfo::loadFromFile(source));
}
return false;
@@ -992,13 +970,26 @@ bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams
{
if (!torrentInfo.isValid()) return false;
return addTorrent_impl(addDataFromParams(params), MagnetUri(), torrentInfo);
return addTorrent_impl(params, MagnetUri(), torrentInfo);
}
// Add a torrent to the BitTorrent session
bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
const TorrentInfo &torrentInfo, const QByteArray &fastresumeData)
{
if (!addData.resumed) {
// manage save path
QString defaultSavePath = this->defaultSavePath();
if (addData.savePath.isEmpty())
addData.savePath = defaultSavePath;
if (!addData.savePath.endsWith("/"))
addData.savePath += "/";
if (useAppendLabelToSavePath()) {
if ((addData.savePath == defaultSavePath) && !addData.label.isEmpty())
addData.savePath += QString("%1/").arg(addData.label);
}
}
libt::add_torrent_params p;
InfoHash hash;
std::vector<char> buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size());
@@ -1006,8 +997,29 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
bool fromMagnetUri = magnetUri.isValid();
if (fromMagnetUri) {
p = magnetUri.addTorrentParams();
hash = magnetUri.hash();
if (m_loadedMetadata.contains(hash)) {
// Adding preloaded torrent
m_loadedMetadata.remove(hash);
libt::torrent_handle handle = m_nativeSession->find_torrent(hash);
--m_extraLimit;
try {
handle.auto_managed(false);
handle.pause();
}
catch (std::exception &) {}
adjustLimits();
// use common 2nd step of torrent addition
m_addingTorrents.insert(hash, addData);
createTorrentHandle(handle);
return true;
}
p = magnetUri.addTorrentParams();
}
else if (torrentInfo.isValid()) {
// Metadata
@@ -1038,6 +1050,8 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
if (m_torrents.contains(hash)) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
return false;
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers());
torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds());
return true;
@@ -1088,15 +1102,12 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
// Add a torrent to the BitTorrent session in hidden mode
// and force it to load its metadata
bool Session::loadMetadata(const QString &magnetUri)
bool Session::loadMetadata(const MagnetUri &magnetUri)
{
Q_ASSERT(magnetUri.startsWith("magnet:", Qt::CaseInsensitive));
if (!magnetUri.isValid()) return false;
MagnetUri magnet(magnetUri);
if (!magnet.isValid()) return false;
InfoHash hash = magnet.hash();
QString name = magnet.name();
InfoHash hash = magnetUri.hash();
QString name = magnetUri.name();
// We should not add torrent if it already
// processed or adding to session
@@ -1108,7 +1119,7 @@ bool Session::loadMetadata(const QString &magnetUri)
qDebug(" -> Hash: %s", qPrintable(hash));
qDebug(" -> Name: %s", qPrintable(name));
libt::add_torrent_params p = magnet.addTorrentParams();
libt::add_torrent_params p = magnetUri.addTorrentParams();
// Flags
// Preallocation mode
@@ -1706,7 +1717,16 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent)
void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data)
{
--m_numResumeData;
writeResumeDataFile(torrent, data);
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Encoding data in parallel while doing IO saves time. Copying libtorrent::entry objects around
// isn't cheap too.
QByteArray out;
libt::bencode(std::back_inserter(out), data);
QMetaObject::invokeMethod(m_resumeDataSavingManager, "saveResumeData",
Q_ARG(QString, torrent->hash()), Q_ARG(QByteArray, out));
}
void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent)
@@ -1873,48 +1893,63 @@ void Session::startUpTorrents()
const QDir resumeDataDir(m_resumeFolderPath);
QStringList fastresumes = resumeDataDir.entryList(
QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted);
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
typedef QPair<int, QString> PrioHashPair;
typedef std::vector<PrioHashPair> PrioHashVector;
typedef std::greater<PrioHashPair> PrioHashGreater;
std::priority_queue<PrioHashPair, PrioHashVector, PrioHashGreater> torrentQueue;
// Fastresume file name format:
// <torrent_info_hash>.fastresume.<torrent_queue_position>
// E.g.:
// fc8a15a2faf2734dbb1dc5f7afdc5c9beaeb1f59.fastresume.2
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
foreach (const QString &fastresume, fastresumes) {
if (rx.indexIn(fastresume) != -1) {
PrioHashPair p = qMakePair(rx.cap(2).toInt(), rx.cap(1));
torrentQueue.push(p);
}
}
QString filePath;
Logger *const logger = Logger::instance();
qDebug("Starting up torrents");
qDebug("Priority queue size: %ld", (long)torrentQueue.size());
// Resume downloads
while (!torrentQueue.empty()) {
const int prio = torrentQueue.top().first;
const QString hash = torrentQueue.top().second;
torrentQueue.pop();
typedef struct
{
QString hash;
MagnetUri magnetUri;
AddTorrentData addTorrentData;
QByteArray data;
} TorrentResumeData;
QString fastresumePath =
resumeDataDir.absoluteFilePath(QString("%1.fastresume.%2").arg(hash).arg(prio));
auto startupTorrent = [this, logger, resumeDataDir](const TorrentResumeData &params)
{
QString filePath = resumeDataDir.filePath(QString("%1.torrent").arg(params.hash));
qDebug() << "Starting up torrent" << params.hash << "...";
if (!addTorrent_impl(params.addTorrentData, params.magnetUri, TorrentInfo::loadFromFile(filePath), params.data))
logger->addMessage(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(params.hash), Log::CRITICAL);
};
qDebug("Starting up torrents");
qDebug("Queue size: %d", fastresumes.size());
// Resume downloads
QMap<int, TorrentResumeData> queuedResumeData;
int nextQueuePosition = 1;
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (const QString &fastresumeName, fastresumes) {
if (rx.indexIn(fastresumeName) == -1) continue;
QString hash = rx.cap(1);
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
AddTorrentData resumeData;
MagnetUri magnetUri;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData, magnetUri)) {
filePath = resumeDataDir.filePath(QString("%1.torrent").arg(hash));
qDebug("Starting up torrent %s ...", qPrintable(hash));
if (!addTorrent_impl(resumeData, magnetUri, TorrentInfo::loadFromFile(filePath), data))
logger->addMessage(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(Utils::Fs::toNativePath(hash)), Log::CRITICAL);
int queuePosition;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData, queuePosition, magnetUri)) {
if (queuePosition <= nextQueuePosition) {
startupTorrent({ hash, magnetUri, resumeData, data });
if (queuePosition == nextQueuePosition) {
++nextQueuePosition;
while (queuedResumeData.contains(nextQueuePosition)) {
startupTorrent(queuedResumeData.take(nextQueuePosition));
++nextQueuePosition;
}
}
}
else {
queuedResumeData[queuePosition] = { hash, magnetUri, resumeData, data };
}
}
}
// starting up downloading torrents (queue position > 0)
foreach (const TorrentResumeData &torrentResumeData, queuedResumeData)
startupTorrent(torrentResumeData);
}
quint64 Session::getAlltimeDL() const
@@ -2062,26 +2097,18 @@ void Session::dispatchTorrentAlert(libt::alert *a)
torrent->handleAlert(a);
}
void Session::handleAddTorrentAlert(libtorrent::add_torrent_alert *p)
void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
{
Logger *const logger = Logger::instance();
if (p->error) {
qDebug("/!\\ Error: Failed to add torrent!");
QString msg = Utils::String::fromStdString(p->message());
logger->addMessage(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING);
emit addTorrentFailed(msg);
return;
}
// Magnet added for preload its metadata
if (!m_addingTorrents.contains(p->handle.info_hash())) return;
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
AddTorrentData data = m_addingTorrents.take(p->handle.info_hash());
AddTorrentData data = m_addingTorrents.take(nativeHandle.info_hash());
TorrentHandle *const torrent = new TorrentHandle(this, p->handle, data);
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, data);
m_torrents.insert(torrent->hash(), torrent);
Preferences *const pref = Preferences::instance();
Logger *const logger = Logger::instance();
bool fromMagnetUri = !torrent->hasMetadata();
@@ -2135,7 +2162,20 @@ void Session::handleAddTorrentAlert(libtorrent::add_torrent_alert *p)
emit torrentAdded(torrent);
}
void Session::handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p)
void Session::handleAddTorrentAlert(libt::add_torrent_alert *p)
{
if (p->error) {
qDebug("/!\\ Error: Failed to add torrent!");
QString msg = Utils::String::fromStdString(p->message());
Logger::instance()->addMessage(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING);
emit addTorrentFailed(msg);
}
else {
createTorrentHandle(p->handle);
}
}
void Session::handleTorrentRemovedAlert(libt::torrent_removed_alert *p)
{
if (m_loadedMetadata.contains(p->info_hash))
emit metadataLoaded(m_loadedMetadata.take(p->info_hash));
@@ -2273,7 +2313,7 @@ void Session::handleListenFailedAlert(libt::listen_failed_alert *p)
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
Logger::instance()->addMessage(
tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4.",
"e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use")
"e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use.")
.arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port()))
.arg(QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
}
@@ -2327,7 +2367,7 @@ bool readFile(const QString &path, QByteArray &buf)
return true;
}
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, MagnetUri &magnetUri)
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, int &prio, MagnetUri &magnetUri)
{
out = AddTorrentData();
out.resumed = true;
@@ -2339,6 +2379,11 @@ bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, MagnetUr
if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false;
out.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath")));
if (out.savePath.isEmpty()) {
Logger::instance()->addMessage("Empty torrent save path was loaded from .fastresume file! Using default one...", Log::WARNING);
out.savePath = Preferences::instance()->getSavePath();
}
out.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
out.label = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label"));
out.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name"));
@@ -2349,31 +2394,11 @@ bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out, MagnetUr
out.addPaused = fast.dict_find_int_value("qBt-paused");
out.addForced = fast.dict_find_int_value("qBt-forced");
prio = fast.dict_find_int_value("qBt-queuePosition");
return true;
}
bool Session::writeResumeDataFile(TorrentHandle *const torrent, const libt::entry &data)
{
const QDir resumeDataDir(m_resumeFolderPath);
QStringList filters(QString("%1.fastresume.*").arg(torrent->hash()));
const QStringList files = resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted);
foreach (const QString &file, files)
Utils::Fs::forceRemove(resumeDataDir.absoluteFilePath(file));
QString filename = QString("%1.fastresume.%2").arg(torrent->hash()).arg(torrent->queuePosition());
QString filepath = resumeDataDir.absoluteFilePath(filename);
qDebug("Saving resume data in %s", qPrintable(filepath));
QFile resumeFile(filepath);
QVector<char> out;
libt::bencode(std::back_inserter(out), data);
if (resumeFile.open(QIODevice::WriteOnly))
return (resumeFile.write(&out[0], out.size()) == out.size());
return false;
}
void torrentQueuePositionUp(const libt::torrent_handle &handle)
{
try {
@@ -2413,33 +2438,3 @@ void torrentQueuePositionBottom(const libt::torrent_handle &handle)
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
}
}
AddTorrentData Session::addDataFromParams(const AddTorrentParams &params)
{
AddTorrentData data;
data.resumed = false;
data.name = params.name;
data.label = params.label;
data.savePath = params.savePath;
data.disableTempPath = params.disableTempPath;
data.sequential = params.sequential;
data.hasSeedStatus = params.skipChecking; // do not react on 'torrent_finished_alert' when skipping
data.skipChecking = params.skipChecking;
data.addForced = params.addForced;
data.addPaused = params.addPaused;
data.filePriorities = params.filePriorities;
data.ratioLimit = params.ignoreShareRatio ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO;
// normalize save path
if (data.savePath.isEmpty()) {
data.savePath = m_defaultSavePath;
if (m_appendLabelToSavePath && !data.label.isEmpty())
data.savePath += QString("%1/").arg(data.label);
}
else if (!data.savePath.endsWith("/")) {
data.savePath += "/";
}
return data;
}

View File

@@ -38,6 +38,8 @@
#include <QWaitCondition>
#include <QNetworkConfigurationManager>
#include <libtorrent/version.hpp>
#include "base/tristatebool.h"
#include "base/types.h"
#include "torrentinfo.h"
@@ -45,13 +47,24 @@
namespace libtorrent
{
class session;
struct torrent_handle;
class entry;
struct add_torrent_params;
struct pe_settings;
struct proxy_settings;
struct session_settings;
struct session_status;
#if LIBTORRENT_VERSION_NUM < 10100
struct proxy_settings;
#else
namespace aux
{
struct proxy_settings;
}
typedef aux::proxy_settings proxy_settings;
#endif
class alert;
struct torrent_alert;
struct state_update_alert;
@@ -86,6 +99,7 @@ namespace libtorrent
struct external_ip_alert;
}
class QThread;
class QTimer;
class QStringList;
class QString;
@@ -95,6 +109,7 @@ template<typename T> class QList;
class FilterParserThread;
class BandwidthScheduler;
class Statistics;
class ResumeDataSavingManager;
typedef QPair<QString, QString> QStringPair;
@@ -114,15 +129,13 @@ namespace BitTorrent
QString name;
QString label;
QString savePath;
bool disableTempPath; // e.g. for imported torrents
bool sequential;
bool disableTempPath = false; // e.g. for imported torrents
bool sequential = false;
TriStateBool addForced;
TriStateBool addPaused;
QVector<int> filePriorities; // used if TorrentInfo is set
bool ignoreShareRatio;
bool skipChecking;
AddTorrentParams();
bool ignoreShareRatio = false;
bool skipChecking = false;
};
struct TorrentStatusReport
@@ -183,7 +196,7 @@ namespace BitTorrent
bool addTorrent(QString source, const AddTorrentParams &params = AddTorrentParams());
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params = AddTorrentParams());
bool deleteTorrent(const QString &hash, bool deleteLocalFiles = false);
bool loadMetadata(const QString &magnetUri);
bool loadMetadata(const MagnetUri &magnetUri);
bool cancelLoadMetadata(const InfoHash &hash);
void recursiveTorrentDownload(const InfoHash &hash);
@@ -313,14 +326,13 @@ namespace BitTorrent
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
void createTorrentHandle(const libtorrent::torrent_handle &nativeHandle);
void saveResumeData();
bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data);
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
AddTorrentData addDataFromParams(const AddTorrentParams &params);
// BitTorrent
libtorrent::session *m_nativeSession;
@@ -355,6 +367,9 @@ namespace BitTorrent
QPointer<BandwidthScheduler> m_bwScheduler;
// Tracker
QPointer<Tracker> m_tracker;
// fastresume data writing thread
QThread *m_ioThread;
ResumeDataSavingManager *m_resumeDataSavingManager;
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
QHash<InfoHash, TorrentHandle *> m_torrents;

View File

@@ -55,7 +55,7 @@ using namespace BitTorrent;
// name starts with a .
bool fileFilter(const std::string &f)
{
return (libt::filename(f)[0] != '.');
return !Utils::Fs::fileName(Utils::String::fromStdString(f)).startsWith('.');
}
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)

View File

@@ -40,6 +40,10 @@
#include <libtorrent/alert_types.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/magnet_uri.hpp>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/time.hpp>
#endif
#include <boost/bind.hpp>
#ifdef Q_OS_WIN
@@ -61,10 +65,31 @@ static const char QB_EXT[] = ".!qB";
namespace libt = libtorrent;
using namespace BitTorrent;
// TrackerInfo
// AddTorrentData
TrackerInfo::TrackerInfo()
: numPeers(0)
AddTorrentData::AddTorrentData()
: resumed(false)
, disableTempPath(false)
, sequential(false)
, hasSeedStatus(false)
, skipChecking(false)
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
{
}
AddTorrentData::AddTorrentData(const AddTorrentParams &params)
: resumed(false)
, name(params.name)
, label(params.label)
, savePath(params.savePath)
, disableTempPath(params.disableTempPath)
, sequential(params.sequential)
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
, skipChecking(params.skipChecking)
, addForced(params.addForced)
, addPaused(params.addPaused)
, filePriorities(params.filePriorities)
, ratioLimit(params.ignoreShareRatio ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO)
{
}
@@ -184,7 +209,11 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_pauseAfterRecheck(false)
, m_needSaveResumeData(false)
{
initialize();
Q_ASSERT(!m_savePath.isEmpty());
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
adjustActualSavePath();
if (!data.resumed) {
setSequentialDownload(data.sequential);
@@ -433,7 +462,7 @@ bool TorrentHandle::connectPeer(const PeerAddress &peerAddress)
libt::address addr = libt::address::from_string(Utils::String::toStdString(peerAddress.ip.toString()), ec);
if (ec) return false;
libt::asio::ip::tcp::endpoint ep(addr, peerAddress.port);
boost::asio::ip::tcp::endpoint ep(addr, peerAddress.port);
SAFE_CALL_BOOL(connect_peer, ep);
}
@@ -847,7 +876,7 @@ qulonglong TorrentHandle::eta() const
QVector<qreal> TorrentHandle::filesProgress() const
{
std::vector<libt::size_type> fp;
std::vector<boost::int64_t> fp;
QVector<qreal> result;
SAFE_CALL(file_progress, fp, libt::torrent_handle::piece_granularity);
@@ -1022,9 +1051,9 @@ qreal TorrentHandle::maxRatio(bool *usesGlobalRatio) const
qreal TorrentHandle::realRatio() const
{
libt::size_type upload = m_nativeStatus.all_time_upload;
boost::int64_t upload = m_nativeStatus.all_time_upload;
// special case for a seeder who lost its stats, also assume nobody will import a 99% done torrent
libt::size_type download = (m_nativeStatus.all_time_download < m_nativeStatus.total_done * 0.01) ? m_nativeStatus.total_done : m_nativeStatus.all_time_download;
boost::int64_t download = (m_nativeStatus.all_time_download < m_nativeStatus.total_done * 0.01) ? m_nativeStatus.total_done : m_nativeStatus.all_time_download;
if (download == 0)
return (upload == 0) ? 0.0 : MAX_RATIO;
@@ -1066,7 +1095,11 @@ int TorrentHandle::connectionsLimit() const
qlonglong TorrentHandle::nextAnnounce() const
{
#if LIBTORRENT_VERSION_NUM < 10100
return m_nativeStatus.next_announce.total_seconds();
#else
return libt::duration_cast<libt::seconds>(m_nativeStatus.next_announce).count();
#endif
}
void TorrentHandle::setName(const QString &name)
@@ -1143,6 +1176,8 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
std::vector<int> fp;
SAFE_GET(fp, file_priorities);
std::vector<int> pp;
SAFE_GET(pp, piece_priorities);
// Download first and last pieces first for all media files in the torrent
int nbfiles = static_cast<int>(fp.size());
@@ -1151,14 +1186,22 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
const QString ext = Utils::Fs::fileExtension(path);
if (Utils::Misc::isPreviewable(ext) && (fp[index] > 0)) {
qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first";
// Determine the priority to set
int prio = b ? 7 : fp[index];
QPair<int, int> extremities = fileExtremityPieces(index);
SAFE_CALL(piece_priority, extremities.first, prio);
SAFE_CALL(piece_priority, extremities.second, prio);
// worst case: AVI index = 1% of total file size (at the end of the file)
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
for (int i = 0; i < nNumPieces; ++i) {
pp[extremities.first + i] = prio;
pp[extremities.second - i] = prio;
}
}
}
SAFE_CALL(prioritize_pieces, pp);
}
void TorrentHandle::toggleFirstLastPiecePriority()
@@ -1432,6 +1475,7 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
resumeData["qBt-queuePosition"] = queuePosition();
m_session->handleTorrentResumeDataReady(this, resumeData);
}
@@ -1679,15 +1723,11 @@ libtorrent::torrent_handle TorrentHandle::nativeHandle() const
void TorrentHandle::updateTorrentInfo()
{
if (!hasMetadata()) return;
#if LIBTORRENT_VERSION_NUM < 10100
m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file);
}
void TorrentHandle::initialize()
{
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
adjustActualSavePath();
#else
m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file.lock());
#endif
}
bool TorrentHandle::isMoveInProgress() const

View File

@@ -38,6 +38,11 @@
#include <QHash>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/version.hpp>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/torrent_status.hpp>
#endif
#include <boost/function.hpp>
#include "base/tristatebool.h"
@@ -97,14 +102,15 @@ namespace BitTorrent
QVector<int> filePriorities;
// for resumed torrents
qreal ratioLimit;
AddTorrentData();
AddTorrentData(const AddTorrentParams &params);
};
struct TrackerInfo
{
QString lastMessage;
quint32 numPeers;
TrackerInfo();
quint32 numPeers = 0;
};
class TorrentState
@@ -344,7 +350,6 @@ namespace BitTorrent
private:
typedef boost::function<void ()> EventTrigger;
void initialize();
void updateStatus();
void updateStatus(const libtorrent::torrent_status &nativeStatus);
void updateState();

View File

@@ -43,8 +43,8 @@
namespace libt = libtorrent;
using namespace BitTorrent;
TorrentInfo::TorrentInfo(boost::intrusive_ptr<const libt::torrent_info> nativeInfo)
: m_nativeInfo(const_cast<libt::torrent_info *>(nativeInfo.get()))
TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo)
: m_nativeInfo(nativeInfo)
{
}
@@ -63,7 +63,7 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
{
error.clear();
libt::error_code ec;
TorrentInfo info(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec));
TorrentInfo info(NativePtr(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec)));
if (ec) {
error = QString::fromUtf8(ec.message().c_str());
qDebug("Cannot load .torrent file: %s", qPrintable(error));
@@ -211,13 +211,27 @@ QByteArray TorrentInfo::metadata() const
return QByteArray(m_nativeInfo->metadata().get(), m_nativeInfo->metadata_size());
}
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
{
if (pieceIndex < 0)
return QStringList();
std::vector<libtorrent::file_slice> files(
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
QStringList res;
for (const libtorrent::file_slice& s: files) {
res.append(filePath(s.file_index));
}
return res;
}
void TorrentInfo::renameFile(uint index, const QString &newPath)
{
if (!isValid()) return;
m_nativeInfo->rename_file(index, Utils::String::toStdString(newPath));
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
}
boost::intrusive_ptr<libtorrent::torrent_info> TorrentInfo::nativeInfo() const
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
{
return m_nativeInfo;
return *reinterpret_cast<const NativePtr *>(&m_nativeInfo);
}

View File

@@ -30,7 +30,9 @@
#define BITTORRENT_TORRENTINFO_H
#include <QtGlobal>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
class QString;
class QUrl;
@@ -47,7 +49,15 @@ namespace BitTorrent
class TorrentInfo
{
public:
explicit TorrentInfo(boost::intrusive_ptr<const libtorrent::torrent_info> nativeInfo = boost::intrusive_ptr<const libtorrent::torrent_info>());
#if LIBTORRENT_VERSION_NUM < 10100
typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
typedef boost::intrusive_ptr<libtorrent::torrent_info> NativePtr;
#else
typedef boost::shared_ptr<const libtorrent::torrent_info> NativeConstPtr;
typedef boost::shared_ptr<libtorrent::torrent_info> NativePtr;
#endif
explicit TorrentInfo(NativeConstPtr nativeInfo = NativeConstPtr());
TorrentInfo(const TorrentInfo &other);
static TorrentInfo loadFromFile(const QString &path, QString &error);
@@ -75,12 +85,14 @@ namespace BitTorrent
QList<TrackerEntry> trackers() const;
QList<QUrl> urlSeeds() const;
QByteArray metadata() const;
QStringList filesForPiece(int pieceIndex) const;
void renameFile(uint index, const QString &newPath);
boost::intrusive_ptr<libtorrent::torrent_info> nativeInfo() const;
NativePtr nativeInfo() const;
private:
boost::intrusive_ptr<libtorrent::torrent_info> m_nativeInfo;
NativeConstPtr m_nativeInfo;
};
}

View File

@@ -30,6 +30,10 @@
#define BITTORRENT_TRACKERENTRY_H
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/announce_entry.hpp>
#endif
class QString;

View File

@@ -195,11 +195,7 @@ void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrent
foreach (const QString &file, files) {
const QString fileAbsPath = dir.absoluteFilePath(file);
if (fileAbsPath.endsWith(".magnet")) {
QFile f(fileAbsPath);
if (f.open(QIODevice::ReadOnly)
&& !BitTorrent::MagnetUri(QString::fromLocal8Bit(f.readAll())).isValid()) {
torrents << fileAbsPath;
}
torrents << fileAbsPath;
}
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
torrents << fileAbsPath;

View File

@@ -81,6 +81,11 @@ RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data,
// Parse HTTP request message
if (m_request.headers.contains("content-length")) {
int content_length = m_request.headers["content-length"].toInt();
if (content_length < 0) {
qWarning() << Q_FUNC_INFO << "bad request: content-length negative";
return BadRequest;
}
if (content_length > static_cast<int>(m_maxContentLength)) {
qWarning() << Q_FUNC_INFO << "bad request: message too long";
return BadRequest;
@@ -92,7 +97,7 @@ RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data,
return IncompleteRequest;
}
if (!parseContent(content)) {
if ((content_length > 0) && !parseContent(content)) {
qWarning() << Q_FUNC_INFO << "message parsing error";
return BadRequest;
}

View File

@@ -52,9 +52,9 @@ Server::~Server()
}
#ifndef QT_NO_OPENSSL
void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
void Server::enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key)
{
m_certificate = certificate;
m_certificates = certificates;
m_key = key;
m_https = true;
}
@@ -62,7 +62,7 @@ void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
void Server::disableHttps()
{
m_https = false;
m_certificate.clear();
m_certificates.clear();
m_key.clear();
}
#endif
@@ -84,9 +84,13 @@ void Server::incomingConnection(int socketDescriptor)
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
#ifndef QT_NO_OPENSSL
if (m_https) {
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::AnyProtocol);
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket*>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificate);
#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)->startServerEncryption();
}
#endif

View File

@@ -54,7 +54,7 @@ namespace Http
~Server();
#ifndef QT_NO_OPENSSL
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
void enableHttps(const QList<QSslCertificate> &certificates, const QSslKey &key);
void disableHttps();
#endif
@@ -69,7 +69,7 @@ namespace Http
IRequestHandler *m_requestHandler;
#ifndef QT_NO_OPENSSL
bool m_https;
QSslCertificate m_certificate;
QList<QSslCertificate> m_certificates;
QSslKey m_key;
#endif
};

View File

@@ -12,11 +12,13 @@ namespace Log
{
enum MsgType
{
NORMAL,
INFO,
WARNING,
CRITICAL //ERROR is defined by libtorrent and results in compiler error
ALL = -1,
NORMAL = 0x1,
INFO = 0x2,
WARNING = 0x4,
CRITICAL = 0x8 //ERROR is defined by libtorrent and results in compiler error
};
Q_DECLARE_FLAGS(MsgTypes, MsgType)
struct Msg
{
@@ -36,6 +38,8 @@ namespace Log
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(Log::MsgTypes)
class Logger : public QObject
{
Q_OBJECT

View File

@@ -300,7 +300,7 @@ QUrl DNSUpdater::getRegistrationUrl(int service)
case DNS::DYNDNS:
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
case DNS::NOIP:
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
return QUrl("https://www.noip.com/remote-access");
default:
Q_ASSERT(0);
}

View File

@@ -42,7 +42,7 @@
#include "private/geoipdatabase.h"
#include "geoipmanager.h"
static const char DATABASE_URL[] = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
static const char DATABASE_URL[] = "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
static const char GEOIP_FOLDER[] = "GeoIP";
static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb";
static const int CACHE_SIZE = 1000;

View File

@@ -58,9 +58,8 @@ namespace
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
{
const int blockSize = 64; // HMAC-MD5 block size
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
if (key.length() > blockSize) // if key is longer than block size (64), reduce key length with MD5 compression
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
}
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
@@ -123,18 +122,17 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
{
const Preferences* const pref = Preferences::instance();
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
m_message = "";
m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
m_message += encodeMimeHeader("From", from, latin1);
m_message += encodeMimeHeader("Subject", subject, latin1);
m_message += encodeMimeHeader("To", to, latin1);
m_message += "MIME-Version: 1.0\r\n";
m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
m_message += "Content-Transfer-Encoding: base64\r\n";
m_message += "\r\n";
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
+ encodeMimeHeader("From", from, latin1)
+ encodeMimeHeader("Subject", subject, latin1)
+ encodeMimeHeader("To", to, latin1)
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/plain; charset=UTF-8\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "\r\n";
// Encode the body in base64
QString crlf_body = body;
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
QByteArray b = crlf_body.replace("\n", "\r\n").toUtf8().toBase64();
int ct = b.length();
for (int i = 0; i < ct; i += 78)
m_message += b.mid(i, 78);
@@ -154,10 +152,10 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
}
else {
#endif
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
m_useSsl = false;
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
m_useSsl = false;
#ifndef QT_NO_OPENSSL
}
}
#endif
}
@@ -186,7 +184,7 @@ void Smtp::readyRead()
ehlo();
}
else {
logError("Connection failed, unrecognized reply: "+line);
logError("Connection failed, unrecognized reply: " + line);
m_state = Close;
}
break;
@@ -223,7 +221,7 @@ void Smtp::readyRead()
}
else {
// Authentication failed!
logError("Authentication failed, msg: "+line);
logError("Authentication failed, msg: " + line);
m_state = Close;
}
break;
@@ -234,7 +232,7 @@ void Smtp::readyRead()
m_state = Data;
}
else {
logError("<mail from> was rejected by server, msg: "+line);
logError("<mail from> was rejected by server, msg: " + line);
m_state = Close;
}
break;
@@ -245,7 +243,7 @@ void Smtp::readyRead()
m_state = Body;
}
else {
logError("<Rcpt to> was rejected by server, msg: "+line);
logError("<Rcpt to> was rejected by server, msg: " + line);
m_state = Close;
}
break;
@@ -256,7 +254,7 @@ void Smtp::readyRead()
m_state = Quit;
}
else {
logError("<data> was rejected by server, msg: "+line);
logError("<data> was rejected by server, msg: " + line);
m_state = Close;
}
break;
@@ -268,7 +266,7 @@ void Smtp::readyRead()
m_state = Close;
}
else {
logError("Message was rejected by the server, error: "+line);
logError("Message was rejected by the server, error: " + line);
m_state = Close;
}
break;
@@ -309,9 +307,9 @@ QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTex
line += "=?utf-8?b?";
for (int i = 0; i < ct; i += 4) {
/*if (line.length() > 72) {
rv += line + "?\n\r";
line = " =?utf-8?b?";
}*/
rv += line + "?\n\r";
line = " =?utf-8?b?";
}*/
line = line + base64.mid(i, 4);
}
line += "?="; // end encoded-word atom
@@ -347,7 +345,7 @@ void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QStri
else {
// Both EHLO and HELO failed, chances are this is NOT
// a SMTP server
logError("Both EHLO and HELO failed, msg: "+line);
logError("Both EHLO and HELO failed, msg: " + line);
m_state = Close;
}
return;
@@ -362,7 +360,7 @@ void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QStri
else {
// greeting followed by extensions
m_state = EhloGreetReceived;
qDebug () << "EHLO greet received";
qDebug() << "EHLO greet received";
return;
}
}
@@ -419,7 +417,7 @@ void Smtp::authenticate()
// Skip authentication
logError("The SMTP server does not seem to support any of the authentications modes "
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
"knowing it is likely to fail... Server Auth Modes: " + auth.join("|"));
m_state = Authenticated;
// At this point the server will not send any response
// So fill the buffer with a fake one to pass the tests
@@ -450,7 +448,7 @@ void Smtp::authCramMD5(const QByteArray& challenge)
}
else {
QByteArray response = m_username.toLatin1() + ' '
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
m_socket->write(response.toBase64() + "\r\n");
m_socket->flush();
m_state = AuthSent;
@@ -470,7 +468,7 @@ void Smtp::authPlain()
auth += m_password.toLatin1();
qDebug() << "password: " << m_password.toLatin1();
// Send it
m_socket->write("auth plain "+ auth.toBase64() + "\r\n");
m_socket->write("auth plain " + auth.toBase64() + "\r\n");
m_socket->flush();
m_state = AuthSent;
}
@@ -501,3 +499,29 @@ void Smtp::logError(const QString &msg)
qDebug() << "Email Notification Error:" << msg;
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
}
QString Smtp::getCurrentDateTime() const
{
// return date & time in the format specified in RFC 2822, section 3.3
const QDateTime nowDateTime = QDateTime::currentDateTime();
const QDate nowDate = nowDateTime.date();
const QLocale eng(QLocale::English);
QString timeStr = nowDateTime.time().toString("HH:mm:ss");
QString weekDayStr = eng.dayName(nowDate.dayOfWeek(), QLocale::ShortFormat);
QString dayStr = QString::number(nowDate.day());
QString monthStr = eng.monthName(nowDate.month(), QLocale::ShortFormat);
QString yearStr = QString::number(nowDate.year());
QDateTime tmp = nowDateTime;
tmp.setTimeSpec(Qt::UTC);
int timeOffsetHour = nowDateTime.secsTo(tmp) / 3600;
int timeOffsetMin = nowDateTime.secsTo(tmp) / 60 - (60 * timeOffsetHour);
int timeOffset = timeOffsetHour * 100 + timeOffsetMin;
char buf[6] = {0};
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
QString timeOffsetStr = buf;
QString ret = weekDayStr + ", " + dayStr + " " + monthStr + " " + yearStr + " " + timeStr + " " + timeOffsetStr;
return ret;
}

View File

@@ -102,6 +102,7 @@ namespace Net
void authPlain();
void authLogin();
void logError(const QString &msg);
QString getCurrentDateTime() const;
QByteArray m_message;
#ifndef QT_NO_OPENSSL

View File

@@ -62,6 +62,8 @@
Preferences* Preferences::m_instance = 0;
static const QString LOG_FOLDER("logs");
Preferences::Preferences()
: m_randomPort(rand() % 64512 + 1024)
, dirty(false)
@@ -273,6 +275,26 @@ void Preferences::setAlternatingRowColors(bool b)
setValue("Preferences/General/AlternatingRowColors", b);
}
bool Preferences::getHideZeroValues() const
{
return value("Preferences/General/HideZeroValues", false).toBool();
}
void Preferences::setHideZeroValues(bool b)
{
setValue("Preferences/General/HideZeroValues", b);
}
int Preferences::getHideZeroComboValues() const
{
return value("Preferences/General/HideZeroComboValues", 0).toInt();
}
void Preferences::setHideZeroComboValues(int n)
{
setValue("Preferences/General/HideZeroComboValues", n);
}
bool Preferences::useRandomPort() const
{
return value("Preferences/General/UseRandomPort", false).toBool();
@@ -335,7 +357,7 @@ void Preferences::setStartMinimized(bool b)
bool Preferences::isSplashScreenDisabled() const
{
return value("Preferences/General/NoSplashScreen", false).toBool();
return value("Preferences/General/NoSplashScreen", true).toBool();
}
void Preferences::setSplashScreenDisabled(bool b)
@@ -969,7 +991,7 @@ void Preferences::setTrackersList(const QString &val)
qreal Preferences::getGlobalMaxRatio() const
{
return value("Preferences/Bittorrent/MaxRatio", -1).toDouble();
return value("Preferences/Bittorrent/MaxRatio", -1).toReal();
}
void Preferences::setGlobalMaxRatio(qreal ratio)
@@ -1054,6 +1076,100 @@ void Preferences::setExecutionLogEnabled(bool b)
setValue("Preferences/ExecutionLog/enabled", b);
}
int Preferences::executionLogMessageTypes() const
{
// as default value we need all the bits set
// -1 is considered the portable way to achieve that
return value("MainWindow/ExecutionLog/Types", -1).toInt();
}
void Preferences::setExecutionLogMessageTypes(const int &value)
{
setValue("MainWindow/ExecutionLog/Types", value);
}
// File log
bool Preferences::fileLogEnabled() const
{
return value("Application/FileLogger/Enabled", true).toBool();
}
void Preferences::setFileLogEnabled(bool enabled)
{
setValue("Application/FileLogger/Enabled", enabled);
}
QString Preferences::fileLogPath() const
{
return value("Application/FileLogger/Path", QVariant(Utils::Fs::QDesktopServicesDataLocation() + LOG_FOLDER)).toString();
}
void Preferences::setFileLogPath(const QString &path)
{
setValue("Application/FileLogger/Path", path);
}
bool Preferences::fileLogBackup() const
{
return value("Application/FileLogger/Backup", true).toBool();
}
void Preferences::setFileLogBackup(bool backup)
{
setValue("Application/FileLogger/Backup", backup);
}
bool Preferences::fileLogDeleteOld() const
{
return value("Application/FileLogger/DeleteOld", true).toBool();
}
void Preferences::setFileLogDeleteOld(bool deleteOld)
{
setValue("Application/FileLogger/DeleteOld", deleteOld);
}
int Preferences::fileLogMaxSize() const
{
int val = value("Application/FileLogger/MaxSize", 10).toInt();
if (val < 1)
return 1;
if (val > 1000)
return 1000;
return val;
}
void Preferences::setFileLogMaxSize(const int &size)
{
setValue("Application/FileLogger/MaxSize", std::min(std::max(size, 1), 1000));
}
int Preferences::fileLogAge() const
{
int val = value("Application/FileLogger/Age", 6).toInt();
if (val < 1)
return 1;
if (val > 365)
return 365;
return val;
}
void Preferences::setFileLogAge(const int &age)
{
setValue("Application/FileLogger/Age", std::min(std::max(age, 1), 365));
}
int Preferences::fileLogAgeType() const
{
int val = value("Application/FileLogger/AgeType", 1).toInt();
return (val < 0 || val > 2) ? 1 : val;
}
void Preferences::setFileLogAgeType(const int &ageType)
{
setValue("Application/FileLogger/AgeType", (ageType < 0 || ageType > 2) ? 1 : ageType);
}
// Queueing system
bool Preferences::isQueueingSystemEnabled() const
{
@@ -1310,12 +1426,12 @@ void Preferences::setAutoRunEnabled(bool enabled)
QString Preferences::getAutoRunProgram() const
{
return Utils::Fs::fromNativePath(value("AutoRun/program").toString());
return value("AutoRun/program").toString();
}
void Preferences::setAutoRunProgram(const QString &program)
{
setValue("AutoRun/program", Utils::Fs::fromNativePath(program));
setValue("AutoRun/program", program);
}
bool Preferences::shutdownWhenDownloadsComplete() const
@@ -1358,6 +1474,16 @@ void Preferences::setShutdownqBTWhenDownloadsComplete(bool shutdown)
setValue("Preferences/Downloads/AutoShutDownqBTOnCompletion", shutdown);
}
bool Preferences::dontConfirmAutoExit() const
{
return value("ShutdownConfirmDlg/DontConfirmAutoExit", false).toBool();
}
void Preferences::setDontConfirmAutoExit(bool dontConfirmAutoExit)
{
setValue("ShutdownConfirmDlg/DontConfirmAutoExit", dontConfirmAutoExit);
}
uint Preferences::diskCacheSize() const
{
uint size = value("Preferences/Downloads/DiskWriteCacheSize", 0).toUInt();
@@ -1900,7 +2026,7 @@ bool Preferences::isTorrentFileAssocSet()
CFStringRef myBundleId = CFBundleGetIdentifier(CFBundleGetMainBundle());
isSet = CFStringCompare(myBundleId, defaultHandlerId, 0) == kCFCompareEqualTo;
CFRelease(defaultHandlerId);
}
}
CFRelease(torrentId);
}
return isSet;

View File

@@ -132,6 +132,10 @@ public:
void showSpeedInTitleBar(bool show);
bool useAlternatingRowColors() const;
void setAlternatingRowColors(bool b);
bool getHideZeroValues() const;
void setHideZeroValues(bool b);
int getHideZeroComboValues() const;
void setHideZeroComboValues(int n);
bool useRandomPort() const;
void setRandomPort(bool b);
bool systrayIntegration() const;
@@ -297,6 +301,25 @@ public:
// Execution Log
bool isExecutionLogEnabled() const;
void setExecutionLogEnabled(bool b);
int executionLogMessageTypes() const;
void setExecutionLogMessageTypes(const int &value);
// File log
bool fileLogEnabled() const;
void setFileLogEnabled(bool enabled);
QString fileLogPath() const;
void setFileLogPath(const QString &path);
bool fileLogBackup() const;
void setFileLogBackup(bool backup);
bool fileLogDeleteOld() const;
void setFileLogDeleteOld(bool deleteOld);
int fileLogMaxSize() const;
void setFileLogMaxSize(const int &size);
int fileLogAge() const;
void setFileLogAge(const int &age);
int fileLogAgeType() const;
void setFileLogAgeType(const int &ageType);
// Queueing system
bool isQueueingSystemEnabled() const;
@@ -356,6 +379,8 @@ public:
void setHibernateWhenDownloadsComplete(bool hibernate);
bool shutdownqBTWhenDownloadsComplete() const;
void setShutdownqBTWhenDownloadsComplete(bool shutdown);
bool dontConfirmAutoExit() const;
void setDontConfirmAutoExit(bool dontConfirmAutoExit);
uint diskCacheSize() const;
void setDiskCacheSize(uint size);
uint diskCacheTTL() const;

View File

@@ -37,6 +37,9 @@
#include <QProcess>
#include <QSettings>
#include <QThread>
#include <QSysInfo>
#include <boost/version.hpp>
#include <libtorrent/version.hpp>
#ifdef DISABLE_GUI
#include <QCoreApplication>
@@ -392,21 +395,6 @@ bool Utils::Misc::isPreviewable(const QString& extension)
return multimedia_extensions.contains(extension.toUpper());
}
QString Utils::Misc::bcLinkToMagnet(QString bc_link)
{
QByteArray raw_bc = bc_link.toUtf8();
raw_bc = raw_bc.mid(8); // skip bc://bt/
raw_bc = QByteArray::fromBase64(raw_bc); // Decode base64
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
QStringList parts = QString(raw_bc).split("/");
if (parts.size() != 5) return QString::null;
QString filename = parts.at(1);
QString hash = parts.at(3);
QString magnet = "magnet:?xt=urn:btih:" + hash;
magnet += "&dn=" + filename;
return magnet;
}
// Take a number of seconds and return an user-friendly
// time duration like "1d 2h 10m".
QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
@@ -595,23 +583,22 @@ void Utils::Misc::openFolderSelect(const QString& absolutePath)
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (QFileInfo(path).exists()) {
QProcess proc;
QString output;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
proc.waitForFinished();
output = proc.readLine().simplified();
QString output = proc.readLine().simplified();
if (output == "dolphin.desktop" || output == "org.kde.dolphin.desktop")
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else if (output == "nautilus.desktop" || output == "org.gnome.Nautilus.desktop"
|| output == "nautilus-folder-handler.desktop")
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "caja-folder-handler.desktop")
proc.startDetached("caja", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "nemo.desktop")
proc.startDetached("nemo", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "konqueror.desktop" || output == "kfmclient_dir.desktop")
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else
else {
// "caja" manager can't pinpoint the file, see: https://github.com/qbittorrent/qBittorrent/issues/5003
openPath(path.left(path.lastIndexOf("/")));
}
}
else {
// If the item to select doesn't exist, try to open its parent
@@ -649,3 +636,35 @@ QSize Utils::Misc::smallIconSize()
return QSize(s, s);
}
#endif
QString Utils::Misc::osName()
{
// static initialization for usage in signal handler
static const QString name =
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
QString("%1 %2 %3")
.arg(QSysInfo::prettyProductName())
.arg(QSysInfo::kernelVersion())
.arg(QSysInfo::currentCpuArchitecture());
#else
"<Input OS name here>";
#endif
return name;
}
QString Utils::Misc::boostVersionString()
{
// static initialization for usage in signal handler
static const QString ver = QString("%1.%2.%3")
.arg(BOOST_VERSION / 100000)
.arg((BOOST_VERSION / 100) % 1000)
.arg(BOOST_VERSION % 100);
return ver;
}
QString Utils::Misc::libtorrentVersionString()
{
// static initialization for usage in signal handler
static const QString ver = LIBTORRENT_VERSION;
return ver;
}

View File

@@ -56,6 +56,10 @@ namespace Utils
QPoint screenCenter(QWidget *win);
QSize smallIconSize();
#endif
QString osName();
QString boostVersionString();
QString libtorrentVersionString();
int pythonVersion();
QString pythonExecutable();
QString pythonVersionComplete();
@@ -66,7 +70,6 @@ namespace Utils
QString friendlyUnit(qreal val, bool is_speed = false);
bool isPreviewable(const QString& extension);
QString bcLinkToMagnet(QString bc_link);
// Take a number of seconds and return an user-friendly
// time duration like "1d 2h 10m".
QString userFriendlyDuration(qlonglong seconds);

15
src/config.h.cmakein Normal file
View File

@@ -0,0 +1,15 @@
#cmakedefine QBT_USES_QT5
#cmakedefine QBT_USE_GUI
#ifndef QBT_USE_GUI
#define DISABLE_GUI
#define DISABLE_COUNTRIES_RESOLUTION
#endif
#cmakedefine QBT_USE_WEBUI
#ifndef QBT_USE_WEBUI
#define DISABLE_WEBUI
#endif
#cmakedefine STACKTRACE_WIN

128
src/gui/CMakeLists.txt Normal file
View File

@@ -0,0 +1,128 @@
set(CMAKE_AUTORCC True)
set(CMAKE_AUTOUIC True)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(lineedit)
add_subdirectory(properties)
add_subdirectory(powermanagement)
add_subdirectory(rss)
add_subdirectory(search)
if (UNIX AND NOT APPLE AND DBUS)
add_subdirectory(qtnotify)
include_directories(qtnotify)
list(APPEND QBT_GUI_OPTIONAL_LINK_LIBRARIES qbt_qtnotify)
endif (UNIX AND NOT APPLE AND DBUS)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}/properties
${CMAKE_CURRENT_BINARY_DIR}/rss
${CMAKE_CURRENT_BINARY_DIR}/search
lineedit/src
powermanagement
properties
rss
../app
)
set(QBT_GUI_HEADERS
about_imp.h
addnewtorrentdialog.h
advancedsettings.h
advancedsettings.h
autoexpandabledialog.h
deletionconfirmationdlg.h
downloadfromurldlg.h
executionlog.h
guiiconprovider.h
hidabletabwidget.h
ico.h
loglistwidget.h
mainwindow.h
messageboxraised.h
options_imp.h
previewlistdelegate.h
previewselect.h
scanfoldersdelegate.h
shutdownconfirm.h
speedlimitdlg.h
statsdialog.h
statusbar.h
torrentcontentfiltermodel.h
torrentcontentmodel.h
torrentcontentmodelfile.h
torrentcontentmodelfolder.h
torrentcontentmodelitem.h
torrentcontenttreeview.h
torrentcreatordlg.h
torrentimportdlg.h
torrentmodel.h
trackerlogin.h
transferlistdelegate.h
transferlistfilterswidget.h
transferlistsortmodel.h
transferlistwidget.h
updownratiodlg.h
)
set(QBT_GUI_SOURCES
addnewtorrentdialog.cpp
advancedsettings.cpp
autoexpandabledialog.cpp
executionlog.cpp
guiiconprovider.cpp
ico.cpp
loglistwidget.cpp
mainwindow.cpp
messageboxraised.cpp
options_imp.cpp
previewselect.cpp
scanfoldersdelegate.cpp
shutdownconfirm.cpp
speedlimitdlg.cpp
statsdialog.cpp
statusbar.cpp
torrentcontentfiltermodel.cpp
torrentcontentmodel.cpp
torrentcontentmodelfile.cpp
torrentcontentmodelfolder.cpp
torrentcontentmodelitem.cpp
torrentcontenttreeview.cpp
torrentcreatordlg.cpp
torrentimportdlg.cpp
torrentmodel.cpp
trackerlogin.cpp
transferlistdelegate.cpp
transferlistfilterswidget.cpp
transferlistsortmodel.cpp
transferlistwidget.cpp
updownratiodlg.cpp
)
if (WIN32 OR APPLE)
list(APPEND QBT_GUI_HEADERS programupdater.h)
list(APPEND QBT_GUI_SOURCES programupdater.cpp)
endif (WIN32 OR APPLE)
set(QBT_GUI_FORMS
mainwindow.ui
about.ui
preview.ui
login.ui
downloadfromurldlg.ui
bandwidth_limit.ui
updownratiodlg.ui
confirmdeletiondlg.ui
torrentimportdlg.ui
executionlog.ui
addnewtorrentdialog.ui
autoexpandabledialog.ui
statsdialog.ui
options.ui
torrentcreatordlg.ui
)
set(QBT_GUI_RESOURCES about.qrc)
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES} ${QBT_GUI_RESOURCES})
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine ${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base)

View File

@@ -5,4 +5,3 @@
<file>translators.html</file>
</qresource>
</RCC>

View File

@@ -7,66 +7,39 @@
<rect>
<x>0</x>
<y>0</y>
<width>504</width>
<height>320</height>
<width>545</width>
<height>295</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>504</width>
<height>320</height>
</size>
</property>
<property name="windowTitle">
<string>About qBittorrent</string>
</property>
<layout class="QVBoxLayout">
<layout class="QVBoxLayout" name="aboutDlgLayout">
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="titleHBoxLayout">
<item>
<widget class="QLabel" name="logo">
<property name="minimumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/skin/qbittorrent32.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lb_name">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;h3&gt;&lt;b&gt;qBittorrent&lt;/b&gt;&lt;/h3&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
<string notr="true">qBittorrent</string>
</property>
</widget>
</item>
<item>
<spacer>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -78,16 +51,13 @@
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab6">
<widget class="QWidget" name="aboutTab">
<attribute name="title">
<string>About</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="aboutTabLayout">
<item row="0" column="0">
<widget class="QLabel" name="mascot_lbl">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/skin/mascot.png</pixmap>
</property>
@@ -101,18 +71,14 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
@@ -123,250 +89,182 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab5">
<widget class="QWidget" name="authorTab">
<attribute name="title">
<string>Author</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<layout class="QVBoxLayout" name="authorTabLayout">
<item>
<widget class="QFrame" name="te_authors">
<layout class="QGridLayout" name="gridLayout">
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Current maintainer</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Greece</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_19">
<property name="text">
<string notr="true">&lt;a href=&quot;mailto:sledgehammer999@qbittorrent.org&quot;&gt;sledgehammer999@qbittorrent.org&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Nationality:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<widget class="QLabel" name="label_16">
<property name="text">
<string>E-mail:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_17">
<property name="text">
<string notr="true">Sledgehammer999</string>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Original author</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>France</string>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>France</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">Christophe Dumez</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string notr="true">chris@qbittorrent.org</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>E-mail:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Country:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">Christophe Dumez</string>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string notr="true">&lt;a href=&quot;mailto:chris@qbittorrent.org&quot;&gt;chris@qbittorrent.org&lt;/a&gt;</string>
</property>
<property name="lineWidth">
<number>0</number>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="2" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Greece</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_19">
<property name="text">
<string notr="true">sledgehammer999@qbittorrent.org</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Country:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_17">
<property name="text">
<string notr="true">Sledgehammer999</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="font">
<font>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>E-mail:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Current maintainer</string>
<string>Name:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_20">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Original author</string>
<string>E-mail:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Nationality:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab4">
<widget class="QWidget" name="thanksTab">
<attribute name="title">
<string>Thanks to</string>
<string>Special Thanks</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
<layout class="QVBoxLayout" name="thanksTabLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="te_thanks">
@@ -377,13 +275,22 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab3">
<widget class="QWidget" name="translationTab">
<attribute name="title">
<string>Translation</string>
<string>Translators</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
<layout class="QVBoxLayout" name="translationTabLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="te_translation">
@@ -394,102 +301,40 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab2">
<widget class="QWidget" name="licenseTab">
<attribute name="title">
<string>License</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
<layout class="QVBoxLayout" name="licenseTabLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="te_license"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab1">
<widget class="QWidget" name="librariesTab">
<attribute name="title">
<string>Libraries</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="librariesTabLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>This version of qBittorrent was built against the following libraries:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
<string>qBittorrent was built with the following libraries:</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string notr="true">Qt:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string notr="true">Boost:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_11">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_13">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string notr="true">Libtorrent:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
@@ -497,14 +342,108 @@
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="2">
<widget class="QLabel" name="label_13">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string notr="true">Qt:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_9">
<property name="text">
<string notr="true">Libtorrent:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string notr="true">Boost:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_11">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_12">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>

View File

@@ -33,67 +33,67 @@
#include "ui_about.h"
#include <QFile>
#include <QtGlobal>
#include <libtorrent/version.hpp>
#include <boost/version.hpp>
#include "base/utils/misc.h"
#include "base/unicodestrings.h"
class about : public QDialog, private Ui::AboutDlg{
Q_OBJECT
class about: public QDialog, private Ui::AboutDlg
{
Q_OBJECT
public:
~about() {
qDebug("Deleting about dlg");
}
public:
about(QWidget *parent) : QDialog(parent)
{
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
about(QWidget *parent): QDialog(parent) {
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
// About
QString aboutText =
QString::fromUtf8("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\"><html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head><body style=\" font-size:11pt; font-weight:400; font-style:normal;\"><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
tr("An advanced BitTorrent client programmed in <nobr>C++</nobr>, based on Qt toolkit and libtorrent-rasterbar.") +
QString::fromUtf8(" <br /><br />") +
trUtf8("Copyright %1 2006-2016 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT)) +
QString::fromUtf8("<br /><br />") +
tr("Home Page: ") +
QString::fromUtf8("<a href=\"http://www.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://www.qbittorrent.org</span></a></p><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
tr("Bug Tracker: ") +
QString::fromUtf8("<a href=\"http://bugs.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://bugs.qbittorrent.org</span></a><br />") +
tr("Forum: ") +
QString::fromUtf8(
"<a href=\"http://forum.qbittorrent.org\"><span style=\" text-decoration: underline; color:#0000ff;\">http://forum.qbittorrent.org</span></a></p><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">") +
tr("IRC: #qbittorrent on Freenode") +
QString::fromUtf8(
"</p></body></html>");
lb_about->setText(aboutText);
// Set icons
logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/skin/qbittorrent22.png")));
//Title
lb_name->setText(QString::fromUtf8("<b><h1>qBittorrent")+QString::fromUtf8(" " VERSION"</h1></b>"));
// Thanks
QFile thanksfile(":/thanks.html");
if (thanksfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_thanks->setHtml(QString::fromUtf8(thanksfile.readAll().constData()));
thanksfile.close();
}
// Translation
QFile translatorsfile(":/translators.html");
if (translatorsfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_translation->setHtml(QString::fromUtf8(translatorsfile.readAll().constData()));
translatorsfile.close();
}
// License
QFile licensefile(":/gpl.html");
if (licensefile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_license->setHtml(QString::fromUtf8(licensefile.readAll().constData()));
licensefile.close();
}
// Libraries
label_11->setText(QT_VERSION_STR);
label_12->setText(LIBTORRENT_VERSION);
label_13->setText(QString::number(BOOST_VERSION / 100000) + "." + QString::number((BOOST_VERSION / 100) % 1000) + "." + QString::number(BOOST_VERSION % 100));
show();
// Title
lb_name->setText("<b><h2>qBittorrent " VERSION "</h2></b>");
// About
QString aboutText = QString(
"<p style=\"white-space: pre-wrap;\">"
"%1\n\n"
"%2\n\n"
"<table>"
"<tr><td>%3</td><td><a href=\"http://www.qbittorrent.org\">http://www.qbittorrent.org</a></td></tr>"
"<tr><td>%4</td><td><a href=\"http://forum.qbittorrent.org\">http://forum.qbittorrent.org</a></td></tr>"
"<tr><td>%5</td><td><a href=\"http://bugs.qbittorrent.org\">http://bugs.qbittorrent.org</a></td></tr>"
"</table>"
"</p>")
.arg(tr("An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar."))
.arg(tr("Copyright %1 2006-2016 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT)))
.arg(tr("Home Page:"))
.arg(tr("Forum:"))
.arg(tr("Bug Tracker:"));
lb_about->setText(aboutText);
// Thanks
QFile thanksfile(":/thanks.html");
if (thanksfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_thanks->setHtml(QString::fromUtf8(thanksfile.readAll().constData()));
thanksfile.close();
}
// Translation
QFile translatorsfile(":/translators.html");
if (translatorsfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_translation->setHtml(QString::fromUtf8(translatorsfile.readAll().constData()));
translatorsfile.close();
}
// License
QFile licensefile(":/gpl.html");
if (licensefile.open(QIODevice::ReadOnly | QIODevice::Text)) {
te_license->setHtml(QString::fromUtf8(licensefile.readAll().constData()));
licensefile.close();
}
// Libraries
label_11->setText(QT_VERSION_STR);
label_12->setText(Utils::Misc::libtorrentVersionString());
label_13->setText(Utils::Misc::boostVersionString());
show();
}
};

View File

@@ -133,15 +133,6 @@ void AddNewTorrentDialog::saveState()
void AddNewTorrentDialog::show(QString source, QWidget *parent)
{
if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
source = Utils::Misc::bcLinkToMagnet(source);
}
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
source = "magnet:?xt=urn:btih:" + source;
}
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
if (Utils::Misc::isUrl(source)) {
@@ -153,8 +144,9 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
}
else {
bool ok = false;
if (source.startsWith("magnet:", Qt::CaseInsensitive))
ok = dlg->loadMagnet(source);
BitTorrent::MagnetUri magnetUri(source);
if (magnetUri.isValid())
ok = dlg->loadMagnet(magnetUri);
else
ok = dlg->loadTorrent(source);
@@ -165,18 +157,24 @@ void AddNewTorrentDialog::show(QString source, QWidget *parent)
}
}
bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path)
bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
{
if (torrent_path.startsWith("file://", Qt::CaseInsensitive))
m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile();
if (torrentPath.startsWith("file://", Qt::CaseInsensitive))
m_filePath = QUrl::fromEncoded(torrentPath.toLocal8Bit()).toLocalFile();
else
m_filePath = torrent_path;
m_filePath = torrentPath;
if (!QFile::exists(m_filePath)) {
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
return false;
}
QFileInfo fileinfo(m_filePath);
if (!fileinfo.isReadable()) {
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file cannot be read from the disk. Probably you don't have enough permissions."));
return false;
}
m_hasMetadata = true;
QString error;
m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(m_filePath, error);
@@ -191,9 +189,14 @@ bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path)
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash);
if (torrent) {
torrent->addTrackers(m_torrentInfo.trackers());
torrent->addUrlSeeds(m_torrentInfo.urlSeeds());
MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers were merged."), QMessageBox::Ok);
if (torrent->isPrivate() || m_torrentInfo.isPrivate()) {
MessageBoxRaised::critical(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers weren't merged because it is a private torrent."), QMessageBox::Ok);
}
else {
torrent->addTrackers(m_torrentInfo.trackers());
torrent->addUrlSeeds(m_torrentInfo.urlSeeds());
MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers were merged."), QMessageBox::Ok);
}
}
else {
MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding state."), QMessageBox::Ok);
@@ -206,22 +209,26 @@ bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path)
return true;
}
bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri)
bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
{
BitTorrent::MagnetUri magnet(magnet_uri);
if (!magnet.isValid()) {
if (!magnetUri.isValid()) {
MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized"));
return false;
}
m_hash = magnet.hash();
m_hash = magnetUri.hash();
// Prevent showing the dialog if download is already present
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash);
if (torrent) {
torrent->addTrackers(magnet.trackers());
torrent->addUrlSeeds(magnet.urlSeeds());
MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Trackers were merged."), QMessageBox::Ok);
if (torrent->isPrivate()) {
MessageBoxRaised::critical(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers weren't merged because it is a private torrent."), QMessageBox::Ok);
}
else {
torrent->addTrackers(magnetUri.trackers());
torrent->addUrlSeeds(magnetUri.urlSeeds());
MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Trackers were merged."), QMessageBox::Ok);
}
}
else {
MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding."), QMessageBox::Ok);
@@ -232,14 +239,14 @@ bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri)
connect(BitTorrent::Session::instance(), SIGNAL(metadataLoaded(BitTorrent::TorrentInfo)), SLOT(updateMetadata(BitTorrent::TorrentInfo)));
// Set dialog title
QString torrent_name = magnet.name();
QString torrent_name = magnetUri.name();
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
setupTreeview();
// Set dialog position
setdialogPosition();
BitTorrent::Session::instance()->loadMetadata(magnet_uri);
BitTorrent::Session::instance()->loadMetadata(magnetUri);
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
ui->lblhash->setText(m_hash);
@@ -259,28 +266,28 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
void AddNewTorrentDialog::showAdvancedSettings(bool show)
{
const int minimumW = minimumWidth();
setMinimumWidth(width()); // to remain the same width
if (show) {
ui->adv_button->setText(QString::fromUtf8(C_UP));
ui->settings_group->setVisible(true);
ui->info_group->setVisible(true);
ui->infoGroup->setVisible(true);
if (m_hasMetadata && (m_torrentInfo.filesCount() > 1)) {
ui->content_tree->setVisible(true);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
}
else {
ui->content_tree->setVisible(false);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
}
static_cast<QVBoxLayout*>(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button);
}
else {
ui->adv_button->setText(QString::fromUtf8(C_DOWN));
ui->settings_group->setVisible(false);
ui->info_group->setVisible(false);
ui->infoGroup->setVisible(false);
ui->buttonsHLayout->insertWidget(0, layout()->takeAt(layout()->indexOf(ui->never_show_cb) + 1)->widget());
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
}
relayout();
adjustSize();
setMinimumWidth(minimumW);
}
void AddNewTorrentDialog::saveSavePathHistory() const
@@ -341,7 +348,7 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable."));
size_string += " (";
size_string += tr("Free disk space: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
size_string += tr("Free space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
ui->save_path_combo->itemData(
ui->save_path_combo->currentIndex()).toString())));
size_string += ")";
@@ -353,7 +360,6 @@ void AddNewTorrentDialog::onSavePathChanged(int index)
// Toggle default save path setting checkbox visibility
ui->default_save_path_cb->setChecked(false);
ui->default_save_path_cb->setVisible(QDir(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()) != QDir(Preferences::instance()->getSavePath()));
relayout();
// Remember index
m_oldIndex = index;
@@ -410,15 +416,6 @@ void AddNewTorrentDialog::browseButton_clicked()
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
}
void AddNewTorrentDialog::relayout()
{
qApp->processEvents();
int min_width = minimumWidth();
setMinimumWidth(width());
adjustSize();
setMinimumWidth(min_width);
}
void AddNewTorrentDialog::renameSelectedFile()
{
const QModelIndexList selectedIndexes = ui->content_tree->selectionModel()->selectedRows(0);
@@ -483,6 +480,10 @@ void AddNewTorrentDialog::renameSelectedFile()
path_items.removeLast();
path_items << new_name_last;
QString new_path = path_items.join("/");
if (Utils::Fs::sameFileNames(old_path, new_path)) {
qDebug("Name did not change");
return;
}
if (!new_path.endsWith("/")) new_path += "/";
// Check for overwriting
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
@@ -673,7 +674,7 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
void AddNewTorrentDialog::setupTreeview()
{
if (!m_hasMetadata) {
ui->comment_lbl->setText(tr("Not Available", "This comment is unavailable"));
setCommentText(tr("Not Available", "This comment is unavailable"));
ui->date_lbl->setText(tr("Not Available", "This date is unavailable"));
}
else {
@@ -681,15 +682,14 @@ void AddNewTorrentDialog::setupTreeview()
setWindowTitle(m_torrentInfo.name());
// Set torrent information
ui->comment_lbl->setText(Utils::Misc::parseHtmlLinks(m_torrentInfo.comment()));
ui->date_lbl->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleLongDate) : tr("Not available"));
setCommentText(Utils::Misc::parseHtmlLinks(m_torrentInfo.comment()));
ui->date_lbl->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleShortDate) : tr("Not available"));
// Prepare content tree
if (m_torrentInfo.filesCount() > 1) {
m_contentModel = new TorrentContentFilterModel(this);
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
ui->content_tree->setModel(m_contentModel);
ui->content_tree->hideColumn(PROGRESS);
m_contentDelegate = new PropListDelegate();
ui->content_tree->setItemDelegate(m_contentDelegate);
connect(ui->content_tree, SIGNAL(clicked(const QModelIndex &)), ui->content_tree, SLOT(edit(const QModelIndex &)));
@@ -700,6 +700,10 @@ void AddNewTorrentDialog::setupTreeview()
if (!m_headerState.isEmpty())
ui->content_tree->header()->restoreState(m_headerState);
// Hide useless columns after loading the header state
ui->content_tree->hideColumn(PROGRESS);
ui->content_tree->hideColumn(REMAINING);
// Expand root folder
ui->content_tree->setExpanded(m_contentModel->index(0, 0), true);
}
@@ -726,7 +730,7 @@ void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString
void AddNewTorrentDialog::handleRedirectedToMagnet(const QString &url, const QString &magnetUri)
{
Q_UNUSED(url)
if (loadMagnet(magnetUri))
if (loadMagnet(BitTorrent::MagnetUri(magnetUri)))
open();
else
this->deleteLater();
@@ -740,3 +744,14 @@ void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QStri
else
this->deleteLater();
}
void AddNewTorrentDialog::setCommentText(const QString &str) const
{
ui->commentLabel->setText(str);
// workaround for the additional space introduced by QScrollArea
int lineHeight = ui->commentLabel->fontMetrics().lineSpacing();
int lines = 1 + str.count("\n");
int height = lineHeight * lines;
ui->scrollArea->setMaximumHeight(height);
}

View File

@@ -38,11 +38,15 @@
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/torrentinfo.h"
QT_BEGIN_NAMESPACE
namespace Ui {
namespace BitTorrent
{
class MagnetUri;
}
namespace Ui
{
class AddNewTorrentDialog;
}
QT_END_NAMESPACE
class TorrentContentFilterModel;
class PropListDelegate;
@@ -64,7 +68,6 @@ private slots:
void displayContentTreeMenu(const QPoint&);
void updateDiskSpaceLabel();
void onSavePathChanged(int);
void relayout();
void renameSelectedFile();
void setdialogPosition();
void updateMetadata(const BitTorrent::TorrentInfo &info);
@@ -79,8 +82,8 @@ protected slots:
private:
explicit AddNewTorrentDialog(QWidget *parent = 0);
bool loadTorrent(const QString& torrent_path);
bool loadMagnet(const QString& magnet_uri);
bool loadTorrent(const QString &torrentPath);
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
void loadSavePathHistory();
void saveSavePathHistory() const;
int indexOfSavePath(const QString& save_path);
@@ -89,6 +92,7 @@ private:
void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
void setupTreeview();
void setCommentText(const QString &str) const;
private:
Ui::AddNewTorrentDialog *ui;

View File

@@ -10,23 +10,11 @@
<height>590</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="AddNewTorrentDialogLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Save as</string>
<string>Save at</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
@@ -34,21 +22,18 @@
<item>
<widget class="QComboBox" name="save_path_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browse...</string>
</property>
@@ -88,28 +73,37 @@
<property name="title">
<string>Torrent settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1">
<item row="0" column="0">
<widget class="QCheckBox" name="start_torrent_cb">
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="2">
<widget class="QCheckBox" name="defaultLabel">
<property name="text">
<string>Start torrent</string>
</property>
<property name="checked">
<bool>true</bool>
<string>Set as default label</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="0">
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Label:</string>
<string>Set label:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item>
<widget class="QComboBox" name="label_combo">
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
@@ -120,6 +114,16 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="start_torrent_cb">
<property name="text">
<string>Start torrent</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="skip_check_cb">
<property name="text">
@@ -127,24 +131,43 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="defaultLabel">
<property name="text">
<string>Set as default label</string>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="info_group">
<widget class="QGroupBox" name="infoGroup">
<property name="title">
<string>Torrent Information</string>
<string>Torrent information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="infoGroupLayout">
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0" columnstretch="0,1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@@ -153,66 +176,95 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="size_lbl">
<property name="text">
<string notr="true">xx GB (xx GB available)</string>
</property>
</widget>
<widget class="QLabel" name="size_lbl"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Comment:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="comment_lbl">
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="date_lbl">
<property name="text">
<string notr="true">02/03/2012 20:30</string>
</property>
</widget>
<item row="1" column="1">
<widget class="QLabel" name="date_lbl"/>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Info Hash:</string>
<string>Hash:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="2" column="1">
<widget class="QLabel" name="lblhash">
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>299</width>
<height>73</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="commentLabel">
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
@@ -236,7 +288,7 @@
<item>
<widget class="QProgressBar" name="progMetaLoading">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -256,33 +308,10 @@
</widget>
</item>
<item>
<widget class="QLabel" name="lblMetaLoading">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<widget class="QLabel" name="lblMetaLoading"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>

View File

@@ -0,0 +1,324 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 qBittorrent project
*
* 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 "advancedsettings.h"
#include <QFont>
#include <QHeaderView>
#include <QHostAddress>
#include <QNetworkInterface>
#include "base/preferences.h"
enum AdvSettingsCols
{
PROPERTY,
VALUE,
COL_COUNT
};
enum AdvSettingsRows
{
// qBittorrent section
QBITTORRENT_HEADER,
// network interface
NETWORK_IFACE,
NETWORK_LISTEN_IPV6,
// behavior
SAVE_RESUME_DATA_INTERVAL,
CONFIRM_RECHECK_TORRENT,
RECHECK_COMPLETED,
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
UPDATE_CHECK,
#endif
// UI related
LIST_REFRESH,
RESOLVE_HOSTS,
RESOLVE_COUNTRIES,
PROGRAM_NOTIFICATIONS,
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
USE_ICON_THEME,
#endif
// libtorrent section
LIBTORRENT_HEADER,
// cache
DISK_CACHE,
DISK_CACHE_TTL,
OS_CACHE,
// ports
MAX_HALF_OPEN,
OUTGOING_PORT_MIN,
OUTGOING_PORT_MAX,
// embedded tracker
TRACKER_STATUS,
TRACKER_PORT,
// seeding
SUPER_SEEDING,
// tracker
TRACKER_EXCHANGE,
ANNOUNCE_ALL_TRACKERS,
NETWORK_ADDRESS,
ROW_COUNT
};
AdvancedSettings::AdvancedSettings(QWidget *parent)
: QTableWidget(parent)
{
// column
setColumnCount(COL_COUNT);
QStringList header = { tr("Setting"), tr("Value", "Value set for this setting") };
setHorizontalHeaderLabels(header);
// row
setRowCount(ROW_COUNT);
verticalHeader()->setVisible(false);
// etc.
setAlternatingRowColors(true);
setSelectionMode(QAbstractItemView::NoSelection);
setEditTriggers(QAbstractItemView::NoEditTriggers);
// Signals
connect(&spin_cache, SIGNAL(valueChanged(int)), SLOT(updateCacheSpinSuffix(int)));
// Load settings
loadAdvancedSettings();
resizeColumnToContents(0);
horizontalHeader()->setStretchLastSection(true);
}
void AdvancedSettings::saveAdvancedSettings()
{
Preferences* const pref = Preferences::instance();
// Disk write cache
pref->setDiskCacheSize(spin_cache.value());
pref->setDiskCacheTTL(spin_cache_ttl.value());
// Enable OS cache
pref->setOsCache(cb_os_cache.isChecked());
// Save resume data interval
pref->setSaveResumeDataInterval(spin_save_resume_data_interval.value());
// Outgoing ports
pref->setOutgoingPortsMin(outgoing_ports_min.value());
pref->setOutgoingPortsMax(outgoing_ports_max.value());
// Recheck torrents on completion
pref->recheckTorrentsOnCompletion(cb_recheck_completed.isChecked());
// Transfer list refresh interval
pref->setRefreshInterval(spin_list_refresh.value());
// Peer resolution
pref->resolvePeerCountries(cb_resolve_countries.isChecked());
pref->resolvePeerHostNames(cb_resolve_hosts.isChecked());
// Max Half-Open connections
pref->setMaxHalfOpenConnections(spin_maxhalfopen.value());
// Super seeding
pref->enableSuperSeeding(cb_super_seeding.isChecked());
// Network interface
if (combo_iface.currentIndex() == 0) {
// All interfaces (default)
pref->setNetworkInterface(QString::null);
pref->setNetworkInterfaceName(QString::null);
}
else {
pref->setNetworkInterface(combo_iface.itemData(combo_iface.currentIndex()).toString());
pref->setNetworkInterfaceName(combo_iface.currentText());
}
// Listen on IPv6 address
pref->setListenIPv6(cb_listen_ipv6.isChecked());
// Network address
QHostAddress addr(txt_network_address.text().trimmed());
if (addr.isNull())
pref->setNetworkAddress("");
else
pref->setNetworkAddress(addr.toString());
// Program notification
pref->useProgramNotification(cb_program_notifications.isChecked());
// Tracker
pref->setTrackerEnabled(cb_tracker_status.isChecked());
pref->setTrackerPort(spin_tracker_port.value());
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
pref->setUpdateCheckEnabled(cb_update_check.isChecked());
#endif
// Icon theme
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
pref->useSystemIconTheme(cb_use_icon_theme.isChecked());
#endif
pref->setConfirmTorrentRecheck(cb_confirm_torrent_recheck.isChecked());
// Tracker exchange
pref->setTrackerExchangeEnabled(cb_enable_tracker_ext.isChecked());
pref->setAnnounceToAllTrackers(cb_announce_all_trackers.isChecked());
}
void AdvancedSettings::updateCacheSpinSuffix(int value)
{
if (value <= 0)
spin_cache.setSuffix(tr(" (auto)"));
else
spin_cache.setSuffix(tr(" MiB"));
}
void AdvancedSettings::loadAdvancedSettings()
{
const Preferences* const pref = Preferences::instance();
// add section headers
QFont boldFont;
boldFont.setBold(true);
addRow(QBITTORRENT_HEADER, tr("qBittorrent Section"), &labelQbtLink);
item(QBITTORRENT_HEADER, PROPERTY)->setFont(boldFont);
labelQbtLink.setText(QString("<a href=\"%1\">%2</a>").arg("https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent#Advanced").arg(tr("Open documentation")));
labelQbtLink.setOpenExternalLinks(true);
addRow(LIBTORRENT_HEADER, tr("libtorrent Section"), &labelLibtorrentLink);
item(LIBTORRENT_HEADER, PROPERTY)->setFont(boldFont);
labelLibtorrentLink.setText(QString("<a href=\"%1\">%2</a>").arg("http://www.libtorrent.org/reference.html").arg(tr("Open documentation")));
labelLibtorrentLink.setOpenExternalLinks(true);
// Disk write cache
spin_cache.setMinimum(0);
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
// These macros may not be available on compilers other than MSVC and GCC
#if defined(__x86_64__) || defined(_M_X64)
spin_cache.setMaximum(4096);
#else
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
spin_cache.setMaximum(1536);
#endif
spin_cache.setValue(pref->diskCacheSize());
updateCacheSpinSuffix(spin_cache.value());
addRow(DISK_CACHE, tr("Disk write cache size"), &spin_cache);
// Disk cache expiry
spin_cache_ttl.setMinimum(15);
spin_cache_ttl.setMaximum(600);
spin_cache_ttl.setValue(pref->diskCacheTTL());
spin_cache_ttl.setSuffix(tr(" s", " seconds"));
addRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spin_cache_ttl);
// Enable OS cache
cb_os_cache.setChecked(pref->osCache());
addRow(OS_CACHE, tr("Enable OS cache"), &cb_os_cache);
// Save resume data interval
spin_save_resume_data_interval.setMinimum(1);
spin_save_resume_data_interval.setMaximum(1440);
spin_save_resume_data_interval.setValue(pref->saveResumeDataInterval());
spin_save_resume_data_interval.setSuffix(tr(" m", " minutes"));
addRow(SAVE_RESUME_DATA_INTERVAL, tr("Save resume data interval", "How often the fastresume file is saved."), &spin_save_resume_data_interval);
// Outgoing port Min
outgoing_ports_min.setMinimum(0);
outgoing_ports_min.setMaximum(65535);
outgoing_ports_min.setValue(pref->outgoingPortsMin());
addRow(OUTGOING_PORT_MIN, tr("Outgoing ports (Min) [0: Disabled]"), &outgoing_ports_min);
// Outgoing port Min
outgoing_ports_max.setMinimum(0);
outgoing_ports_max.setMaximum(65535);
outgoing_ports_max.setValue(pref->outgoingPortsMax());
addRow(OUTGOING_PORT_MAX, tr("Outgoing ports (Max) [0: Disabled]"), &outgoing_ports_max);
// Recheck completed torrents
cb_recheck_completed.setChecked(pref->recheckTorrentsOnCompletion());
addRow(RECHECK_COMPLETED, tr("Recheck torrents on completion"), &cb_recheck_completed);
// Transfer list refresh interval
spin_list_refresh.setMinimum(30);
spin_list_refresh.setMaximum(99999);
spin_list_refresh.setValue(pref->getRefreshInterval());
spin_list_refresh.setSuffix(tr(" ms", " milliseconds"));
addRow(LIST_REFRESH, tr("Transfer list refresh interval"), &spin_list_refresh);
// Resolve Peer countries
cb_resolve_countries.setChecked(pref->resolvePeerCountries());
addRow(RESOLVE_COUNTRIES, tr("Resolve peer countries (GeoIP)"), &cb_resolve_countries);
// Resolve peer hosts
cb_resolve_hosts.setChecked(pref->resolvePeerHostNames());
addRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &cb_resolve_hosts);
// Max Half Open connections
spin_maxhalfopen.setMinimum(0);
spin_maxhalfopen.setMaximum(99999);
spin_maxhalfopen.setValue(pref->getMaxHalfOpenConnections());
addRow(MAX_HALF_OPEN, tr("Maximum number of half-open connections [0: Unlimited]"), &spin_maxhalfopen);
// Super seeding
cb_super_seeding.setChecked(pref->isSuperSeedingEnabled());
addRow(SUPER_SEEDING, tr("Strict super seeding"), &cb_super_seeding);
// Network interface
combo_iface.addItem(tr("Any interface", "i.e. Any network interface"));
const QString current_iface = pref->getNetworkInterface();
bool interface_exists = current_iface.isEmpty();
int i = 1;
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
combo_iface.addItem(iface.humanReadableName(), iface.name());
if (!current_iface.isEmpty() && (iface.name() == current_iface)) {
combo_iface.setCurrentIndex(i);
interface_exists = true;
}
++i;
}
// Saved interface does not exist, show it anyway
if (!interface_exists) {
combo_iface.addItem(pref->getNetworkInterfaceName(), current_iface);
combo_iface.setCurrentIndex(i);
}
addRow(NETWORK_IFACE, tr("Network Interface (requires restart)"), &combo_iface);
// Listen on IPv6 address
cb_listen_ipv6.setChecked(pref->getListenIPv6());
addRow(NETWORK_LISTEN_IPV6, tr("Listen on IPv6 address (requires restart)"), &cb_listen_ipv6);
// Network address
txt_network_address.setText(pref->getNetworkAddress());
addRow(NETWORK_ADDRESS, tr("IP Address to report to trackers (requires restart)"), &txt_network_address);
// Program notifications
cb_program_notifications.setChecked(pref->useProgramNotification());
addRow(PROGRAM_NOTIFICATIONS, tr("Display program on-screen notifications"), &cb_program_notifications);
// Tracker State
cb_tracker_status.setChecked(pref->isTrackerEnabled());
addRow(TRACKER_STATUS, tr("Enable embedded tracker"), &cb_tracker_status);
// Tracker port
spin_tracker_port.setMinimum(1);
spin_tracker_port.setMaximum(65535);
spin_tracker_port.setValue(pref->getTrackerPort());
addRow(TRACKER_PORT, tr("Embedded tracker port"), &spin_tracker_port);
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
cb_update_check.setChecked(pref->isUpdateCheckEnabled());
addRow(UPDATE_CHECK, tr("Check for software updates"), &cb_update_check);
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
cb_use_icon_theme.setChecked(pref->useSystemIconTheme());
addRow(USE_ICON_THEME, tr("Use system icon theme"), &cb_use_icon_theme);
#endif
// Torrent recheck confirmation
cb_confirm_torrent_recheck.setChecked(pref->confirmTorrentRecheck());
addRow(CONFIRM_RECHECK_TORRENT, tr("Confirm torrent recheck"), &cb_confirm_torrent_recheck);
// Tracker exchange
cb_enable_tracker_ext.setChecked(pref->trackerExchangeEnabled());
addRow(TRACKER_EXCHANGE, tr("Exchange trackers with other peers"), &cb_enable_tracker_ext);
// Announce to all trackers
cb_announce_all_trackers.setChecked(pref->announceToAllTrackers());
addRow(ANNOUNCE_ALL_TRACKERS, tr("Always announce to all trackers"), &cb_announce_all_trackers);
}
template <typename T>
void AdvancedSettings::addRow(int row, const QString &rowText, T* widget)
{
setItem(row, PROPERTY, new QTableWidgetItem(rowText));
setCellWidget(row, VALUE, widget);
if (std::is_same<T, QCheckBox>::value)
connect(widget, SIGNAL(stateChanged(int)), SIGNAL(settingsChanged()));
else if (std::is_same<T, QSpinBox>::value)
connect(widget, SIGNAL(valueChanged(int)), SIGNAL(settingsChanged()));
else if (std::is_same<T, QComboBox>::value)
connect(widget, SIGNAL(currentIndexChanged(int)), SIGNAL(settingsChanged()));
else if (std::is_same<T, QLineEdit>::value)
connect(widget, SIGNAL(textChanged(QString)), SIGNAL(settingsChanged()));
}

View File

@@ -1,325 +1,78 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015
*
* 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 ADVANCEDSETTINGS_H
#define ADVANCEDSETTINGS_H
#include <QTableWidget>
#include <QHeaderView>
#include <QLabel>
#include <QSpinBox>
#include <QHostAddress>
#include <QCheckBox>
#include <QLineEdit>
#include <QComboBox>
#include <QNetworkInterface>
#include <QTableWidget>
#include "base/preferences.h"
enum AdvSettingsCols
{
PROPERTY,
VALUE
};
enum AdvSettingsRows
{
DISK_CACHE,
DISK_CACHE_TTL,
OS_CACHE,
SAVE_RESUME_DATA_INTERVAL,
OUTGOING_PORT_MIN,
OUTGOING_PORT_MAX,
RECHECK_COMPLETED,
LIST_REFRESH,
RESOLVE_COUNTRIES,
RESOLVE_HOSTS,
MAX_HALF_OPEN,
SUPER_SEEDING,
NETWORK_IFACE,
NETWORK_LISTEN_IPV6,
NETWORK_ADDRESS,
PROGRAM_NOTIFICATIONS,
TRACKER_STATUS,
TRACKER_PORT,
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
UPDATE_CHECK,
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
USE_ICON_THEME,
#endif
CONFIRM_RECHECK_TORRENT,
TRACKER_EXCHANGE,
ANNOUNCE_ALL_TRACKERS,
ROW_COUNT
};
class AdvancedSettings: public QTableWidget
{
Q_OBJECT
private:
QSpinBox spin_cache, spin_save_resume_data_interval, outgoing_ports_min, outgoing_ports_max, spin_list_refresh, spin_maxhalfopen, spin_tracker_port;
QCheckBox cb_os_cache, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts,
cb_super_seeding, cb_program_notifications, cb_tracker_status,
cb_confirm_torrent_recheck, cb_enable_tracker_ext, cb_listen_ipv6;
QComboBox combo_iface;
QSpinBox spin_cache_ttl;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QCheckBox cb_update_check;
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
QCheckBox cb_use_icon_theme;
#endif
QCheckBox cb_announce_all_trackers;
QLineEdit txt_network_address;
public:
AdvancedSettings(QWidget *parent=0): QTableWidget(parent)
{
// Set visual appearance
setEditTriggers(QAbstractItemView::NoEditTriggers);
setAlternatingRowColors(true);
setColumnCount(2);
QStringList header;
header << tr("Setting") << tr("Value", "Value set for this setting");
setHorizontalHeaderLabels(header);
setColumnWidth(0, width()/2);
horizontalHeader()->setStretchLastSection(true);
verticalHeader()->setVisible(false);
setRowCount(ROW_COUNT);
// Signals
connect(&spin_cache, SIGNAL(valueChanged(int)), SLOT(updateCacheSpinSuffix(int)));
// Load settings
loadAdvancedSettings();
}
~AdvancedSettings() {}
AdvancedSettings(QWidget *parent);
public slots:
void saveAdvancedSettings()
{
Preferences* const pref = Preferences::instance();
// Disk write cache
pref->setDiskCacheSize(spin_cache.value());
pref->setDiskCacheTTL(spin_cache_ttl.value());
// Enable OS cache
pref->setOsCache(cb_os_cache.isChecked());
// Save resume data interval
pref->setSaveResumeDataInterval(spin_save_resume_data_interval.value());
// Outgoing ports
pref->setOutgoingPortsMin(outgoing_ports_min.value());
pref->setOutgoingPortsMax(outgoing_ports_max.value());
// Recheck torrents on completion
pref->recheckTorrentsOnCompletion(cb_recheck_completed.isChecked());
// Transfer list refresh interval
pref->setRefreshInterval(spin_list_refresh.value());
// Peer resolution
pref->resolvePeerCountries(cb_resolve_countries.isChecked());
pref->resolvePeerHostNames(cb_resolve_hosts.isChecked());
// Max Half-Open connections
pref->setMaxHalfOpenConnections(spin_maxhalfopen.value());
// Super seeding
pref->enableSuperSeeding(cb_super_seeding.isChecked());
// Network interface
if (combo_iface.currentIndex() == 0) {
// All interfaces (default)
pref->setNetworkInterface(QString::null);
pref->setNetworkInterfaceName(QString::null);
}
else {
pref->setNetworkInterface(combo_iface.itemData(combo_iface.currentIndex()).toString());
pref->setNetworkInterfaceName(combo_iface.currentText());
}
// Listen on IPv6 address
pref->setListenIPv6(cb_listen_ipv6.isChecked());
// Network address
QHostAddress addr(txt_network_address.text().trimmed());
if (addr.isNull())
pref->setNetworkAddress("");
else
pref->setNetworkAddress(addr.toString());
// Program notification
pref->useProgramNotification(cb_program_notifications.isChecked());
// Tracker
pref->setTrackerEnabled(cb_tracker_status.isChecked());
pref->setTrackerPort(spin_tracker_port.value());
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
pref->setUpdateCheckEnabled(cb_update_check.isChecked());
#endif
// Icon theme
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
pref->useSystemIconTheme(cb_use_icon_theme.isChecked());
#endif
pref->setConfirmTorrentRecheck(cb_confirm_torrent_recheck.isChecked());
// Tracker exchange
pref->setTrackerExchangeEnabled(cb_enable_tracker_ext.isChecked());
pref->setAnnounceToAllTrackers(cb_announce_all_trackers.isChecked());
}
void saveAdvancedSettings();
signals:
void settingsChanged();
private:
void setRow(int row, const QString &property, QSpinBox* editor)
{
setItem(row, PROPERTY, new QTableWidgetItem(property));
bool ok; Q_UNUSED(ok);
ok = connect(editor, SIGNAL(valueChanged(int)), SIGNAL(settingsChanged()));
Q_ASSERT(ok);
setCellWidget(row, VALUE, editor);
}
void setRow(int row, const QString &property, QComboBox* editor)
{
setItem(row, PROPERTY, new QTableWidgetItem(property));
bool ok; Q_UNUSED(ok);
ok = connect(editor, SIGNAL(currentIndexChanged(int)), SIGNAL(settingsChanged()));
Q_ASSERT(ok);
setCellWidget(row, VALUE, editor);
}
void setRow(int row, const QString &property, QCheckBox* editor)
{
setItem(row, PROPERTY, new QTableWidgetItem(property));
bool ok; Q_UNUSED(ok);
ok = connect(editor, SIGNAL(stateChanged(int)), SIGNAL(settingsChanged()));
Q_ASSERT(ok);
setCellWidget(row, VALUE, editor);
}
void setRow(int row, const QString &property, QLineEdit* editor)
{
setItem(row, PROPERTY, new QTableWidgetItem(property));
bool ok; Q_UNUSED(ok);
ok = connect(editor, SIGNAL(textChanged(QString)), SIGNAL(settingsChanged()));
Q_ASSERT(ok);
setCellWidget(row, VALUE, editor);
}
private slots:
void updateCacheSpinSuffix(int value)
{
if (value <= 0)
spin_cache.setSuffix(tr(" (auto)"));
else
spin_cache.setSuffix(tr(" MiB"));
}
void updateCacheSpinSuffix(int value);
void loadAdvancedSettings()
{
const Preferences* const pref = Preferences::instance();
// Disk write cache
spin_cache.setMinimum(0);
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
// These macros may not be available on compilers other than MSVC and GCC
#if defined(__x86_64__) || defined(_M_X64)
spin_cache.setMaximum(4096);
#else
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
spin_cache.setMaximum(1536);
#endif
spin_cache.setValue(pref->diskCacheSize());
updateCacheSpinSuffix(spin_cache.value());
setRow(DISK_CACHE, tr("Disk write cache size"), &spin_cache);
// Disk cache expiry
spin_cache_ttl.setMinimum(15);
spin_cache_ttl.setMaximum(600);
spin_cache_ttl.setValue(pref->diskCacheTTL());
spin_cache_ttl.setSuffix(tr(" s", " seconds"));
setRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spin_cache_ttl);
// Enable OS cache
cb_os_cache.setChecked(pref->osCache());
setRow(OS_CACHE, tr("Enable OS cache"), &cb_os_cache);
// Save resume data interval
spin_save_resume_data_interval.setMinimum(1);
spin_save_resume_data_interval.setMaximum(1440);
spin_save_resume_data_interval.setValue(pref->saveResumeDataInterval());
spin_save_resume_data_interval.setSuffix(tr(" m", " minutes"));
setRow(SAVE_RESUME_DATA_INTERVAL, tr("Save resume data interval", "How often the fastresume file is saved."), &spin_save_resume_data_interval);
// Outgoing port Min
outgoing_ports_min.setMinimum(0);
outgoing_ports_min.setMaximum(65535);
outgoing_ports_min.setValue(pref->outgoingPortsMin());
setRow(OUTGOING_PORT_MIN, tr("Outgoing ports (Min) [0: Disabled]"), &outgoing_ports_min);
// Outgoing port Min
outgoing_ports_max.setMinimum(0);
outgoing_ports_max.setMaximum(65535);
outgoing_ports_max.setValue(pref->outgoingPortsMax());
setRow(OUTGOING_PORT_MAX, tr("Outgoing ports (Max) [0: Disabled]"), &outgoing_ports_max);
// Recheck completed torrents
cb_recheck_completed.setChecked(pref->recheckTorrentsOnCompletion());
setRow(RECHECK_COMPLETED, tr("Recheck torrents on completion"), &cb_recheck_completed);
// Transfer list refresh interval
spin_list_refresh.setMinimum(30);
spin_list_refresh.setMaximum(99999);
spin_list_refresh.setValue(pref->getRefreshInterval());
spin_list_refresh.setSuffix(tr(" ms", " milliseconds"));
setRow(LIST_REFRESH, tr("Transfer list refresh interval"), &spin_list_refresh);
// Resolve Peer countries
cb_resolve_countries.setChecked(pref->resolvePeerCountries());
setRow(RESOLVE_COUNTRIES, tr("Resolve peer countries (GeoIP)"), &cb_resolve_countries);
// Resolve peer hosts
cb_resolve_hosts.setChecked(pref->resolvePeerHostNames());
setRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &cb_resolve_hosts);
// Max Half Open connections
spin_maxhalfopen.setMinimum(0);
spin_maxhalfopen.setMaximum(99999);
spin_maxhalfopen.setValue(pref->getMaxHalfOpenConnections());
setRow(MAX_HALF_OPEN, tr("Maximum number of half-open connections [0: Unlimited]"), &spin_maxhalfopen);
// Super seeding
cb_super_seeding.setChecked(pref->isSuperSeedingEnabled());
setRow(SUPER_SEEDING, tr("Strict super seeding"), &cb_super_seeding);
// Network interface
combo_iface.addItem(tr("Any interface", "i.e. Any network interface"));
const QString current_iface = pref->getNetworkInterface();
bool interface_exists = current_iface.isEmpty();
int i = 1;
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
combo_iface.addItem(iface.humanReadableName(),iface.name());
if (!current_iface.isEmpty() && iface.name() == current_iface) {
combo_iface.setCurrentIndex(i);
interface_exists = true;
}
++i;
}
// Saved interface does not exist, show it anyway
if (!interface_exists) {
combo_iface.addItem(pref->getNetworkInterfaceName(),current_iface);
combo_iface.setCurrentIndex(i);
}
setRow(NETWORK_IFACE, tr("Network Interface (requires restart)"), &combo_iface);
// Listen on IPv6 address
cb_listen_ipv6.setChecked(pref->getListenIPv6());
setRow(NETWORK_LISTEN_IPV6, tr("Listen on IPv6 address (requires restart)"), &cb_listen_ipv6);
// Network address
txt_network_address.setText(pref->getNetworkAddress());
setRow(NETWORK_ADDRESS, tr("IP Address to report to trackers (requires restart)"), &txt_network_address);
// Program notifications
cb_program_notifications.setChecked(pref->useProgramNotification());
setRow(PROGRAM_NOTIFICATIONS, tr("Display program on-screen notifications"), &cb_program_notifications);
// Tracker State
cb_tracker_status.setChecked(pref->isTrackerEnabled());
setRow(TRACKER_STATUS, tr("Enable embedded tracker"), &cb_tracker_status);
// Tracker port
spin_tracker_port.setMinimum(1);
spin_tracker_port.setMaximum(65535);
spin_tracker_port.setValue(pref->getTrackerPort());
setRow(TRACKER_PORT, tr("Embedded tracker port"), &spin_tracker_port);
private:
void loadAdvancedSettings();
template <typename T> void addRow(int row, const QString &rowText, T* widget);
QLabel labelQbtLink, labelLibtorrentLink;
QSpinBox spin_cache, spin_save_resume_data_interval, outgoing_ports_min, outgoing_ports_max, spin_list_refresh, spin_maxhalfopen, spin_tracker_port, spin_cache_ttl;
QCheckBox cb_os_cache, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts,
cb_super_seeding, cb_program_notifications, cb_tracker_status,
cb_confirm_torrent_recheck, cb_enable_tracker_ext, cb_listen_ipv6, cb_announce_all_trackers;
QComboBox combo_iface;
QLineEdit txt_network_address;
// OS dependent settings
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
cb_update_check.setChecked(pref->isUpdateCheckEnabled());
setRow(UPDATE_CHECK, tr("Check for software updates"), &cb_update_check);
QCheckBox cb_update_check;
#endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
cb_use_icon_theme.setChecked(pref->useSystemIconTheme());
setRow(USE_ICON_THEME, tr("Use system icon theme"), &cb_use_icon_theme);
QCheckBox cb_use_icon_theme;
#endif
// Torrent recheck confirmation
cb_confirm_torrent_recheck.setChecked(pref->confirmTorrentRecheck());
setRow(CONFIRM_RECHECK_TORRENT, tr("Confirm torrent recheck"), &cb_confirm_torrent_recheck);
// Tracker exchange
cb_enable_tracker_ext.setChecked(pref->trackerExchangeEnabled());
setRow(TRACKER_EXCHANGE, tr("Exchange trackers with other peers"), &cb_enable_tracker_ext);
// Announce to all trackers
cb_announce_all_trackers.setChecked(pref->announceToAllTrackers());
setRow(ANNOUNCE_ALL_TRACKERS, tr("Always announce to all trackers"), &cb_announce_all_trackers);
}
};
#endif // ADVANCEDSETTINGS_H

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>confirmShutdownDlg</class>
<widget class="QDialog" name="confirmShutdownDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>407</width>
<height>103</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>15</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="warningLabel">
<property name="text">
<string notr="true">warning icon</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QLabel" name="shutdownText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">shutdown message goes here</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="neverShowAgainCheckbox">
<property name="text">
<string>Don't show again</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>confirmShutdownDlg</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>confirmShutdownDlg</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -35,18 +35,20 @@
#include <QPalette>
#include "executionlog.h"
#include "ui_executionlog.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "guiiconprovider.h"
#include "loglistwidget.h"
ExecutionLog::ExecutionLog(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ExecutionLog)
, m_msgList(new LogListWidget(MAX_LOG_MESSAGES))
, m_peerList(new LogListWidget(MAX_LOG_MESSAGES))
{
ui->setupUi(this);
m_msgList = new LogListWidget(MAX_LOG_MESSAGES,
Log::MsgTypes(Preferences::instance()->executionLogMessageTypes()));
ui->tabConsole->setTabIcon(0, GuiIconProvider::instance()->getIcon("view-calendar-journal"));
ui->tabConsole->setTabIcon(1, GuiIconProvider::instance()->getIcon("view-filter"));
ui->tabGeneral->layout()->addWidget(m_msgList);
@@ -68,6 +70,11 @@ ExecutionLog::~ExecutionLog()
delete ui;
}
void ExecutionLog::showMsgTypes(const Log::MsgTypes &types)
{
m_msgList->showMsgTypes(types);
}
void ExecutionLog::addLogMessage(const Log::Msg &msg)
{
QString text;
@@ -89,7 +96,7 @@ void ExecutionLog::addLogMessage(const Log::Msg &msg)
}
text = "<font color='grey'>" + time.toString(Qt::SystemLocaleShortDate) + "</font> - <font color='" + color.name() + "'>" + msg.message + "</font>";
m_msgList->appendLine(text);
m_msgList->appendLine(text, msg.type);
}
void ExecutionLog::addPeerMessage(const Log::Peer& peer)
@@ -102,5 +109,5 @@ void ExecutionLog::addPeerMessage(const Log::Peer& peer)
else
text = "<font color='grey'>" + time.toString(Qt::SystemLocaleShortDate) + "</font> - " + tr("<font color='red'>%1</font> was banned", "x.y.z.w was banned").arg(peer.ip);
m_peerList->appendLine(text);
m_peerList->appendLine(text, Log::NORMAL);
}

View File

@@ -32,27 +32,22 @@
#define EXECUTIONLOG_H
#include <QWidget>
#include "base/logger.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class ExecutionLog;
}
QT_END_NAMESPACE
class Logger;
class LogListWidget;
namespace Log
{
struct Msg;
struct Peer;
}
class ExecutionLog: public QWidget
{
Q_OBJECT
public:
explicit ExecutionLog(QWidget *parent = 0);
void showMsgTypes(const Log::MsgTypes &types);
~ExecutionLog();
private slots:

View File

@@ -14,6 +14,18 @@
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabConsole">
<property name="tabPosition">
@@ -26,13 +38,13 @@
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout"/>
<layout class="QVBoxLayout" name="tabGeneralLayout"/>
</widget>
<widget class="QWidget" name="tabBan">
<attribute name="title">
<string>Blocked IPs</string>
</attribute>
<layout class="QVBoxLayout" name="_2"/>
<layout class="QVBoxLayout" name="tabBanLayout"/>
</widget>
</widget>
</item>

View File

@@ -510,7 +510,7 @@ This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the
<a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
<a href="https://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
instead of this License.
</p>
</body>

View File

@@ -75,6 +75,7 @@ SOURCES += \
$$PWD/statsdialog.cpp \
$$PWD/messageboxraised.cpp \
$$PWD/statusbar.cpp \
$$PWD/advancedsettings.cpp \
$$PWD/trackerlogin.cpp \
$$PWD/options_imp.cpp \
$$PWD/shutdownconfirm.cpp \
@@ -102,6 +103,7 @@ FORMS += \
$$PWD/bandwidth_limit.ui \
$$PWD/updownratiodlg.ui \
$$PWD/confirmdeletiondlg.ui \
$$PWD/confirmshutdowndlg.ui \
$$PWD/torrentimportdlg.ui \
$$PWD/executionlog.ui \
$$PWD/addnewtorrentdialog.ui \

View File

@@ -0,0 +1,18 @@
set(QBT_LINEEDIT_SOURCES
src/lineedit.cpp
)
set(QBT_LINEEDIT_HEADERS
src/lineedit.h
)
set(QBT_LINEEDIT_RESOURCES
resources/lineeditimages.qrc
)
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS} ${QBT_LINEEDIT_RESOURCES})
if (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt4::QtGui)
else (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt5::Widgets)
endif (QT4_FOUND)

View File

@@ -37,60 +37,67 @@
#include "loglistwidget.h"
#include "guiiconprovider.h"
LogListWidget::LogListWidget(int max_lines, QWidget *parent) :
QListWidget(parent),
m_maxLines(max_lines)
LogListWidget::LogListWidget(int maxLines, const Log::MsgTypes &types, QWidget *parent)
: QListWidget(parent)
, m_maxLines(maxLines)
, m_types(types)
{
// Allow multiple selections
setSelectionMode(QAbstractItemView::ExtendedSelection);
// Context menu
QAction *copyAct = new QAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy"), this);
QAction *clearAct = new QAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Clear"), this);
connect(copyAct, SIGNAL(triggered()), SLOT(copySelection()));
connect(clearAct, SIGNAL(triggered()), SLOT(clearLog()));
addAction(copyAct);
addAction(clearAct);
setContextMenuPolicy(Qt::ActionsContextMenu);
// Allow multiple selections
setSelectionMode(QAbstractItemView::ExtendedSelection);
// Context menu
QAction *copyAct = new QAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy"), this);
QAction *clearAct = new QAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Clear"), this);
connect(copyAct, SIGNAL(triggered()), SLOT(copySelection()));
connect(clearAct, SIGNAL(triggered()), SLOT(clear()));
addAction(copyAct);
addAction(clearAct);
setContextMenuPolicy(Qt::ActionsContextMenu);
}
void LogListWidget::showMsgTypes(const Log::MsgTypes &types)
{
m_types = types;
for (int i = 0; i < count(); ++i) {
QListWidgetItem *tempItem = item(i);
if (!tempItem) continue;
Log::MsgType itemType = static_cast<Log::MsgType>(tempItem->data(Qt::UserRole).toInt());
setRowHidden(i, !(m_types & itemType));
}
}
void LogListWidget::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Copy)) {
copySelection();
return;
}
if (event->matches(QKeySequence::SelectAll)) {
selectAll();
return;
}
if (event->matches(QKeySequence::Copy))
copySelection();
else if (event->matches(QKeySequence::SelectAll))
selectAll();
}
void LogListWidget::appendLine(const QString &line)
void LogListWidget::appendLine(const QString &line, const Log::MsgType &type)
{
QListWidgetItem *item = new QListWidgetItem;
// We need to use QLabel here to support rich text
QLabel *lbl = new QLabel(line);
lbl->setContentsMargins(4, 2, 4, 2);
item->setSizeHint(lbl->sizeHint());
insertItem(0, item);
setItemWidget(item, lbl);
const int nbLines = count();
// Limit log size
if (nbLines > m_maxLines)
delete takeItem(nbLines - 1);
QListWidgetItem *item = new QListWidgetItem;
// We need to use QLabel here to support rich text
QLabel *lbl = new QLabel(line);
lbl->setContentsMargins(4, 2, 4, 2);
item->setSizeHint(lbl->sizeHint());
item->setData(Qt::UserRole, type);
insertItem(0, item);
setItemWidget(item, lbl);
setRowHidden(0, !(m_types & type));
const int nbLines = count();
// Limit log size
if (nbLines > m_maxLines)
delete takeItem(nbLines - 1);
}
void LogListWidget::copySelection()
{
static QRegExp html_tag("<[^>]+>");
QList<QListWidgetItem*> items = selectedItems();
QStringList strings;
foreach (QListWidgetItem* it, items)
strings << static_cast<QLabel*>(itemWidget(it))->text().replace(html_tag, "");
static QRegExp htmlTag("<[^>]+>");
QStringList strings;
foreach (QListWidgetItem* it, selectedItems())
strings << static_cast<QLabel*>(itemWidget(it))->text().replace(htmlTag, "");
QApplication::clipboard()->setText(strings.join("\n"));
}
void LogListWidget::clearLog() {
clear();
QApplication::clipboard()->setText(strings.join("\n"));
}

View File

@@ -31,31 +31,33 @@
#define LOGLISTWIDGET_H
#include <QListWidget>
#include "base/logger.h"
QT_BEGIN_NAMESPACE
class QKeyEvent;
QT_END_NAMESPACE
class LogListWidget : public QListWidget
class LogListWidget: public QListWidget
{
Q_OBJECT
public:
explicit LogListWidget(int max_lines = 100, QWidget *parent = 0);
// -1 is the portable way to have all the bits set
explicit LogListWidget(int maxLines, const Log::MsgTypes &types = Log::ALL, QWidget *parent = 0);
void showMsgTypes(const Log::MsgTypes &types);
public slots:
void appendLine(const QString &line);
void appendLine(const QString &line, const Log::MsgType &type);
protected slots:
void copySelection();
void clearLog();
void copySelection();
protected:
void keyPressEvent(QKeyEvent *event);
void keyPressEvent(QKeyEvent *event);
private:
int m_maxLines;
int m_maxLines;
Log::MsgTypes m_types;
};
#endif // LOGLISTWIDGET_H

View File

@@ -273,9 +273,20 @@ MainWindow::MainWindow(QWidget *parent)
actionSpeed_in_title_bar->setChecked(pref->speedInTitleBar());
actionRSS_Reader->setChecked(pref->isRSSEnabled());
actionSearch_engine->setChecked(pref->isSearchEnabled());
actionExecution_Logs->setChecked(pref->isExecutionLogEnabled());
actionExecutionLogs->setChecked(pref->isExecutionLogEnabled());
Log::MsgTypes flags(pref->executionLogMessageTypes());
actionNormalMessages->setChecked(flags & Log::NORMAL);
actionInformationMessages->setChecked(flags & Log::INFO);
actionWarningMessages->setChecked(flags & Log::WARNING);
actionCriticalMessages->setChecked(flags & Log::CRITICAL);
displayRSSTab(actionRSS_Reader->isChecked());
on_actionExecution_Logs_triggered(actionExecution_Logs->isChecked());
on_actionExecutionLogs_triggered(actionExecutionLogs->isChecked());
on_actionNormalMessages_triggered(actionNormalMessages->isChecked());
on_actionInformationMessages_triggered(actionInformationMessages->isChecked());
on_actionWarningMessages_triggered(actionWarningMessages->isChecked());
on_actionCriticalMessages_triggered(actionCriticalMessages->isChecked());
if (actionSearch_engine->isChecked())
QTimer::singleShot(0, this, SLOT(on_actionSearch_engine_triggered()));
@@ -1507,7 +1518,7 @@ void MainWindow::minimizeWindow()
setWindowState(windowState() ^ Qt::WindowMinimized);
}
void MainWindow::on_actionExecution_Logs_triggered(bool checked)
void MainWindow::on_actionExecutionLogs_triggered(bool checked)
{
if (checked) {
Q_ASSERT(!m_executionLog);
@@ -1518,9 +1529,66 @@ void MainWindow::on_actionExecution_Logs_triggered(bool checked)
else if (m_executionLog) {
delete m_executionLog;
}
actionNormalMessages->setEnabled(checked);
actionInformationMessages->setEnabled(checked);
actionWarningMessages->setEnabled(checked);
actionCriticalMessages->setEnabled(checked);
Preferences::instance()->setExecutionLogEnabled(checked);
}
void MainWindow::on_actionNormalMessages_triggered(bool checked)
{
if (!m_executionLog)
return;
Preferences* const pref = Preferences::instance();
Log::MsgTypes flags(pref->executionLogMessageTypes());
checked ? (flags |= Log::NORMAL) : (flags &= ~Log::NORMAL);
m_executionLog->showMsgTypes(flags);
pref->setExecutionLogMessageTypes(flags);
}
void MainWindow::on_actionInformationMessages_triggered(bool checked)
{
if (!m_executionLog)
return;
Preferences* const pref = Preferences::instance();
Log::MsgTypes flags(pref->executionLogMessageTypes());
checked ? (flags |= Log::INFO) : (flags &= ~Log::INFO);
m_executionLog->showMsgTypes(flags);
pref->setExecutionLogMessageTypes(flags);
}
void MainWindow::on_actionWarningMessages_triggered(bool checked)
{
if (!m_executionLog)
return;
Preferences* const pref = Preferences::instance();
Log::MsgTypes flags(pref->executionLogMessageTypes());
checked ? (flags |= Log::WARNING) : (flags &= ~Log::WARNING);
m_executionLog->showMsgTypes(flags);
pref->setExecutionLogMessageTypes(flags);
}
void MainWindow::on_actionCriticalMessages_triggered(bool checked)
{
if (!m_executionLog)
return;
Preferences* const pref = Preferences::instance();
Log::MsgTypes flags(pref->executionLogMessageTypes());
checked ? (flags |= Log::CRITICAL) : (flags &= ~Log::CRITICAL);
m_executionLog->showMsgTypes(flags);
pref->setExecutionLogMessageTypes(flags);
}
void MainWindow::on_actionAutoExit_qBittorrent_toggled(bool enabled)
{
qDebug() << Q_FUNC_INFO << enabled;

View File

@@ -214,7 +214,11 @@ private slots:
void on_actionTop_tool_bar_triggered();
void on_action_Import_Torrent_triggered();
void on_actionDonate_money_triggered();
void on_actionExecution_Logs_triggered(bool checked);
void on_actionExecutionLogs_triggered(bool checked);
void on_actionNormalMessages_triggered(bool checked);
void on_actionInformationMessages_triggered(bool checked);
void on_actionWarningMessages_triggered(bool checked);
void on_actionCriticalMessages_triggered(bool checked);
void on_actionAutoExit_qBittorrent_toggled(bool );
void on_actionAutoSuspend_system_toggled(bool );
void on_actionAutoHibernate_system_toggled(bool );

View File

@@ -35,7 +35,7 @@
<x>0</x>
<y>0</y>
<width>914</width>
<height>22</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu_Edit">
@@ -95,12 +95,23 @@
<property name="title">
<string>&amp;View</string>
</property>
<widget class="QMenu" name="menuLog">
<property name="title">
<string>&amp;Log</string>
</property>
<addaction name="actionExecutionLogs"/>
<addaction name="separator"/>
<addaction name="actionNormalMessages"/>
<addaction name="actionInformationMessages"/>
<addaction name="actionWarningMessages"/>
<addaction name="actionCriticalMessages"/>
</widget>
<addaction name="actionTop_tool_bar"/>
<addaction name="actionSpeed_in_title_bar"/>
<addaction name="separator"/>
<addaction name="actionSearch_engine"/>
<addaction name="actionRSS_Reader"/>
<addaction name="actionExecution_Logs"/>
<addaction name="menuLog"/>
<addaction name="separator"/>
<addaction name="actionStatistics"/>
<addaction name="separator"/>
@@ -340,17 +351,6 @@
<string>P&amp;ause All</string>
</property>
</action>
<action name="actionExecution_Logs">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Log</string>
</property>
<property name="toolTip">
<string>Execution Log</string>
</property>
</action>
<action name="actionAutoExit_qBittorrent">
<property name="checkable">
<bool>true</bool>
@@ -414,9 +414,47 @@
<string>Check for Program Updates</string>
</property>
</action>
<action name="actionExecutionLogs">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show</string>
</property>
</action>
<action name="actionNormalMessages">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Normal Messages</string>
</property>
</action>
<action name="actionInformationMessages">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Information Messages</string>
</property>
</action>
<action name="actionWarningMessages">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Warning Messages</string>
</property>
</action>
<action name="actionCriticalMessages">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Critical Messages</string>
</property>
</action>
</widget>
<resources>
<include location="../icons.qrc"/>
</resources>
<resources/>
<connections/>
</ui>

View File

@@ -23,30 +23,21 @@
<bool>false</bool>
</property>
<widget class="QListWidget" name="tabSelection">
<property name="autoScroll">
<bool>false</bool>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
<property name="movement">
<enum>QListView::Static</enum>
</property>
<property name="flow">
<enum>QListView::TopToBottom</enum>
</property>
<property name="isWrapping" stdset="0">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="spacing">
<number>4</number>
</property>
<property name="gridSize">
<size>
<width>110</width>
<height>60</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
@@ -57,78 +48,36 @@
<property name="text">
<string>Behavior</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>Downloads</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>Connection</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>Speed</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>BitTorrent</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>Web UI</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
<item>
<property name="text">
<string>Advanced</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
<property name="flags">
<set>ItemIsSelectable|ItemIsEnabled</set>
</property>
</item>
</widget>
<widget class="QStackedWidget" name="tabOption">
@@ -143,6 +92,9 @@
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -161,9 +113,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>454</width>
<height>610</height>
<y>-190</y>
<width>486</width>
<height>732</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
@@ -257,6 +209,44 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="checkHideZero">
<property name="text">
<string>Hide zero and infinity values</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboHideZero">
<item>
<property name="text">
<string>Always</string>
</property>
</item>
<item>
<property name="text">
<string>Paused torrents only</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="font">
@@ -379,6 +369,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkProgramAutoExitConfirm">
<property name="text">
<string>Confirmation on auto-exit when downloads finish</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="checkShowSystray">
<property name="title">
@@ -480,6 +480,153 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="checkFileLog">
<property name="title">
<string>Log file</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Save path:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textFileLogPath"/>
</item>
<item>
<widget class="QToolButton" name="browseFileLogDir">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkFileLogBackup">
<property name="text">
<string>Backup the log file after:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinFileLogSize">
<property name="suffix">
<string> MB</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkFileLogDelete">
<property name="text">
<string>Delete backup logs older than:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinFileLogAge">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>365</number>
</property>
<property name="value">
<number>6</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboFileLogAgeType">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string comment="Delete backup logs older than 10 months">days</string>
</property>
</item>
<item>
<property name="text">
<string comment="Delete backup logs older than 10 months">months</string>
</property>
</item>
<item>
<property name="text">
<string comment="Delete backup logs older than 10 years">years</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
@@ -507,6 +654,9 @@
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -520,8 +670,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>454</width>
<height>942</height>
<width>487</width>
<height>1334</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -1020,6 +1170,9 @@
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -1033,8 +1186,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>586</height>
<width>450</width>
<height>658</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_20">
@@ -1563,8 +1716,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>275</width>
<height>401</height>
<width>376</width>
<height>444</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
@@ -1934,6 +2087,9 @@
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -1947,8 +2103,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>440</width>
<height>481</height>
<width>555</width>
<height>527</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
@@ -2052,7 +2208,7 @@
<item>
<widget class="QLabel" name="label_anonymous">
<property name="text">
<string> (&lt;a href=&quot;http://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode&quot;&gt;More information&lt;/a&gt;)</string>
<string> (&lt;a href=&quot;https://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode&quot;&gt;More information&lt;/a&gt;)</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
@@ -2320,14 +2476,17 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tabOptionPage7">
<layout class="QVBoxLayout">
<widget class="QWidget" name="tabWebuiPage">
<layout class="QVBoxLayout" name="tabWebuiPageLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -2341,8 +2500,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>480</height>
<width>419</width>
<height>537</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_23">
@@ -2526,7 +2685,7 @@
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="lblWebUIInfo">
<property name="text">
<string>&lt;a href=http://httpd.apache.org/docs/2.2/ssl/ssl_faq.html#aboutcerts&gt;Information about certificates&lt;/a&gt;</string>
<string>&lt;a href=https://httpd.apache.org/docs/current/ssl/ssl_faq.html#aboutcerts&gt;Information about certificates&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
@@ -2707,47 +2866,19 @@
</layout>
</widget>
<widget class="QWidget" name="tabAdvancedPage">
<layout class="QVBoxLayout" name="verticalLayout_35">
<layout class="QVBoxLayout" name="advPageLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea_advanced">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_10">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>98</width>
<height>28</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_36">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@@ -67,6 +67,7 @@ options_imp::options_imp(QWidget *parent)
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
// Icons
tabSelection->item(TAB_UI)->setIcon(GuiIconProvider::instance()->getIcon("preferences-desktop"));
tabSelection->item(TAB_BITTORRENT)->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network"));
@@ -79,6 +80,10 @@ options_imp::options_imp(QWidget *parent)
tabSelection->item(TAB_WEBUI)->setHidden(true);
#endif
tabSelection->item(TAB_ADVANCED)->setIcon(GuiIconProvider::instance()->getIcon("preferences-other"));
for (int i = 0; i < tabSelection->count(); ++i) {
tabSelection->item(i)->setSizeHint(QSize(96, 64)); // uniform size for all icons
}
IpFilterRefreshBtn->setIcon(GuiIconProvider::instance()->getIcon("view-refresh"));
hsplitter->setCollapsible(0, false);
@@ -141,6 +146,8 @@ options_imp::options_imp(QWidget *parent)
connect(comboI18n, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton()));
connect(confirmDeletion, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkAltRowColors, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkHideZero, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(comboHideZero, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton()));
connect(checkShowSystray, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkCloseToSystray, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkMinimizeToSysTray, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
@@ -150,6 +157,7 @@ options_imp::options_imp(QWidget *parent)
#endif
connect(checkShowSplash, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkProgramExitConfirm, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkProgramAutoExitConfirm, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkPreventFromSuspend, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(comboTrayIcon, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton()));
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && !defined(QT_DBUS_LIB)
@@ -159,6 +167,16 @@ options_imp::options_imp(QWidget *parent)
connect(checkAssociateTorrents, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkAssociateMagnetLinks, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
#endif
connect(checkFileLog, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(textFileLogPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
connect(checkFileLogBackup, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkFileLogBackup, SIGNAL(toggled(bool)), spinFileLogSize, SLOT(setEnabled(bool)));
connect(checkFileLogDelete, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkFileLogDelete, SIGNAL(toggled(bool)), spinFileLogAge, SLOT(setEnabled(bool)));
connect(checkFileLogDelete, SIGNAL(toggled(bool)), comboFileLogAgeType, SLOT(setEnabled(bool)));
connect(spinFileLogSize, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton()));
connect(spinFileLogAge, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton()));
connect(comboFileLogAgeType, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton()));
// Downloads tab
connect(textSavePath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
connect(textTempPath, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
@@ -187,17 +205,19 @@ options_imp::options_imp(QWidget *parent)
connect(autoRunBox, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(autoRun_txt, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton()));
autoRun_param->setText(QString::fromUtf8("%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10")
.arg(tr("Supported parameters (case sensitive):"))
.arg(tr("%N: Torrent name"))
.arg(tr("%L: Label"))
.arg(tr("%F: Content path (same as root path for multifile torrent)"))
.arg(tr("%R: Root path (first torrent subdirectory path)"))
.arg(tr("%D: Save path"))
.arg(tr("%C: Number of files"))
.arg(tr("%Z: Torrent size (bytes)"))
.arg(tr("%T: Current tracker"))
.arg(tr("%I: Info hash")));
const QString autoRunStr = QString::fromUtf8("%1\n %2\n %3\n %4\n %5\n %6\n %7\n %8\n %9\n %10\n%11")
.arg(tr("Supported parameters (case sensitive):"))
.arg(tr("%N: Torrent name"))
.arg(tr("%L: Category"))
.arg(tr("%F: Content path (same as root path for multifile torrent)"))
.arg(tr("%R: Root path (first torrent subdirectory path)"))
.arg(tr("%D: Save path"))
.arg(tr("%C: Number of files"))
.arg(tr("%Z: Torrent size (bytes)"))
.arg(tr("%T: Current tracker"))
.arg(tr("%I: Info hash"))
.arg(tr("Tip: Encapsulate parameter with quotation marks to avoid text being cut off at whitespace (e.g., \"%N\")"));
autoRun_param->setText(autoRunStr);
// Connection tab
connect(spinPort, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton()));
@@ -279,10 +299,8 @@ options_imp::options_imp(QWidget *parent)
// Tab selection mechanism
connect(tabSelection, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*)));
// Load Advanced settings
QVBoxLayout *adv_layout = new QVBoxLayout();
advancedSettings = new AdvancedSettings();
adv_layout->addWidget(advancedSettings);
scrollArea_advanced->setLayout(adv_layout);
advancedSettings = new AdvancedSettings(tabAdvancedPage);
advPageLayout->addWidget(advancedSettings);
connect(advancedSettings, SIGNAL(settingsChanged()), this, SLOT(enableApplyButton()));
// Adapt size
@@ -293,14 +311,21 @@ options_imp::options_imp(QWidget *parent)
void options_imp::initializeLanguageCombo()
{
// List language files
const QDir lang_dir(":/lang");
const QStringList lang_files = lang_dir.entryList(QStringList() << "qbittorrent_*.qm", QDir::Files);
foreach (QString lang_file, lang_files) {
QString localeStr = lang_file.mid(12); // remove "qbittorrent_"
const QDir langDir(":/lang");
const QStringList langFiles = langDir.entryList(QStringList("qbittorrent_*.qm"), QDir::Files);
foreach (const QString langFile, langFiles) {
QString localeStr = langFile.mid(12); // remove "qbittorrent_"
localeStr.chop(3); // Remove ".qm"
QLocale locale(localeStr);
QString language_name = languageToLocalizedString(locale);
comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".png"), */ language_name, localeStr);
QString languageName;
if (localeStr.startsWith("eo", Qt::CaseInsensitive)) {
// QLocale doesn't work with that locale. Esperanto isn't a "real" language.
languageName = QString::fromUtf8(C_LOCALE_ESPERANTO);
}
else {
QLocale locale(localeStr);
languageName = languageToLocalizedString(locale);
}
comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".png"), */ languageName, localeStr);
qDebug() << "Supported locale:" << localeStr;
}
}
@@ -312,8 +337,6 @@ options_imp::~options_imp()
foreach (const QString &path, addedScanDirs)
ScanFoldersModel::instance()->removePath(path);
ScanFoldersModel::instance()->configure(); // reloads "removed" paths
delete scrollArea_advanced->layout();
delete advancedSettings;
}
void options_imp::changePage(QListWidgetItem *current, QListWidgetItem *previous)
@@ -396,6 +419,8 @@ void options_imp::saveOptions()
pref->setLocale(locale);
pref->setConfirmTorrentDeletion(confirmDeletion->isChecked());
pref->setAlternatingRowColors(checkAltRowColors->isChecked());
pref->setHideZeroValues(checkHideZero->isChecked());
pref->setHideZeroComboValues(comboHideZero->currentIndex());
pref->setSystrayIntegration(systrayIntegration());
pref->setTrayIconStyle(TrayIcon::Style(comboTrayIcon->currentIndex()));
pref->setCloseToTray(closeToTray());
@@ -403,6 +428,7 @@ void options_imp::saveOptions()
pref->setStartMinimized(startMinimized());
pref->setSplashScreenDisabled(isSlashScreenDisabled());
pref->setConfirmOnExit(checkProgramExitConfirm->isChecked());
pref->setDontConfirmAutoExit(!checkProgramAutoExitConfirm->isChecked());
pref->setPreventFromSuspend(preventFromSuspend());
#ifdef Q_OS_WIN
pref->setWinStartup(WinStartup());
@@ -422,6 +448,13 @@ void options_imp::saveOptions()
checkAssociateMagnetLinks->setEnabled(!checkAssociateMagnetLinks->isChecked());
}
#endif
pref->setFileLogEnabled(checkFileLog->isChecked());
pref->setFileLogPath(Utils::Fs::fromNativePath(textFileLogPath->text()));
pref->setFileLogBackup(checkFileLogBackup->isChecked());
pref->setFileLogMaxSize(spinFileLogSize->value());
pref->setFileLogDeleteOld(checkFileLogDelete->isChecked());
pref->setFileLogAge(spinFileLogAge->value());
pref->setFileLogAgeType(comboFileLogAgeType->currentIndex());
// End General preferences
// Downloads preferences
@@ -523,7 +556,6 @@ void options_imp::saveOptions()
pref->setWebUiHttpsKey(m_sslKey);
}
pref->setWebUiUsername(webUiUsername());
// FIXME: Check that the password is valid (not empty at least)
pref->setWebUiPassword(webUiPassword());
pref->setWebUiLocalAuthEnabled(!checkBypassLocalAuth->isChecked());
// DynDNS
@@ -571,16 +603,21 @@ void options_imp::loadOptions()
int intValue;
qreal floatValue;
QString strValue;
bool fileLogBackup = true;
bool fileLogDelete = true;
const Preferences* const pref = Preferences::instance();
// General preferences
setLocale(pref->getLocale());
confirmDeletion->setChecked(pref->confirmTorrentDeletion());
checkAltRowColors->setChecked(pref->useAlternatingRowColors());
checkHideZero->setChecked(pref->getHideZeroValues());
comboHideZero->setCurrentIndex(pref->getHideZeroComboValues());
checkShowSplash->setChecked(!pref->isSplashScreenDisabled());
checkStartMinimized->setChecked(pref->startMinimized());
checkProgramExitConfirm->setChecked(pref->confirmOnExit());
checkProgramAutoExitConfirm->setChecked(!pref->dontConfirmAutoExit());
checkShowSystray->setChecked(pref->systrayIntegration());
if (checkShowSystray->isChecked()) {
@@ -602,6 +639,19 @@ void options_imp::loadOptions()
checkAssociateMagnetLinks->setChecked(Preferences::isMagnetLinkAssocSet());
checkAssociateMagnetLinks->setEnabled(!checkAssociateMagnetLinks->isChecked());
#endif
checkFileLog->setChecked(pref->fileLogEnabled());
textFileLogPath->setText(Utils::Fs::toNativePath(pref->fileLogPath()));
fileLogBackup = pref->fileLogBackup();
checkFileLogBackup->setChecked(fileLogBackup);
spinFileLogSize->setEnabled(fileLogBackup);
fileLogDelete = pref->fileLogDeleteOld();
checkFileLogDelete->setChecked(fileLogDelete);
spinFileLogAge->setEnabled(fileLogDelete);
comboFileLogAgeType->setEnabled(fileLogDelete);
spinFileLogSize->setValue(pref->fileLogMaxSize());
spinFileLogAge->setValue(pref->fileLogAge());
comboFileLogAgeType->setCurrentIndex(pref->fileLogAgeType());
// End General preferences
// Downloads preferences
@@ -1028,6 +1078,10 @@ void options_imp::on_buttonBox_accepted()
tabSelection->setCurrentRow(TAB_SPEED);
return;
}
if (!webUIAuthenticationOk()) {
tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
applyButton->setEnabled(false);
this->hide();
saveOptions();
@@ -1043,6 +1097,10 @@ void options_imp::applySettings(QAbstractButton* button)
tabSelection->setCurrentRow(TAB_SPEED);
return;
}
if (!webUIAuthenticationOk()) {
tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
saveOptions();
}
}
@@ -1172,8 +1230,14 @@ QString options_imp::getLocale() const
void options_imp::setLocale(const QString &localeStr)
{
QLocale locale(localeStr);
QString name = locale.name();
QString name;
if (localeStr.startsWith("eo", Qt::CaseInsensitive)) {
name = "eo";
}
else {
QLocale locale(localeStr);
name = locale.name();
}
// Attempt to find exact match
int index = comboI18n->findData(name, Qt::UserRole);
if (index < 0) {
@@ -1283,6 +1347,19 @@ QString options_imp::askForExportDir(const QString& currentExportPath)
return dir;
}
void options_imp::on_browseFileLogDir_clicked()
{
const QString path = Utils::Fs::expandPathAbs(Utils::Fs::fromNativePath(textFileLogPath->text()));
QDir pathDir(path);
QString dir;
if (!path.isEmpty() && pathDir.exists())
dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), pathDir.absolutePath());
else
dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), QDir::homePath());
if (!dir.isNull())
textFileLogPath->setText(Utils::Fs::toNativePath(dir));
}
void options_imp::on_browseExportDirButton_clicked()
{
const QString newExportDir = askForExportDir(textExportDir->text());
@@ -1434,7 +1511,6 @@ QString options_imp::languageToLocalizedString(const QLocale &locale)
return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
return QString::fromUtf8(C_LOCALE_ENGLISH);
}
case QLocale::Esperanto: return QString::fromUtf8(C_LOCALE_ESPERANTO);
case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
@@ -1530,15 +1606,22 @@ void options_imp::setSslCertificate(const QByteArray &cert, bool interactive)
bool options_imp::schedTimesOk()
{
QString msg;
if (schedule_from->time() == schedule_to->time())
msg = tr("The start time and the end time can't be the same.");
if (!msg.isEmpty()) {
QMessageBox::critical(this, tr("Time Error"), msg);
if (schedule_from->time() == schedule_to->time()) {
QMessageBox::warning(this, tr("Time Error"), tr("The start time and the end time can't be the same."));
return false;
}
return true;
}
bool options_imp::webUIAuthenticationOk()
{
if (webUiUsername().length() < 3) {
QMessageBox::warning(this, tr("Length Error"), tr("The Web UI username must be at least 3 characters long."));
return false;
}
if (webUiPassword().length() < 6) {
QMessageBox::warning(this, tr("Length Error"), tr("The Web UI password must be at least 6 characters long."));
return false;
}
return true;
}

View File

@@ -83,6 +83,7 @@ private slots:
void handleScanFolderViewSelectionChanged();
void on_IpFilterRefreshBtn_clicked();
void handleIPFilterParsed(bool error, int ruleCount);
void on_browseFileLogDir_clicked();
void on_browseExportDirButton_clicked();
void on_browseExportDirFinButton_clicked();
void on_browseFilterButton_clicked();
@@ -166,6 +167,7 @@ private:
void setSslKey(const QByteArray &key, bool interactive = true);
void setSslCertificate(const QByteArray &cert, bool interactive = true);
bool schedTimesOk();
bool webUIAuthenticationOk();
private:
QButtonGroup choiceLanguage;

View File

@@ -0,0 +1,25 @@
set(QBT_POWERMANAGEMENT_HEADERS
powermanagement.h
)
set(QBT_POWERMANAGEMENT_SOURCES
powermanagement.cpp
)
if (UNIX AND DBUS)
find_package(X11)
if (X11_FOUND)
list(APPEND QBT_POWERMANAGEMENT_HEADERS powermanagement_x11.h)
list(APPEND QBT_POWERMANAGEMENT_SOURCES powermanagement_x11.cpp)
endif (X11_FOUND)
endif (UNIX AND DBUS)
add_library(qbt_powermanagement STATIC ${QBT_POWERMANAGEMENT_SOURCES} ${QBT_POWERMANAGEMENT_HEADERS})
set_target_properties(qbt_powermanagement PROPERTIES AUTOUIC False AUTORCC False)
target_link_qt_components(qbt_powermanagement Core)
if (X11_FOUND)
target_link_qt_components(qbt_powermanagement DBus)
endif (X11_FOUND)
if (WIN32)
target_link_libraries(qbt_powermanagement PowrProf)
endif (WIN32)

View File

@@ -41,7 +41,7 @@
namespace
{
const QString RSS_URL("http://www.fosshub.com/software/feedqBittorent");
const QString RSS_URL("https://www.fosshub.com/software/feedqBittorent");
#ifdef Q_OS_MAC
const QString OS_TYPE("Mac OS X");

View File

@@ -0,0 +1,47 @@
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
../lineedit/src/
)
set(QBT_PROPERTIES_FORMS
propertieswidget.ui
trackersadditiondlg.ui
peersadditiondlg.ui
)
set(QBT_PROPERTIES_HEADERS
propertieswidget.h
peerlistwidget.h
proplistdelegate.h
trackerlist.h
downloadedpiecesbar.h
peerlistdelegate.h
peerlistsortmodel.h
peersadditiondlg.h
trackersadditiondlg.h
pieceavailabilitybar.h
proptabbar.h
speedwidget.h
speedplotview.h
)
set(QBT_PROPERTIES_SOURCES
propertieswidget.cpp
proplistdelegate.cpp
peerlistwidget.cpp
trackerlist.cpp
peersadditiondlg.cpp
downloadedpiecesbar.cpp
trackersadditiondlg.cpp
pieceavailabilitybar.cpp
proptabbar.cpp
speedwidget.cpp
speedplotview.cpp
)
add_library(qbt_properties STATIC ${QBT_PROPERTIES_HEADERS} ${QBT_PROPERTIES_SOURCES})
target_link_libraries(qbt_properties qbt_base)
if (NOT QT4_FOUND)
target_link_libraries(qbt_properties Qt5::Widgets Qt5::Concurrent)
endif (NOT QT4_FOUND)

View File

@@ -41,7 +41,7 @@ class PeerListDelegate: public QItemDelegate {
public:
enum PeerListColumns {COUNTRY, IP, PORT, CONNECTION, FLAGS, CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED,
TOT_DOWN, TOT_UP, RELEVANCE, IP_HIDDEN, COL_COUNT};
TOT_DOWN, TOT_UP, RELEVANCE, DOWNLOADING_PIECE, IP_HIDDEN, COL_COUNT};
public:
PeerListDelegate(QObject *parent) : QItemDelegate(parent) {}

View File

@@ -35,6 +35,9 @@
#include <QMenu>
#include <QClipboard>
#include <QMessageBox>
#ifdef QBT_USES_QT5
#include <QTableView>
#endif
#include "base/net/reverseresolution.h"
#include "base/bittorrent/torrenthandle.h"
@@ -54,7 +57,7 @@
PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent)
, m_properties(parent)
, m_displayFlags(false)
, m_resolveCountries(false)
{
// Load settings
loadSettings();
@@ -64,9 +67,10 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
setItemsExpandable(false);
setAllColumnsShowFocus(true);
setSelectionMode(QAbstractItemView::ExtendedSelection);
header()->setStretchLastSection(false);
// List Model
m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT);
m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, QVariant()); // 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::PORT, Qt::Horizontal, tr("Port"));
m_listModel->setHeaderData(PeerListDelegate::FLAGS, Qt::Horizontal, tr("Flags"));
@@ -78,21 +82,28 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
m_listModel->setHeaderData(PeerListDelegate::TOT_DOWN, Qt::Horizontal, tr("Downloaded", "i.e: total data downloaded"));
m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, tr("Uploaded", "i.e: total data uploaded"));
m_listModel->setHeaderData(PeerListDelegate::RELEVANCE, Qt::Horizontal, tr("Relevance", "i.e: How relevant this peer is to us. How many pieces it has that we don't."));
m_listModel->setHeaderData(PeerListDelegate::DOWNLOADING_PIECE, Qt::Horizontal, tr("Files", "i.e. files that are being downloaded right now"));
// Proxy model to support sorting without actually altering the underlying model
m_proxyModel = new PeerListSortModel();
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSourceModel(m_listModel);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
setModel(m_proxyModel);
//Explicitly set the column visibility. When columns are added/removed
//between versions this prevents some of them being hidden due to
//incorrect restoreState() being used.
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
showColumn(i);
hideColumn(PeerListDelegate::IP_HIDDEN);
hideColumn(PeerListDelegate::COL_COUNT);
if (!Preferences::instance()->resolvePeerCountries())
hideColumn(PeerListDelegate::COUNTRY);
m_wasCountryColHidden = isColumnHidden(PeerListDelegate::COUNTRY);
//Ensure that at least one column is visible at all times
bool atLeastOne = false;
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) {
if (!isColumnHidden(i)) {
atLeastOne = true;
break;
}
}
if (!atLeastOne)
setColumnHidden(PeerListDelegate::IP, false);
//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
//in the above scenario.
@@ -110,9 +121,20 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
// IP to Hostname resolver
updatePeerHostNameResolutionState();
// SIGNAL/SLOT
header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayToggleColumnsMenu(const QPoint &)));
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
handleSortColumnChanged(header()->sortIndicatorSection());
m_copyHotkey = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_C), this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut);
#ifdef QBT_USES_QT5
// This hack fixes reordering of first column with Qt5.
// https://github.com/qtproject/qtbase/commit/e0fc088c0c8bc61dbcaf5928b24986cd61a22777
QTableView unused;
unused.setVerticalHeader(this->header());
this->header()->setParent(this);
unused.setVerticalHeader(new QHeaderView(Qt::Horizontal));
#endif
}
PeerListWidget::~PeerListWidget()
@@ -126,6 +148,46 @@ PeerListWidget::~PeerListWidget()
delete m_copyHotkey;
}
void PeerListWidget::displayToggleColumnsMenu(const QPoint&)
{
QMenu hideshowColumn(this);
hideshowColumn.setTitle(tr("Column visibility"));
QList<QAction*> actions;
for (int i = 0; i < PeerListDelegate::IP_HIDDEN; ++i) {
if ((i == PeerListDelegate::COUNTRY) && !Preferences::instance()->resolvePeerCountries()) {
actions.append(nullptr); // keep the index in sync
continue;
}
QAction *myAct = hideshowColumn.addAction(m_listModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
myAct->setCheckable(true);
myAct->setChecked(!isColumnHidden(i));
actions.append(myAct);
}
int visibleCols = 0;
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++) {
if (!isColumnHidden(i))
visibleCols++;
if (visibleCols > 1)
break;
}
// Call menu
QAction *act = hideshowColumn.exec(QCursor::pos());
if (act) {
int col = actions.indexOf(act);
Q_ASSERT(col >= 0);
Q_ASSERT(visibleCols > 0);
if (!isColumnHidden(col) && (visibleCols == 1))
return;
qDebug("Toggling column %d visibility", col);
setColumnHidden(col, !isColumnHidden(col));
if (!isColumnHidden(col) && (columnWidth(col) <= 5))
setColumnWidth(col, 100);
saveSettings();
}
}
void PeerListWidget::updatePeerHostNameResolutionState()
{
if (Preferences::instance()->resolvePeerHostNames()) {
@@ -142,15 +204,18 @@ void PeerListWidget::updatePeerHostNameResolutionState()
void PeerListWidget::updatePeerCountryResolutionState()
{
if (Preferences::instance()->resolvePeerCountries() != m_displayFlags) {
m_displayFlags = !m_displayFlags;
if (m_displayFlags) {
if (Preferences::instance()->resolvePeerCountries() != m_resolveCountries) {
m_resolveCountries = !m_resolveCountries;
if (m_resolveCountries) {
loadPeers(m_properties->getCurrentTorrent());
showColumn(PeerListDelegate::COUNTRY);
resizeColumnToContents(PeerListDelegate::COUNTRY);
if (!m_wasCountryColHidden) {
showColumn(PeerListDelegate::COUNTRY);
resizeColumnToContents(PeerListDelegate::COUNTRY);
}
}
else {
hideColumn(PeerListDelegate::COUNTRY);
m_wasCountryColHidden = false; // to forcefully enable that column if the user decides to resolve countries again
}
}
}
@@ -282,14 +347,14 @@ void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool fo
QString peerIp = addr.ip.toString();
if (m_peerItems.contains(peerIp)) {
// Update existing peer
updatePeer(peerIp, peer);
updatePeer(peerIp, torrent, peer);
oldeersSet.remove(peerIp);
if (forceHostnameResolution && m_resolver)
m_resolver->resolve(peerIp);
}
else {
// Add new peer
m_peerItems[peerIp] = addPeer(peerIp, peer);
m_peerItems[peerIp] = addPeer(peerIp, torrent, peer);
m_peerAddresses[peerIp] = addr;
// Resolve peer host name is asked
if (m_resolver)
@@ -307,7 +372,7 @@ void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool fo
}
}
QStandardItem* PeerListWidget::addPeer(const QString &ip, const BitTorrent::PeerInfo &peer)
QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer)
{
int row = m_listModel->rowCount();
// Adding Peer to peer list
@@ -316,7 +381,7 @@ QStandardItem* PeerListWidget::addPeer(const QString &ip, const BitTorrent::Peer
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
if (m_displayFlags) {
if (m_resolveCountries) {
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
@@ -337,14 +402,18 @@ QStandardItem* PeerListWidget::addPeer(const QString &ip, const BitTorrent::Peer
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), peer.relevance());
QStringList downloadingFiles(torrent->info().filesForPiece(peer.downloadingPieceIndex()));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWNLOADING_PIECE), downloadingFiles.join(QLatin1String(";")));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWNLOADING_PIECE), downloadingFiles.join(QLatin1String("\n")), Qt::ToolTipRole);
return m_listModel->item(row, PeerListDelegate::IP);
}
void PeerListWidget::updatePeer(const QString &ip, const BitTorrent::PeerInfo &peer)
void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer)
{
QStandardItem *item = m_peerItems.value(ip);
int row = item->row();
if (m_displayFlags) {
if (m_resolveCountries) {
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
@@ -364,6 +433,9 @@ void PeerListWidget::updatePeer(const QString &ip, const BitTorrent::PeerInfo &p
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), peer.relevance());
QStringList downloadingFiles(torrent->info().filesForPiece(peer.downloadingPieceIndex()));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWNLOADING_PIECE), downloadingFiles.join(QLatin1String(";")));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWNLOADING_PIECE), downloadingFiles.join(QLatin1String("\n")), Qt::ToolTipRole);
}
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname)

View File

@@ -68,8 +68,8 @@ public:
~PeerListWidget();
void loadPeers(BitTorrent::TorrentHandle *const torrent, bool forceHostnameResolution = false);
QStandardItem *addPeer(const QString &ip, const BitTorrent::PeerInfo &peer);
void updatePeer(const QString &ip, const BitTorrent::PeerInfo &peer);
QStandardItem *addPeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer);
void updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer);
void updatePeerHostNameResolutionState();
void updatePeerCountryResolutionState();
void clear();
@@ -77,6 +77,7 @@ public:
private slots:
void loadSettings();
void saveSettings() const;
void displayToggleColumnsMenu(const QPoint&);
void showPeerListMenu(const QPoint&);
void banSelectedPeers();
void copySelectedPeers();
@@ -92,7 +93,8 @@ private:
QSet<QString> m_missingFlags;
QPointer<Net::ReverseResolution> m_resolver;
PropertiesWidget *m_properties;
bool m_displayFlags;
bool m_resolveCountries;
bool m_wasCountryColHidden;
QShortcut *m_copyHotkey;
};

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