Compare commits

..

175 Commits

Author SHA1 Message Date
sledgehammer999
c6fe1d3620 Bump to 3.3.5 2016-06-20 04:22:46 +03:00
sledgehammer999
73f08dd6e1 Update Changelog. 2016-06-20 04:19:41 +03:00
sledgehammer999
22dc71f365 Fix build. 2016-06-20 04:13:44 +03:00
ngosang
455cc29dad [search engine] Remove filters from Demonoid 2016-06-20 02:40:47 +03:00
ngosang
9d39a2cddc [search engine] Remove filters from Torrentreactor and BTDigg 2016-06-20 02:40:46 +03:00
sledgehammer999
efeb46571b Sync translations from Transifex and run lupdate. 2016-06-20 02:36:16 +03:00
sledgehammer999
a7b564bb2e Fix parsing of eMule .DAT filters. Closes #5281. 2016-06-20 02:25:12 +03:00
sledgehammer999
10df509bce Fix API_VERSION and API_VERSION_MIN numbers. 2016-06-20 02:18:19 +03:00
sledgehammer999
7b394d6e99 Don't use QCollator for sorting on Windows. Closes #5238 and #5240. 2016-06-20 02:04:37 +03:00
Chocobo1
f1968de2c9 Improve usability of "Run External Program"
Users can write (platform dependent) shell scripts now.
2016-06-20 02:04:37 +03:00
Chocobo1
b5db0eeec1 Add log messages 2016-06-20 02:04:37 +03:00
Chocobo1
3ed803f59b Fix widget name collision.
Small cleanup on Downloads page.
2016-06-20 02:04:36 +03:00
Chocobo1
ff10702bfd Let user able to specifiy a filter when choosing an IP filter file 2016-06-20 02:04:36 +03:00
Chocobo1
73d3664f92 Fix reload button size is not the same size as "..." button.
Rename Connection tab page
2016-06-20 02:04:36 +03:00
Chocobo1
aa6025aa87 Rename "options.ui" to "optionsdlg.ui"
Rename class name "options_imp" to "OptionsDialog"
Rename "options_imp.cpp" to "optionsdlg.h"
2016-06-20 02:04:35 +03:00
Chocobo1
a344886ef6 Move options ui as a pointer member variable 2016-06-20 02:04:35 +03:00
Chocobo1
c181019f40 Follow header inclusion rule 2016-06-20 02:04:21 +03:00
Chocobo1
5962efde23 Cleanup Connection page in Option dialog.
Add vertical spacer, closes #845.
2016-06-20 02:04:21 +03:00
Eugene Shalygin
d43466d466 Fix empty tooltips showed at the progress bar borders
If tooltip text contains an empty HTML body, Qt still shows it as an
empty rectangle. Thus, output HTML tags only if we are within the
image region, and return true empty string if we are at the borders.
2016-06-20 02:04:21 +03:00
Eugene Shalygin
29fb8e8085 Replace TorrentHandle::fileExtremityPieces() with TorrentInfo::filePieces() 2016-06-20 02:04:20 +03:00
Eugene Shalygin
b076ff68ac Show files in tooltips for pieces progress bars
In addition to the current tooltip, which shows color legend, if user
holds the Shift key during hovering we show another tooltip which
contains a table of contents for the piece under the moue cursor. The
table lists file sizes and names. If the cursor points to a part of a
file which spans several pieces, those pieces are highlighted.
2016-06-20 02:04:20 +03:00
Eugene Shalygin
5f2da3a529 Follow project coding style. Issue #2192. 2016-06-20 02:04:20 +03:00
ngosang
576fbe5dc1 Update Python version requirements 2016-06-20 02:04:19 +03:00
Eugene Shalygin
8fc7f3fdc0 uncrustify: replace "add" with "force" for sp_ parameters
This will make uncrustify able to remove excessive spaces
2016-06-20 02:04:19 +03:00
Chocobo1
becd67ac84 Delete all mainwindow child widgets. Closes #4871, #5049.
Some of the `delete` can be handled by the findChild loop
2016-06-20 02:04:19 +03:00
Chocobo1
7761a2604a Use QAtomicInt to guarantee cleanup() is only executed once 2016-06-20 02:04:18 +03:00
ngosang
c6546db138 Minor changes in Search UI 2016-06-20 02:04:18 +03:00
ngosang
f0dd4d5673 [Search engine] Update Torrentz 2016-06-20 02:04:18 +03:00
ngosang
7f245b63d7 [Search engine] Update PirateBay 2016-06-20 02:04:17 +03:00
ngosang
af17f4df9e Minor fix: typo and two warnings 2016-06-20 02:04:17 +03:00
sledgehammer999
3ec2f94b27 Fixup previous commits and allow IPv6 interface addresses too. 2016-06-20 02:04:17 +03:00
Sjoerd van der Berg
d7f1beb7f1 Use a combo box to select the IP address you want to listen on for a specific interface 2016-06-20 02:04:16 +03:00
Sjoerd van der Berg
8c7a4ab86c Fix variable name 2016-06-20 02:04:16 +03:00
Sjoerd van der Berg
ce2d42a264 Change back to the original names for the announce address
Only allow ip's through that match the currenrly selected network interface and address
2016-06-20 02:04:16 +03:00
Sjoerd van der Berg
dc011a5599 Add option to bind directly to an IP instead of using a network Interface 2016-06-20 02:04:16 +03:00
Eugene Shalygin
3c6b79805c Use QPalette::LinkVisited color for downloaded items in search results 2016-06-20 02:04:15 +03:00
sledgehammer999
7e7055f2ff Sync translations from Transifex and run lupdate. 2016-06-20 02:04:12 +03:00
sledgehammer999
18c9a65340 Rename ASM to TMM and related stuff. 2016-06-20 02:03:08 +03:00
sledgehammer999
f5ce39a36a Add tooltip to the saving mode checkbox. 2016-06-20 02:03:08 +03:00
sledgehammer999
58e4f9d38e Slim down 'Downloads' page of options window. 2016-06-20 02:03:08 +03:00
botmtl
5bd7dce396 default RSSRefreshInterval to 30 mins
closes issue #5235 (https://github.com/qbittorrent/qBittorrent/issues/5235)
2016-06-20 02:03:07 +03:00
sledgehammer999
d9d49b6d0b Fix gcc 6 compilation with qmake. See #5237. 2016-06-20 02:03:07 +03:00
Chocobo1
cc48ca1fdc Use QString::localeAwareCompare for comparsion 2016-06-20 02:03:07 +03:00
Chocobo1
2dc6002064 Use boost:circular_buffer instead of QList.
QList has to store an additional pointer for each element which leads to bad space efficiency.
2016-06-20 02:03:06 +03:00
ngosang
1478b21e8d [WebUI] Expose Add trackers feature 2016-06-20 02:03:06 +03:00
Gabriele
726c2fd56d WebUI: Add command to get the logs
Add /query/getLog and /query/getPeerLog to respectively retrieve
the main log and the peer log.

GET /query/getLog
Params:
 - normal (bool): include normal messages (default true)
 - info (bool): include info messages (default true)
 - warning (bool): include warning messages (default true)
 - critical (bool): include critical messages (default true)
 - last_known_id (int): exclude messages with id <= 'last_known_id'

GET /query/getPeerLog
Params:
 - last_known_id (int): exclude messages with id <= 'last_known_id'
2016-06-20 02:02:49 +03:00
SeigneurSerpent
df86d66702 Improve stack trace for windows by including source filenames and line numbers 2016-06-20 02:02:48 +03:00
Eugene Shalygin
223415fb69 update uncrustify config 2016-06-20 02:02:48 +03:00
thalieht
a4c9b667a7 resize column to contents only when they're visible
refactor updatePeerCountryResolutionState
deprecate m_resolveCountries var
2016-06-20 02:02:48 +03:00
Chocobo1
5f8e05ba50 Cleanup headers
Code formatting
2016-06-20 02:02:47 +03:00
Chocobo1
ed2689de15 Add lookup function to get PathType display names 2016-06-20 02:02:47 +03:00
Chocobo1
9e124527e1 Improve error messages for "Auto download torrents" 2016-06-20 02:02:47 +03:00
Vladimir Golovnev (Glassez)
59971aa577 Use new alert dispathing API 2016-06-20 02:02:47 +03:00
Ibrahim Tachijian
164ca0289a Add 'Added on' column in Webui. Closes #5145,#1092,#738 2016-06-20 02:02:46 +03:00
Chocobo1
c0aa50d74b Change ambiguous text "Copy selected" to "Copy IP:port" 2016-06-20 02:02:46 +03:00
Chocobo1
66d7dc751c Change "Auto download torrent" default save path to "default location". 2016-06-20 02:02:46 +03:00
Chocobo1
c1738f97f1 Improve wordings in "Auto download torrent" section 2016-06-20 02:02:45 +03:00
Eugene Shalygin
105874613a Fix crash when adding torrent via a magnet url
The file guard object has to be initialised in this case too because it
is accessed when dialog gets accepted or cancelled. Initialise it with
empty path.
2016-06-20 02:02:45 +03:00
Eugene Shalygin
d6829b253b Fix a typo in TorrentFileGuard 2016-06-20 02:02:45 +03:00
Eugene Shalygin
c1291539bf uncrustify: add constructor initialization list options 2016-06-20 02:02:45 +03:00
Eugene Shalygin
3060c979f4 Add option to automatically remove .torrent files upon adding
Some browsers do not download files, intended for immediate opening,
into a temporary directory, and thus a regular download directories
accumulate those unneeded files.

The option allows qBittorrent to clean after itself and delete those
files whether they were succesfully added or not (user-selectable
policy).
2016-06-20 02:02:41 +03:00
sledgehammer999
d71a18b945 Update definition of QBT_USES_QT5 to be true for qt >= 5.2.0 2016-06-20 02:01:34 +03:00
Chocobo1
4078fc5e2d Add case-sensitive & case-insensitive natural sort helper function
Fix helper function not being thread-safe
Use QBT_USES_QT5 define
2016-06-20 02:01:34 +03:00
Chocobo1
b7cb53a251 Cleanup headers
Move `class NaturalCompare` to .cpp file
2016-06-20 02:01:33 +03:00
Chocobo1
275a775769 Minor code formatting 2016-06-20 02:01:33 +03:00
Chocobo1
1c49ff1df6 Sort labels with naturalCompare(). Closes #3919. 2016-06-20 02:01:18 +03:00
Chocobo1
fc77fdbcb5 Replace naturalSort() with naturalCompare(). 2016-06-20 02:01:18 +03:00
Chocobo1
e0e757b610 Use qmake built-in test function files to locate translation files 2016-06-20 02:01:18 +03:00
Chocobo1
33fe9b6d87 Use POSIX shell 2016-06-20 02:01:17 +03:00
Chocobo1
fca78d1a3f Re-enable cache for linux builds
Enable cache for OSX builds
2016-06-20 02:01:17 +03:00
Chocobo1
92794a786f Suppress warning on OSX builds 2016-06-20 02:01:17 +03:00
zywo
08d9ad7f80 Set about tab font-size to default 2016-06-20 02:01:16 +03:00
Chocobo1
6023093329 Fix "IP Filtering - Apply to trackers" wasn't being applied. Closes #5217. 2016-06-20 02:01:16 +03:00
Eugene Shalygin
c070193c30 uncrustify: honour pointer and reference symbols alignment rules
Stick the '*' and '&' symbols to the variable name if there is one.
Also fix empty function bodies collapse option: replace it with the
eat_blanks_before_close_brace setting.
2016-06-20 02:01:16 +03:00
Eugene Shalygin
633fb7a7de refactor SettingsStorage class
1. Extract "transaction" support for QSettings into separate class
TransactionalSettings.
2. Define macrto with explicit name for the case when this "transaction"
support is needed.
3. A bit optimize QHash <-> QSettings copying: replace assign with
insert() and remove repetitive key lookups.
4. In save() check dirty status before getting the lock too.

The changes from items 1 and 2 make text more structured and the logic
of the SettingsStorage class gets separated from the implementation level
task of guarding the settings serialization. The changes in 3 and 4 do not
make the app much faster, but neither make any harm to the code readability.
2016-06-20 02:01:15 +03:00
Eugene Shalygin
14a37e8d45 cmake: add Qt::DBus library dependency for base module
This is needed for powermanagement and was mistakenly forgotten
2016-06-20 02:01:15 +03:00
Eugene Shalygin
d03a71899e cmake: mark libtorrent and qtsingleapplication include dirs as system 2016-06-20 02:01:15 +03:00
sledgehammer999
283338f5f3 Don't show added torrent notification during startup. 2016-06-20 02:01:15 +03:00
sledgehammer999
b600253313 Display notifications when a torrent is added. Closes #334 and #915. 2016-06-20 02:01:14 +03:00
Vladimir Golovnev (Glassez)
c97b5ab617 Fix .!qB extension is added when disabled 2016-06-20 02:01:14 +03:00
Vladimir Golovnev (Glassez)
9016b698c9 Use truncated torrent hash in temp path 2016-06-20 02:01:14 +03:00
Vladimir Golovnev (Glassez)
eb7a7b9c04 Use unique temp directories
Save torrent in temp_path/<torrent_hash> directory.
Closes #5154.
2016-06-20 02:01:13 +03:00
Eugene Shalygin
9e0a3ee692 cmake: reflect qmake project changes 2016-06-20 02:00:51 +03:00
ngosang
c899ed5b50 Fix duplicate network interfaces. Closes #5131 2016-06-20 02:00:50 +03:00
Vladimir Golovnev (Glassez)
a6c50aff95 Delete Import Torrent Dialog 2016-06-20 02:00:50 +03:00
Vladimir Golovnev (Glassez)
ab4a608342 Fix rechecking after torrent is finished 2016-06-20 02:00:50 +03:00
Vladimir Golovnev (Glassez)
e7cfd7a31d Try to find incomplete files for new torrent 2016-06-20 02:00:49 +03:00
Vladimir Golovnev (Glassez)
c4ea13b284 Fix torrent adding with existing data 2016-06-20 02:00:49 +03:00
Chocobo1
5d09639109 Fix mutually exclusive radio buttons can be unselected. 2016-06-20 02:00:49 +03:00
Chocobo1
885f5b4bee Enable word wrap for "Run external program" help text 2016-06-20 02:00:49 +03:00
Chocobo1
3cd06c457a Fix toolbar resizing when m_searchFilter appears/disappears. Closes #5120. 2016-06-20 01:53:08 +03:00
Chocobo1
373e22660d Disable comboHideZero when checkHideZero is unchecked.
Disable `checkLimituTPConnections` when `checkuTP` is unchecked.
Minor code formatting
2016-06-20 01:53:08 +03:00
Chocobo1
92f58a40e7 Morph QToolButton to QPushButton 2016-06-20 01:53:07 +03:00
Chocobo1
dbf5a264bd Fix path to resource 2016-06-20 01:52:54 +03:00
ngosang
145dcf9efe [Search engine] Fix LegitTorrents plugin 2016-06-20 01:52:54 +03:00
ngosang
f05e25c750 [Search engine] Fix Mininova plugin 2016-06-20 01:52:54 +03:00
ngosang
fc02377171 [Search engine] Fix Torrentz plugin for Python2 2016-06-20 01:52:53 +03:00
Eugene Shalygin
3816052b0a Add "slots" to WORDS keyword.
Uncrustify inserts line break between an access specifier and "slots".
However, it keeps these words combinations if "Q_SLOTS" is used.
Its built-in parser classifies "Q_SLOTS" as WORD token, thus let's add
"slots" to that class.
2016-06-20 01:52:53 +03:00
ngosang
fae583e0da Fix Add tracker dialog URL download 2016-06-20 01:52:53 +03:00
ngosang
1114c198ca Fix Add tracker dialog empty trackers 2016-06-20 01:52:52 +03:00
Eugene Shalygin
c5f9567c0b cmake: raise minimal cmake version to 3.5 and use its features 2016-06-20 01:52:52 +03:00
Eugene Shalygin
e9a5768e4f cmake: drop explicit Boost dependency
We get it implicitly from libtorrent
2016-06-20 01:52:52 +03:00
Eugene Shalygin
858e5f8db8 cmake: add imported target for Libtorrent 2016-06-20 01:52:51 +03:00
Vladimir Golovnev (Glassez)
0afa83dbfa Add coding style rule for header inclusion order 2016-06-20 01:52:51 +03:00
ngosang
fc95ce310e Fix Update all button height in RSS tab 2016-06-20 01:52:51 +03:00
ngosang
e2c9e7b877 Minor change in Stats dialog 2016-06-20 01:52:51 +03:00
sledgehammer999
b1e62ca006 Display the filepath when a torrent fails to load. Closes #100 and #805. 2016-06-20 01:52:50 +03:00
Jerome Leclanche
a063ebd396 LICENSE: Fix mixed indenting 2016-06-20 01:52:50 +03:00
Jerome Leclanche
728dd744bb LICENSE: Remove copy/paste error 2016-06-20 01:52:49 +03:00
Chocobo1
a0c1ee76a2 Fix qBittorrent doesn't exit immediately when "all donwloads are done -> exit" option enabled.
This fix is provided by glassez.
2016-06-20 01:52:49 +03:00
Chocobo1
c7b15b9cc5 Enable access to shutdown functions when configured with --disable-gui option 2016-06-20 01:52:29 +03:00
Chocobo1
0770fe8b09 Fix Coverity Scan 143909.
Also, the setting "Confirmation on auto-exit when downloads finish" wasn't working before.
2016-06-20 01:52:28 +03:00
sledgehammer999
cf98220c40 Fix log menu item position broken by 1760beed17. 2016-06-20 01:52:28 +03:00
sledgehammer999
2eb393ae9a Bump API_VERSION to 11. 2016-06-20 01:52:26 +03:00
buinsky
d65d11d64c WebUI: Select category on right click 2016-06-20 01:50:55 +03:00
buinsky
f4fdb80cc9 WebUI: Don't show several context menus at the same time 2016-06-20 01:50:54 +03:00
buinsky
50a0ce1da2 WebUI: Adjust context menu position 2016-06-20 01:50:54 +03:00
buinsky
2e9370481d WebUI: Implement delete torrents by category 2016-06-20 01:50:54 +03:00
buinsky
c60c58b092 WebUI: Implement pause torrents by category 2016-06-20 01:50:53 +03:00
buinsky
ba5dded076 WebUI: Implement start torrents by category 2016-06-20 01:50:53 +03:00
buinsky
eb36949e87 WebUI: Implement removing unused categories 2016-06-20 01:50:53 +03:00
buinsky
b7358a3039 WebUI: Implement removing categories 2016-06-20 01:50:52 +03:00
buinsky
d0f082e238 WebUI: Implement adding categories 2016-06-20 01:50:52 +03:00
buinsky
b301444f5e WebUI: Add empty context menu to categories filter 2016-06-20 01:50:52 +03:00
buinsky
900a80adc1 WebUI: Rename function updateCategoryFN to setCategoryFN 2016-06-20 01:50:51 +03:00
buinsky
d4887ee736 WebUI: Refactor ContextMenu class 2016-06-20 01:50:37 +03:00
Chocobo1
e23566cde1 Rename files:
confirmshutdowndlg.ui => shutdownconfirmdlg.ui
shutdownconfirm.cpp => shutdownconfirmdlg.cpp
2016-06-20 01:50:36 +03:00
Chocobo1
b5f30a6efb Enlarge dialog size. Closes #5091.
This avoids text clipping when using Qt4 lib.
2016-06-20 01:50:36 +03:00
Chocobo1
deb1d54999 Rename enum
Change identifier from ShutdownAction to ShutdownDialogAction
Change enum value from None to Exit
2016-06-20 01:50:36 +03:00
Chocobo1
7779efbc30 Refactor
Add helper function to initialize shutdown message.
Group similar functions together.
Merge shutdown() function into its only caller.
Add override keyword
2016-06-20 01:50:35 +03:00
Chocobo1
acd65e3185 Cleanup header include
Code formatting
2016-06-20 01:50:35 +03:00
Chocobo1
f2dd050ade Fix Coverity Scan cid 143911.
`filesCount()` could return a negative value.
2016-06-20 01:50:35 +03:00
Chocobo1
9c28a48f2e Fix class member initialize order 2016-06-20 01:50:35 +03:00
Chocobo1
3e8af95d30 Fix warnings in mainwindow.ui. Closes #5117.
gui/mainwindow.ui: Warning: The name 'actionExecutionLogs' (QAction) is already in use, defaulting to 'actionExecutionLogs1'.
gui/mainwindow.ui: Warning: action `actionSearch_engine' not declared
gui/mainwindow.ui: Warning: action `actionRSS_Reader' not declared
2016-06-20 01:50:19 +03:00
Eugene Shalygin
cba9d222de cmake: add imported target for QtSingleApplication
This simplifies cmake code a bit: we remove if's and just generate
different target (imported or alias) with the same name and use it
unconditionally.
2016-06-20 01:50:18 +03:00
Eugene Shalygin
b137eb01ed cmake: fix Qt resources linkage. Closes #5080
Qt resource is innitialized by a static object constructor (see
https://wiki.qt.io/QtResources). When we put resources into a static
library, the linker removes that static objects and thus the resources
themselves. To correct that we append resources to the main executable
sources list. This is done via custom function qbt_target_sources which
knows where to read the executable' name.
2016-06-20 01:50:18 +03:00
Vladimir Golovnev (Glassez)
fd156580a0 Fix coding style rules
Closes #5075
2016-06-20 01:50:18 +03:00
Chocobo1
21c17f2a81 Move m_searchPattern widget from code to .ui file 2016-06-20 01:50:18 +03:00
Chocobo1
5f19cd2c0e [Search] Add seperator for categories & plugins
Sort categories & plugins entries
Rename & reorder entries
Use full name for plugins
2016-06-20 01:50:17 +03:00
Chocobo1
9279fedd49 [Search] match combobox height to button height 2016-06-20 01:50:17 +03:00
Chocobo1
2361d6f12d [Search] minor cleanups 2016-06-20 01:50:00 +03:00
Vladimir Golovnev (Glassez)
78bfbc5669 Use nova2dl.py script instead of DownloadManager
Closes #5026
2016-06-20 01:49:59 +03:00
sledgehammer999
6101f11673 Sync translations from Transifex and run lupdate. 2016-06-20 01:49:56 +03:00
sledgehammer999
15361a6c16 Follow project coding style. Issue #2192. 2016-06-20 01:36:35 +03:00
Anton Lashkov
835a999464 SpeedPlotView: code correction 2016-06-20 01:36:34 +03:00
Anton Lashkov
b9b0739efd SpeedPlotView: Simplify code using PointData struct 2016-06-20 01:36:21 +03:00
Anton Lashkov
a34c072b7a SpeedPlotView: Use separate buffers for periods with reduce number of points. Fix coding style. 2016-06-20 01:36:20 +03:00
Anton Lashkov
72e88ba7ec SpeedPlotView: Save int's instead of double's 2016-06-20 01:36:20 +03:00
Anton Lashkov
afb143cad1 SpeedPlotView: Replace QQueue by boost::circular_buffer, reduce number of points to draw, increase legend background size 2016-06-20 01:36:20 +03:00
Chocobo1
1ca5d10abb Fix resizing bug in "add torrent dialog". Closes #5036.
1. Place all widgets in "Torrent information" into the same grid layout
instead of different layouts.
2. Tweak tab order.
2016-06-20 01:36:01 +03:00
Chocobo1
67675b6cdc Fix long text clipping. Closes #5091. 2016-06-20 01:36:00 +03:00
Eugene Shalygin
ef47983d8d Remove duplicated code from search tab and widget
Both download handlers (in the tab and widget classes) convert model
index into an URL, set row color, and call download function.
Make the download button handler (in the SearchWidget class) call the
slot of the SearchTab class.
2016-06-20 01:36:00 +03:00
Eugene Shalygin
cf47517ee3 cmake: reflect qmake project changes 2016-06-20 01:36:00 +03:00
sledgehammer999
33199bbd74 Fix qt5 bundle on OSX. Closes #4014. 2016-06-20 01:35:59 +03:00
sledgehammer999
4fb735c6d1 Change gpg signing key.
I forgot the passphrase to the old one.
2016-06-20 01:35:59 +03:00
Vladimir Golovnev (Glassez)
99ca42bf48 Fix MainWindow coding style (Issue #2192) 2016-06-20 01:35:59 +03:00
Vladimir Golovnev (Glassez)
f6b4b5f6f0 Fix macro define cross over access specifiers 2016-06-20 01:35:59 +03:00
Vladimir Golovnev (Glassez)
6f73c32fcc Delete old RSS cookies dialog 2016-06-20 01:35:42 +03:00
Vladimir Golovnev (Glassez)
c1611988b4 Create basic cookies management dialog 2016-06-20 01:35:42 +03:00
Vladimir Golovnev (Glassez)
fbe1d2d78a Implement basic cookies model 2016-06-20 01:35:41 +03:00
Chocobo1
34fec15ceb Improve TransferListDelegate::sizeHint 2016-06-20 01:35:41 +03:00
ngosang
1977e4f24d Fix uic warnings. Closes #5056 2016-06-20 01:35:41 +03:00
Chocobo1
5774d27c7b Follow project coding style. Issue #2192. 2016-06-20 01:35:17 +03:00
Chocobo1
3a6a857371 Let windows handle all widgets scaling.
Declare ourselves as a Non DPI–aware app.
Tested with Qt 5.6.
2016-06-20 01:02:07 +03:00
Eugene Shalygin
e2f88feaf9 Optimize widgets inside of the search tab
Since we already have searchtab.ui, let's set up all the widgets there.
Additionally, save a bit of vertical space by putting results label in
a row with the filter widgets.
2016-06-20 01:02:07 +03:00
Eugene Shalygin
16fec04b6a Implement search filters in the proxy model. Partially closes #972 2016-06-20 01:02:06 +03:00
sledgehammer999
fbac4afa2d Fix indentation. 2016-06-20 01:02:06 +03:00
Eugene Shalygin
d6e9736af1 update uncrustify config 2016-06-20 01:02:06 +03:00
sledgehammer999
76b3c72c86 Fix memory leak. 2016-06-20 01:02:05 +03:00
sledgehammer999
ef954fea55 Use SettingsStorage instead. 2016-06-20 01:00:18 +03:00
Chocobo1
11d085712e Minor refactor 2016-06-20 01:00:10 +03:00
Vladimir Golovnev (Glassez)
e4bc7852e5 Fix save path bugs 2016-06-20 00:59:57 +03:00
Vladimir Golovnev (Glassez)
3d107e0588 Implement Advanced Saving Management subsystem
Closes #4696
2016-06-20 00:59:37 +03:00
Vladimir Golovnev (Glassez)
8e5c31ae82 Move Preferences backend code into SettingsStorage class
Closes #4764
2016-06-20 00:52:50 +03:00
Vladimir Golovnev (Glassez)
3c03ccc14a Make AddNewTorrentDialog behavior uniform 2016-06-20 00:52:44 +03:00
241 changed files with 121343 additions and 82097 deletions

View File

@@ -30,10 +30,7 @@ notifications:
# container-based builds
#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: ccache
# opt-in Ubuntu Trusty
sudo: required
@@ -80,12 +77,6 @@ before_install:
- 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
@@ -117,27 +108,44 @@ install:
# Qt
if [ "$qt" = 4 ]; then sudo apt-get -qq install qt4-default libqt4-dev ; fi ;
if [ "$qt" = 5 ]; then sudo apt-get -qq install qt5-default qtbase5-dev qttools5-dev-tools ; fi ;
# ccache
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
dpkg-query -L ccache && export use_ccache=true ;
ccache -V && ccache --show-stats && ccache --zero-stats ;
fi ;
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# dependencies
brew update > /dev/null && brew install colormake libtorrent-rasterbar ;
brew update > /dev/null && brew install colormake ccache libtorrent-rasterbar ;
# Qt
if [ "$qt" = 4 ]; then brew install qt ; fi ;
if [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 ; fi ;
if [ "$qt" = 4 ]; then brew install qt && ln -s /usr/local/Cellar/qt/4.8.7_2/plugins /usr/local ; fi ;
if [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5 && ln -s /usr/local/Cellar/qt5/5.6.0/plugins /usr/local ; fi ;
# ccache
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH" && export use_ccache=true ;
ccache -V && ccache --show-stats && ccache --zero-stats ;
fi ;
fi
script:
- 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
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile ; # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile ;
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile ;
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" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS" ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "$TRAVIS_BUILD_DIR/src/" && macdeployqt "$qbt_exe.app" && cd "$qbt_exe.app/Contents/MacOS" ; fi
- ./$qbt_exe --version
after_script:

View File

@@ -1,53 +0,0 @@
-----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-----

53
5B7CC9A2.asc Normal file
View File

@@ -0,0 +1,53 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2
mQINBFcNIIwBEACpGHvHW9ku7rwCSc2Dv4gh3MO3HPoP7Ba4RiEKwa7SCbPzc0DL
JypV4gNfnrpiO7bWVh5v+otbZTkQeNXWbx6hDUa2e5GCCuJifIu3PxpmMcNJFvvF
nk5QRf6dtz4Sm2x6joYprvsEUjyk+wHC016/0g7yhc/w0sclXlpKK+8Pl5DFrf5C
i5uljy3oJgl54D2yYAvxu3BrdTVKhLVYADUf1Fl3b5pV7VJwr+9wGuTqkORe1rpi
9NGWXUaTmKF8+XAJxlbYIUOZQpQ02clFxz0T7o/+m74N8tK9j7g8H2Q3QwtKi0q1
gI48LqI/EuZHIaRz/3pEVISlIpWzGqBL/G4I/UtzJLHyvySsqWXAKllKpk97XX77
XxFy3VL3fR7o4IohAj5fD083X8tuBIP2dxmHzxHTWveKBlEV6C4MdtVRow8ia3lu
RKLz6PF0hBBpebAP4MWAN8cy4ePBCe9BvyI2+3tPgqtlC2tEZLnRru6mtagPi4sj
Yo/iFkSQdTXrxeyrMJh161gsWl16JeAfz4Dq8IBoUA1hXIjfM9FcIv1rCY6Y8JwS
TtWMGYtzIcqE71wZxqnJuyFZkgC14NDTLgUwLf8XJOTWlMW9CY+tStjjw+sNoIPf
p7YQCmss4p5J8flnxH4xJ8ogOHxENidA+Z/J9mtGjxXIXHavPlO3IEg/DwARAQAB
tHFzbGVkZ2VoYW1tZXI5OTkgKFVzZWQgZm9yIHNpZ25pbmcgcUJpdHRvcnJlbnQg
c291cmNlIHRhcmJhbGxzIGFuZCBiaW5hcmllcyB2Mi4pIDxzbGVkZ2VoYW1tZXI5
OTlAcWJpdHRvcnJlbnQub3JnPokCNwQTAQgAIQUCVw0gjAIbAwULCQgHAgYVCAkK
CwIEFgIDAQIeAQIXgAAKCRBuSi0CW3zJojB2D/0bKlelRDQDtWzfRyxrdhe5pgAt
x1AsN/Cl7h8zlbAw38bL+jQ2/GmtzwzEqPfQc7IFnbeg0PZ58p7Hikj9h6JEhkyA
1qekkriclUmblEwDne3TjPixqgoBfNcDQu74dT08XpM8auFQo31/jJ104903o0O5
+CPOPn2KTdwpcSpwAVIj/3H96gZWegJDNpdByJUVbzYCt1erJ6I0ZURKhzU1VTJj
ZdEGB2YsvYpt5rsi41IYZZG33jMsPxSDDNJ/MiLXxkn08ZawNET6fnkEJJ37n9Pw
82lTZjFEFU+KTMT7dNjIejWCRgHVLgW8sO2lCPqMiFfWymD/N3sFpBO+UI86y5ds
hfGFAWcgSq9pVjuW4sbX3PntBnoNd+geDD1Ic4rP3jHRe5HuYGhtHO6xv/r7HeY5
HiShCTSSDBJqFmhfjrCo0nISKnzyxgO/rY9vFlwXsKkTyL7s53ONkjwK34WmGnya
tXdjBWShzAiTfF5hephfBSszmoBG2C8Jcu6P5n4buBY4RCsEa+6jE0R1vCtmpVwx
WrXOeN2kGYMpAkPK1L69Le0FofgUDKlaFMv7KRl4R367xNRukYrsKwVlontJ+Y72
X5t1BeRn8VSp0IzhssNXM8a4bTE8lvs889DOS2vgWEHIi0iyIesJYWPs4AKUw4rG
EDwWxtTS0a7Rfx3DxLkCDQRXDSCMARAAqMIVJizEJp205c546IN75xeYiFszNXcs
3768IY8bOoWj+rTwt2wIwtL/3O5K2dG79CSt2H5o6BPKmq43tOO60YW3Yk3m9BB/
gnAVqk0QOPr5O8+yeBzdElU8CZh6y6zZMWugSkNmTDm6jZzPhgNjcjrit/dl9+0D
GqJQcqoD8WzEWNcWrMHVz9cDewnLSVkwR758mZMaIiL7R10MZ++tNrC0j69UINqx
+9z1r1J07+NNnxqSTxVRcbjPYtM9E+tUiVFS2HPWN9ShVDkBAEdoWh90qzRaMiFl
2NGNGOD1iHx/xr06RMeGEEXt2vhSlhfMW2YQW+UD2jzlFbARf53v39MUKKscGuIp
BhxGw3JCq4l6qLW/bDkgnoXlOhZDmhQm6OpsjAyk9IEdd3ponSc7yYD3mUkJKR9e
TaALD5t6TQGyNHakb4UfoXtE2RR78cbPlLIwag7eQ8GsNA+dfjowmOZdojx3ROsH
ZdGQwb0YFLjuKAusA3TY+lCfbS6kzE2iI2DuaW+3dICcLrYuibbVb0CBNHyD+8KE
tczdur/wm0lhqyVJkGyZKZT8C2cPxywKgy1Rn6F8Yfmj0Lna3nvtaZu0ZUS4/8Li
t5PcOso1lSmYBuD6yq+GEAMCnUmn1Pm8eZRMlxxQuTPvyJKQrRDhbtAAr472MSno
JKlS4SfaUF0AEQEAAYkCHwQYAQgACQUCVw0gjAIbDAAKCRBuSi0CW3zJomZDD/9I
Jmzd5hiEzntlp84pyIJcfyIRe4KImvldAy6T02OSIbF1HzCNnwmqIPob6MOdMZ+K
NwMK0htRkrRr/zM034+lBiWKZt+tVYHu49ioTYXEjAc5qDJE09Sq7HceQnhgE48f
1n54XGT5G2w5gw+/a8Qn1SceE44VwXafL3E1gKaOrrsb1UH/AJhp+W4VMu+7bLXu
7h1tN6v2PhvCYvBt3zyy8Q8xfJ2x7/D1lbF8ATJAiZ/km9x5bRm7OGRliVYaUe1n
yR42fZOj3CBmAR0+lZLgjriqdMXrs+qlBbrmAhkn0XPQXAeaPifKoKIGDAUWIsqD
HqM7imMGT+MR9APfSw8M4enOJWL+HnKpVBEARCEDpaFpJ3u7QRucFybpEhvIymoN
ftyw+urId2Eg2K33NypeZo3M1K2LC65f2Ta7f/sZcIDUTbgW+m334fgVl1KptDA5
DX3U9lTci7mi4uPuAFtbWrB1di4jYrxXYuzFm5g4xTb0Hw3kYIB6WXF+I7i0JaGO
THxPC5X5lIAZrYrkxh+1n1Y1CY+TC8JcTzwORJIbFFm9tD/BHXa4849k4DVvFYCZ
khq+/56FKZfoVByhB+x+2GaMlsBm1uPniO4lAakFPpIi0kaap4UVayQ/7ak+Bhsc
AIHZUy6NtgZkuvW3xdpwp07LYo2ilhMI8RnzmtoRmg==
=tDGM
-----END PGP PUBLIC KEY BLOCK-----

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.2)
cmake_policy(VERSION 3.2)
cmake_minimum_required(VERSION 3.5)
cmake_policy(VERSION 3.5)
project(qBittorrent VERSION 3.4.0.0)

View File

@@ -29,8 +29,10 @@ class MyOtherClass
{
public:
//code
protected:
//code
private:
//code
};
@@ -87,10 +89,14 @@ default:
}
```
#### d. single-line blocks (lambdas, initializer lists etc.) ####
#### d. Brace enclosed initializers ####
Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.<br/>
But you must insert a space between the variable name and initializer.
```c++
{} // empty - space before {
{ body } // spaces around { and before }
Class obj {}; // empty
Class obj {expr};
Class obj {expr1, /*...,*/ exprN};
QVariantMap map {{"key1", 5}, {"key2", 10}};
```
### 2. If blocks ###
@@ -173,11 +179,11 @@ All names should be camelCased.
#### a. Type names and namespaces ####
Type names and namespaces start with Upper case letter (except POD types).
```c++
class ClassName {}
class ClassName {};
struct StructName {}
struct StructName {};
enum EnumName {}
enum EnumName {};
typedef QList<ClassName> SomeList;
@@ -201,7 +207,40 @@ class MyClass
}
```
### 8. Misc.###
### 8. Header inclusion order.###
The headers should be placed in the following order:
1. Module header (in .cpp)
2. System/Qt/Boost etc. headers (splitted in subcategories if you have many).
3. Application headers, starting from *Base* headers.
The headers should be ordered alphabetically within each group (subgroup).<br/>
<br/>
Example:
```c++
// examplewidget.cpp
#include "examplewidget.h"
#include <cmath>
#include <cstdio>
#include <QDateTime>
#include <QList>
#include <QString>
#include <QUrl>
#include <libtorrent/version.hpp>
#include "base/bittorrent/session.h"
#include "base/bittorrent/infohash.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "ui_examplewidget.h"
```
### 9. Misc.###
* Line breaks for long lines with operation:
@@ -211,47 +250,18 @@ 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 />
We allow the use of the **auto** keyword only where it is strictly necessary
(for example, to declare a lambda object, etc.), or where it **enhances** the readability of the code.
Declarations for which one can gather enough information about the object interface (type) from its name
or the usage pattern (an iterator or a loop variable are good examples of clear patterns)
or the right part of the expression nicely fit here.<br/>
<br/>
When weighing whether to use an auto-typed variable please think about potential reviewers of your code,
who will read it as a plain diff (on github.com, for instance). Please make sure that such reviewers can
understand the code completely and without excessive effort.<br/>
<br/>
Some valid use cases:
```c++
template <typename List>
@@ -274,9 +284,17 @@ auto spinBox = static_cast<QSpinBox*>(sender());
* 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.
Before and after the assignment and other binary (and ternary) operators there should be a space.<br/>
There should not be a space between increment/decrement and its operand.<br/>
Some valid use cases:
```c++
for (int a=0; a<b; ++b) {
a += 20;
a = (b <= MAX_B ? b : MAX_B);
++a;
b--;
for (int a = 0; a < b; ++b) {
// code
}
```
@@ -286,5 +304,5 @@ for (int a=0; a<b; ++b) {
* Method definitions aren't allowed in header files
### 9. Not covered above###
###10. 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.

72
COPYING
View File

@@ -12,15 +12,15 @@ exception statement from your version.
----------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -70,7 +70,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -269,7 +269,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -291,64 +291,4 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
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 GNU Library General
Public License instead of this License.
END OF TERMS AND CONDITIONS

View File

@@ -1,3 +1,74 @@
* Mon Jun 20 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.5
- FEATURE: Implement Torrent Management Mode(TMM) (glassez)
- FEATURE: New cookies management dialog and various related fixes (glassez)
- FEATURE: Use unique temp directories (temp_path/<truncated_torrent_hash>). Closes #5154. (glassez)
- FEATURE: Display notifications when a torrent is added. Closes #334 and #915. (sledgehammer999)
- FEATURE: Sort labels with natural sort algorithm in the right-click menu. Closes #3919. (Chocobo1)
- FEATURE: Add option to automatically remove .torrent files upon adding (Eugene Shalygin)
- FEATURE: Add option to bind directly to an IP instead of using a network Interface (Sjoerd van der Berg, sledgehammer999)
- FEATURE: Detailed tooltips on the progress and availability bars in the General button of each torrent. (Eugene Shalygin)
- FEATURE: Let user able to specifiy a filter when choosing an IP filter file (Chocobo1)
- FEATURE: Improve usability of "Run External Program". Users can write (platform dependent) shell scripts now. (Chocobo1)
- PERFORMANCE: Optimize drawing in speed graph (Anton Lashkov, Chocobo1)
- BUGFIX: Fix memory leak. (sledgehammer999)
- BUGFIX: Fix resizing bug in "add torrent dialog". Closes #5036. (Chocobo1)
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all donwloads are done -> exit" option enabled. (glassez, Chocobo1)
- BUGFIX: Display the filepath when a torrent fails to load. Closes #100 and #805. (sledgehammer999)
- BUGFIX: Fix Add tracker dialog empty trackers (ngosang)
- BUGFIX: Fix Add tracker dialog URL download (ngosang)
- BUGFIX: Fix torrent adding with existing data (glassez)
- BUGFIX: Try to find incomplete files for new torrent (glassez)
- BUGFIX: Fix rechecking after torrent is finished (glassez)
- BUGFIX: Fix duplicate network interfaces. Closes #5131 (ngosang)
- BUGFIX: Fix .!qB extension is added when disabled (glassez)
- BUGFIX: Fix "IP Filtering - Apply to trackers" wasn't being applied. Closes #5217. (Chocobo1)
- BUGFIX: Don't resize the Country column needlessly (thalieht)
- BUGFIX: Fix crashing when exiting the program while the Options window was showing. Closes #4871, #5049. (Chocobo1)
- BUGFIX: Fix parsing of eMule .DAT filters. Closes #5281. (thalieht, sledgehammer999)
- WEBUI: Implement in setting/removing/showing categories. (buinsky)
- WEBUI: Add 'Added on' column in Webui. Closes #5145,#1092,#738 (Ibrahim Tachijian)
- WEBUI: Add command to get the logs (pmzqla)
- WEBUI: Expose Add trackers feature (pmzqla)
- WEBUI: Bump API_VERSION and API_VERSION_MIN to 10. (sledgehammer999)
- SEARCH: Implement search filters in the proxy model. (Eugene Shalygin)
- SEARCH: Optimize widgets inside of the search tab (Eugene Shalygin)
- SEARCH: Remove duplicated code from search tab and widget (Eugene Shalygin)
- SEARCH: Use nova2dl.py script instead of DownloadManager. Closes #5026. (glassez)
- SEARCH: Various UI cleanups and optimizations. (Chocobo1, ngosang)
- SEARCH: Fix Torrentz, Mininova, LegitTorrents, PirateBay plugins (ngosang)
- SEARCH: Fix toolbar resizing when m_searchFilter appears/disappears. Closes #5120. (Chocobo1)
- SEARCH: Use QPalette::LinkVisited color for downloaded items in search results (Eugene Shalygin)
- SEARCH: Update Python version requirements (min 2.7.9 / 3.3.0) (ngosang)
- SEARCH: Remove filters from Torrentreactor, BTDigg and Demonoid. It is done in the GUI now. (ngosang)
- RSS: default refresh interval to 30 mins (botmtl)
- COSMETIC: Improve TransferListDelegate::sizeHint (Chocobo1)
- COSMETIC: Fix long text clipping. Closes #5091. (Chocobo1)
- COSMETIC: Minor change in Stats dialog (ngosang)
- COSMETIC: Fix Update all button height in RSS tab (ngosang)
- COSMETIC: Disable `comboHideZero` when `checkHideZero` is unchecked. (Chocobo1)
- COSMETIC: Disable `checkLimituTPConnections` when `checkuTP` is unchecked. (Chocobo1)
- COSMETIC: Enable word wrap for "Run external program" help text (Chocobo1)
- COSMETIC: Fix mutually exclusive radio buttons can be unselected. (Chocobo1)
- COSMETIC: Set About tab font-size to default (zywo)
- COSMETIC: Improve wordings in "Auto download torrent" section (Chocobo1)
- COSMETIC: Change "Auto download torrent" default save path to "default location". (Chocobo1)
- COSMETIC: Change ambiguous text "Copy selected" to "Copy IP:port" (Chocobo1)
- COSMETIC: Improve error messages for "Auto download torrents" (Chocobo1)
- COSMETIC: Slim down 'Downloads' page of options window. (sledgehammer999)
- COSMETIC: Cleanup Connection page in Option dialog. Closes #845. (Chocobo1)
- COSMETIC: Fix reload button size is not the same size as "..." button in options dialog. (Chocobo1)
- WINDOWS: Let Windows handle all widgets scaling. (Chocobo1)
- WINDOWS: Improve stack trace for windows by including source filenames and line numbers (SeigneurSerpent)
- OSX: Fix qt5 bundle on OSX. Closes #4014. (sledgehammer999)
- OTHER: Make AddNewTorrentDialog behavior uniform (glassez)
- OTHER: Optimize the shutdown dialog (Chocobo1)
- OTHER: Enable access to shutdown functions when configured with `--disable-gui` option (Chocobo1)
- OTHER: Delete Import Torrent Dialog. Just use the "add new torrent" dialog. (glassez)
- OTHER: Optimize code for natural sorting (Chocobo1)
- OTHER: Use new alert dispathing API for libtorrent 1.1.x (glassez)
- OTHER: Fix gcc 6 compilation with qmake. See #5237. (sledgehammer999)
* 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)

View File

@@ -29,8 +29,9 @@ 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 />
The key currently used is 4096R/[5B7CC9A2](https://pgp.mit.edu/pks/lookup?op=get&search=0x6E4A2D025B7CC9A2) with fingerprint `D8F3DA77AAC6741053599C136E4A2D025B7CC9A2`.<br />
You can also download it from [here](https://github.com/qbittorrent/qBittorrent/raw/master/5B7CC9A2.asc).<br />
**PREVIOUSLY** the following key was used to sign the v3.3.4 source tarballs and v3.3.4 Windows installer **only**: 4096R/[520EC6F6](https://pgp.mit.edu/pks/lookup?op=get&search=0xA1ACCAE4520EC6F6) with fingerprint `F4A5FD201B117B1C2AB590E2A1ACCAE4520EC6F6`.<br />
### Misc:
For more information please visit:

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
aclocal -I m4
autoconf

View File

@@ -91,3 +91,16 @@ find_package_handle_standard_args(LibtorrentRasterbar DEFAULT_MSG
mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
LibtorrentRasterbar_ENCRYPTION_INDEX)
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::LibTorrent)
add_library(LibtorrentRasterbar::LibTorrent UNKNOWN IMPORTED)
set_target_properties(LibtorrentRasterbar::LibTorrent PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${LibtorrentRasterbar_LIBRARIES}"
INTERFACE_COMPILE_OPTIONS "${LibtorrentRasterbar_DEFINITIONS}"
)
endif()

View File

@@ -79,3 +79,16 @@ ELSE (QTSINGLEAPPLICATION_FOUND)
ENDIF (QTSINGLEAPPLICATION_FOUND)
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)
if(NOT TARGET QtSingleApplication::QtSingleApplication)
add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
)
if(EXISTS "${QTSINGLEAPPLICATION_LIBRARY}")
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${QTSINGLEAPPLICATION_LIBRARY}")
endif()
endif(NOT TARGET QtSingleApplication::QtSingleApplication)

View File

@@ -0,0 +1,17 @@
# a helper function which appends source to the main qBt target
# the target name is read from QBT_TARGET_NAME variable
# sources file names are relative to the the ${qbt_executable_SOURCE_DIR}
function (qbt_target_sources)
set (_sources_rel "")
foreach (_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
file (RELATIVE_PATH _source_rel "${qbt_executable_SOURCE_DIR}" "${_source_abs}")
list (APPEND _sources_rel "${_source_rel}")
endforeach()
target_sources (${QBT_TARGET_NAME} PRIVATE "${_sources_rel}")
endfunction (qbt_target_sources)

View File

@@ -6,8 +6,14 @@ DATADIR = @EXPAND_DATADIR@
MANPREFIX = @EXPAND_MANDIR@
QMAKE_CXXFLAGS += @QBT_CONF_EXTRA_CFLAGS@
INCLUDEPATH += @QBT_CONF_INCLUDES@
LIBS += @LDFLAGS@ @LIBS@
EXTERNAL_INCLUDES = @QBT_CONF_INCLUDES@
EXTERNAL_INCLUDES -= $$QMAKE_DEFAULT_INCDIRS
INCLUDEPATH += $$EXTERNAL_INCLUDES
EXTERNAL_LIBS = @LDFLAGS@ @LIBS@
EXTERNAL_LIBS -= $$QMAKE_DEFAULT_LIBDIRS
LIBS += $$EXTERNAL_LIBS
CONFIG += @QBT_ADD_CONFIG@
CONFIG -= @QBT_REMOVE_CONFIG@

2
dist/mac/Info.plist vendored
View File

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

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.4"
!define PROG_VERSION "3.3.5"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)

View File

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

View File

@@ -3,15 +3,9 @@ set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroLinkQtComponents)
include(QbtTargetSources)
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)
@@ -88,16 +82,22 @@ set(QBT_USES_QT5 ${QT5})
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
if (GUI)
set(QBT_TARGET_NAME qbittorrent)
else (GUI)
set(QBT_TARGET_NAME qbittorrent-nox)
endif (GUI)
add_subdirectory(base)
if (SYSTEM_QTSINGLEAPPLICATION)
find_package(QtSingleApplication REQUIRED)
include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIR})
else (SYSTEM_QTSINGLEAPPLICATION)
include_directories(app/qtsingleapplication)
add_subdirectory(app/qtsingleapplication)
endif (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(app)
add_subdirectory(base)
if (GUI)
add_subdirectory(gui)
endif (GUI)
@@ -106,4 +106,3 @@ if (WEBUI)
add_subdirectory(webui)
endif (WEBUI)
add_subdirectory(app)

View File

@@ -1,3 +1,4 @@
project(qbt_executable)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS
@@ -86,13 +87,10 @@ 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)
@@ -152,6 +150,7 @@ add_executable(${QBT_TARGET_NAME} ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM
set_target_properties(${QBT_TARGET_NAME}
PROPERTIES
AUTOUIC True
AUTORCC True
MACOSX_BUNDLE True
)
@@ -159,14 +158,7 @@ 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)
target_link_libraries(${QBT_TARGET_NAME} ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
if (APPLE)
set(qbt_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")

View File

@@ -33,6 +33,7 @@
#include <QLibraryInfo>
#include <QSysInfo>
#include <QProcess>
#include <QAtomicInt>
#ifndef DISABLE_GUI
#include "gui/guiiconprovider.h"
@@ -48,7 +49,7 @@
#endif // Q_OS_MAC
#include "mainwindow.h"
#include "addnewtorrentdialog.h"
#include "shutdownconfirm.h"
#include "shutdownconfirmdlg.h"
#else // DISABLE_GUI
#include <iostream>
#endif // DISABLE_GUI
@@ -61,6 +62,7 @@
#include "filelogger.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/iconprovider.h"
@@ -71,16 +73,34 @@
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
static const char PARAMS_SEPARATOR[] = "|";
namespace
{
#define SETTINGS_KEY(name) "Application/" name
// FileLogger properties keys
#define FILELOGGER_SETTINGS_KEY(name) SETTINGS_KEY("FileLogger/") name
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld");
const QString KEY_FILELOGGER_MAXSIZE = FILELOGGER_SETTINGS_KEY("MaxSize");
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
//just a shortcut
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
const QString LOG_FOLDER("logs");
const char PARAMS_SEPARATOR[] = "|";
}
Application::Application(const QString &id, int &argc, char **argv)
: BaseApplication(id, argc, argv)
, m_running(false)
#ifndef DISABLE_GUI
, m_shutdownAct(ShutdownAction::None)
#endif
, m_shutdownAct(ShutdownDialogAction::Exit)
{
Logger::initInstance();
SettingsStorage::initInstance();
Preferences::initInstance();
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
@@ -104,20 +124,111 @@ 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();
if (isFileLoggerEnabled())
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
}
void Application::configure()
#ifndef DISABLE_GUI
QPointer<MainWindow> Application::mainWindow()
{
bool fileLogEnabled = Preferences::instance()->fileLogEnabled();
return m_window;
}
#endif
if (fileLogEnabled && !m_fileLogger)
m_fileLogger = new FileLogger;
else if (!fileLogEnabled)
bool Application::isFileLoggerEnabled() const
{
return settings()->loadValue(KEY_FILELOGGER_ENABLED, true).toBool();
}
void Application::setFileLoggerEnabled(bool value)
{
if (value && !m_fileLogger)
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
else if (!value)
delete m_fileLogger;
settings()->storeValue(KEY_FILELOGGER_ENABLED, value);
}
QString Application::fileLoggerPath() const
{
return settings()->loadValue(KEY_FILELOGGER_PATH, QVariant(Utils::Fs::QDesktopServicesDataLocation() + LOG_FOLDER)).toString();
}
void Application::setFileLoggerPath(const QString &value)
{
if (m_fileLogger)
m_fileLogger->changePath(value);
settings()->storeValue(KEY_FILELOGGER_PATH, value);
}
bool Application::isFileLoggerBackup() const
{
return settings()->loadValue(KEY_FILELOGGER_BACKUP, true).toBool();
}
void Application::setFileLoggerBackup(bool value)
{
if (m_fileLogger)
m_fileLogger->setBackup(value);
settings()->storeValue(KEY_FILELOGGER_BACKUP, value);
}
bool Application::isFileLoggerDeleteOld() const
{
return settings()->loadValue(KEY_FILELOGGER_DELETEOLD, true).toBool();
}
void Application::setFileLoggerDeleteOld(bool value)
{
if (value && m_fileLogger)
m_fileLogger->deleteOld(fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
settings()->storeValue(KEY_FILELOGGER_DELETEOLD, value);
}
int Application::fileLoggerMaxSize() const
{
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt();
if (val < 1)
return 1;
if (val > 1000)
return 1000;
return val;
}
void Application::setFileLoggerMaxSize(const int value)
{
if (m_fileLogger)
m_fileLogger->setMaxSize(value);
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000));
}
int Application::fileLoggerAge() const
{
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt();
if (val < 1)
return 1;
if (val > 365)
return 365;
return val;
}
void Application::setFileLoggerAge(const int value)
{
settings()->storeValue(KEY_FILELOGGER_AGE, std::min(std::max(value, 1), 365));
}
int Application::fileLoggerAgeType() const
{
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
return (val < 0 || val > 2) ? 1 : val;
}
void Application::setFileLoggerAgeType(const int value)
{
settings()->storeValue(KEY_FILELOGGER_AGETYPE, (value < 0 || value > 2) ? 1 : value);
}
void Application::processMessage(const QString &message)
@@ -131,6 +242,48 @@ void Application::processMessage(const QString &message)
m_paramsQueue.append(params);
}
void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) const
{
QString program = Preferences::instance()->getAutoRunProgram();
program.replace("%N", torrent->name());
program.replace("%L", torrent->category());
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
program.replace("%C", QString::number(torrent->filesCount()));
program.replace("%Z", QString::number(torrent->totalSize()));
program.replace("%T", torrent->currentTracker());
program.replace("%I", torrent->hash());
Logger *logger = Logger::instance();
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name()).arg(program));
#if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
program.prepend(QLatin1String("cmd.exe /C "));
if (program.size() >= MAX_PATH) {
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(MAX_PATH), Log::CRITICAL);
return;
}
STARTUPINFOW si = {0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
WCHAR *arg = new WCHAR[program.size() + 1];
program.toWCharArray(arg);
arg[program.size()] = L'\0';
if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
delete[] arg;
#else
QProcess::startDetached(program);
#endif
}
void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent)
{
// Prepare mail content
@@ -155,75 +308,58 @@ void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent)
Preferences *const pref = Preferences::instance();
// AutoRun program
if (pref->isAutoRunEnabled()) {
QString program = pref->getAutoRunProgram();
program.replace("%N", torrent->name());
program.replace("%L", torrent->label());
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
program.replace("%C", QString::number(torrent->filesCount()));
program.replace("%Z", QString::number(torrent->totalSize()));
program.replace("%T", torrent->currentTracker());
program.replace("%I", torrent->hash());
QProcess::startDetached(program);
}
if (pref->isAutoRunEnabled())
runExternalProgram(torrent);
// Mail notification
if (pref->isMailNotificationEnabled())
if (pref->isMailNotificationEnabled()) {
Logger::instance()->addMessage(tr("Torrent: %1, sending mail notification").arg(torrent->name()));
sendNotificationEmail(torrent);
}
}
void Application::allTorrentsFinished()
{
#ifndef DISABLE_GUI
Preferences *const pref = Preferences::instance();
bool isExit = pref->shutdownqBTWhenDownloadsComplete();
bool isShutdown = pref->shutdownWhenDownloadsComplete();
bool isSuspend = pref->suspendWhenDownloadsComplete();
bool isHibernate = pref->hibernateWhenDownloadsComplete();
bool will_shutdown = (pref->shutdownWhenDownloadsComplete()
|| pref->shutdownqBTWhenDownloadsComplete()
|| pref->suspendWhenDownloadsComplete()
|| pref->hibernateWhenDownloadsComplete());
bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
if (!haveAction) return;
// Auto-Shutdown
if (will_shutdown) {
bool suspend = pref->suspendWhenDownloadsComplete();
bool hibernate = pref->hibernateWhenDownloadsComplete();
bool shutdown = pref->shutdownWhenDownloadsComplete();
ShutdownDialogAction action = ShutdownDialogAction::Exit;
if (isSuspend)
action = ShutdownDialogAction::Suspend;
else if (isHibernate)
action = ShutdownDialogAction::Hibernate;
else if (isShutdown)
action = ShutdownDialogAction::Shutdown;
// Confirm shutdown
ShutdownAction action = ShutdownAction::None;
if (suspend)
action = ShutdownAction::Suspend;
else if (hibernate)
action = ShutdownAction::Hibernate;
else if (shutdown)
action = ShutdownAction::Shutdown;
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) {
qDebug("Preparing for auto-shutdown because all downloads are complete!");
// Disabling it for next time
pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
m_shutdownAct = action;
}
qDebug("Exiting the application");
exit();
#ifndef DISABLE_GUI
// ask confirm
if ((action == ShutdownDialogAction::Exit) && (pref->dontConfirmAutoExit())) {
// do nothing & skip confirm
}
else {
if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
}
#endif // DISABLE_GUI
// Actually shut down
if (action != ShutdownDialogAction::Exit) {
qDebug("Preparing for auto-shutdown because all downloads are complete!");
// Disabling it for next time
pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
m_shutdownAct = action;
}
qDebug("Exiting the application");
exit();
}
bool Application::sendParams(const QStringList &params)
@@ -247,7 +383,7 @@ void Application::processParams(const QStringList &params)
foreach (QString param, params) {
param = param.trimmed();
#ifndef DISABLE_GUI
if (Preferences::instance()->useAdditionDialog())
if (AddNewTorrentDialog::isEnabled())
AddNewTorrentDialog::show(param, m_window);
else
#endif
@@ -266,7 +402,7 @@ int Application::exec(const QStringList &params)
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()), Qt::QueuedConnection);
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::initInstance();
@@ -443,11 +579,9 @@ void Application::cleanup()
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
// cleanup() can be called multiple times during shutdown. We only need it once.
static bool alreadyDone = false;
if (alreadyDone)
static QAtomicInt alreadyDone;
if (!alreadyDone.testAndSetAcquire(0, 1))
return;
alreadyDone = true;
#endif // Q_OS_WIN
// Hide the window and not leave it on screen as
@@ -486,9 +620,11 @@ void Application::cleanup()
#endif
Net::DownloadManager::freeInstance();
Preferences::freeInstance();
SettingsStorage::freeInstance();
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
#ifndef DISABLE_GUI
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
@@ -498,9 +634,10 @@ void Application::cleanup()
shutdownBRDestroy((HWND)m_window->effectiveWinId());
#endif // Q_OS_WIN
delete m_window;
if (m_shutdownAct != ShutdownAction::None) {
#endif // DISABLE_GUI
if (m_shutdownAct != ShutdownDialogAction::Exit) {
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
Utils::Misc::shutdownComputer(m_shutdownAct);
}
#endif
}

View File

@@ -76,6 +76,26 @@ public:
int exec(const QStringList &params);
bool sendParams(const QStringList &params);
#ifndef DISABLE_GUI
QPointer<MainWindow> mainWindow();
#endif
// FileLogger properties
bool isFileLoggerEnabled() const;
void setFileLoggerEnabled(bool value);
QString fileLoggerPath() const;
void setFileLoggerPath(const QString &path);
bool isFileLoggerBackup() const;
void setFileLoggerBackup(bool value);
bool isFileLoggerDeleteOld() const;
void setFileLoggerDeleteOld(bool value);
int fileLoggerMaxSize() const;
void setFileLoggerMaxSize(const int value);
int fileLoggerAge() const;
void setFileLoggerAge(const int value);
int fileLoggerAgeType() const;
void setFileLoggerAgeType(const int value);
protected:
#ifndef DISABLE_GUI
#ifdef Q_OS_MAC
@@ -85,7 +105,6 @@ protected:
#endif
private slots:
void configure();
void processMessage(const QString &message);
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
void allTorrentsFinished();
@@ -96,10 +115,10 @@ private slots:
private:
bool m_running;
ShutdownDialogAction m_shutdownAct;
#ifndef DISABLE_GUI
QPointer<MainWindow> m_window;
ShutdownAction m_shutdownAct;
#endif
#ifndef DISABLE_WEBUI
@@ -115,6 +134,7 @@ private:
void initializeTranslation();
void processParams(const QStringList &params);
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
};

View File

@@ -26,37 +26,31 @@
* exception statement from your version.
*/
#include <QDateTime>
#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)
FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize, const bool deleteOld, const int age, const FileLogAgeType ageType)
: m_backup(backup)
, m_maxSize(maxSize)
, m_logFile(nullptr)
{
m_flusher.setInterval(0);
m_flusher.setSingleShot(true);
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
configure();
changePath(path);
if (deleteOld)
this->deleteOld(age, ageType);
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 &)));
}
@@ -67,10 +61,9 @@ FileLogger::~FileLogger()
delete m_logFile;
}
void FileLogger::configure()
void FileLogger::changePath(const QString& newPath)
{
const Preferences* const pref = Preferences::instance();
QString tmpPath = Utils::Fs::fromNativePath(pref->fileLogPath());
QString tmpPath = Utils::Fs::fromNativePath(newPath);
QDir dir(tmpPath);
dir.mkpath(tmpPath);
tmpPath = dir.absoluteFilePath("qbittorrent.log");
@@ -85,30 +78,39 @@ void FileLogger::configure()
m_logFile = new QFile(m_path);
openLogFile();
}
}
m_backup = pref->fileLogBackup();
m_size = pref->fileLogMaxSize();
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
{
QDateTime date = QDateTime::currentDateTime();
QDir dir(m_path);
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());
}
switch (ageType) {
case DAYS:
date = date.addDays(age);
break;
case MONTHS:
date = date.addMonths(age);
break;
default:
date = date.addYears(age);
}
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::setBackup(bool value)
{
m_backup = value;
}
void FileLogger::setMaxSize(int value)
{
m_maxSize = value;
}
void FileLogger::addLogMessage(const Log::Msg &msg)
@@ -133,7 +135,7 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
if (m_backup && (m_logFile->size() >= (m_size * 1024 * 1024))) {
if (m_backup && (m_logFile->size() >= (m_maxSize * 1024 * 1024))) {
closeLogFile();
int counter = 0;
QString backupLogFilename = m_path + ".bak";

View File

@@ -45,11 +45,22 @@ class FileLogger : public QObject
Q_DISABLE_COPY(FileLogger)
public:
FileLogger();
enum FileLogAgeType
{
DAYS,
MONTHS,
YEARS
};
FileLogger(const QString &path, const bool backup, const int maxSize, const bool deleteOld, const int age, const FileLogAgeType ageType);
~FileLogger();
void changePath(const QString &newPath);
void deleteOld(const int age, const FileLogAgeType ageType);
void setBackup(bool value);
void setMaxSize(int value);
private slots:
void configure();
void addLogMessage(const Log::Msg &msg);
void flushLog();
@@ -57,9 +68,9 @@ private:
void openLogFile();
void closeLogFile();
bool m_backup;
int m_size;
QString m_path;
bool m_backup;
int m_maxSize;
QFile *m_logFile;
QTimer m_flusher;
};

View File

@@ -1,3 +1,5 @@
project(qtsingleapplication)
set(QBT_QTSINGLEAPPLICATION_HEADERS
qtlocalpeer.h
)
@@ -15,6 +17,7 @@ else (GUI)
endif (GUI)
add_library(qtsingleapplication ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
if (QT4_FOUND)
target_link_libraries(qtsingleapplication Qt4::QtNetwork)
@@ -30,3 +33,4 @@ if (GUI)
endif(QT4_FOUND)
endif (GUI)
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)

View File

@@ -25,6 +25,7 @@
#include <dbghelp.h>
#include <stdio.h>
#include <QDir>
#include <QTextStream>
#ifdef __MINGW32__
#include <cxxabi.h>
@@ -41,6 +42,9 @@ namespace straceWin
#ifdef __MINGW32__
void demangle(QString& str);
#endif
QString getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr);
bool makeRelativePath(const QString& dir, QString& file);
}
#ifdef __MINGW32__
@@ -108,6 +112,65 @@ BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVO
}
/**
* Cuts off leading 'dir' path from 'file' path, otherwise leaves it unchanged
* returns true if 'dir' is an ancestor of 'file', otherwise - false
*/
bool straceWin::makeRelativePath(const QString& dir, QString& file)
{
QString d = QDir::toNativeSeparators(QDir(dir).absolutePath());
QString f = QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath());
// append separator at the end of dir
QChar separator = QDir::separator();
if (!d.isEmpty() && (d[d.length() - 1] != separator))
d += separator;
if (f.startsWith(d, Qt::CaseInsensitive)) {
f.remove(0, d.length());
file.swap(f);
return true;
}
return false;
}
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
{
IMAGEHLP_LINE64 line = {0};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD dwDisplacement = 0;
if (SymGetLineFromAddr64(hProcess, addr, &dwDisplacement, &line)) {
QString path(line.FileName);
#if defined STACKTRACE_WIN_PROJECT_PATH || defined STACKTRACE_WIN_MAKEFILE_PATH
#define STACKTRACE_WIN_QUOTE(x) #x
#define STACKTRACE_WIN_STRING(x) STACKTRACE_WIN_QUOTE(x)
//prune leading project directory path or build target directory path
bool success = false;
#ifdef STACKTRACE_WIN_PROJECT_PATH
QString projectPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_PROJECT_PATH));
success = makeRelativePath(projectPath, path);
#endif
#ifdef STACKTRACE_WIN_MAKEFILE_PATH
if (!success) {
QString targetPath(STACKTRACE_WIN_STRING(STACKTRACE_WIN_MAKEFILE_PATH));
makeRelativePath(targetPath, path);
}
#endif
#endif
return QString("%1 : %2").arg(path).arg(line.LineNumber);
}
return QString();
}
#if defined( _M_IX86 ) && defined(Q_CC_MSVC)
// Disable global optimization and ignore /GS waning caused by
@@ -221,11 +284,16 @@ const QString straceWin::getBacktrace()
fileName = fileName.mid(slashPos + 1);
}
QString funcName;
QString sourceFile;
if(SymFromAddr(hProcess, ihsf.InstructionOffset, &dwDisplacement, pSymbol)) {
funcName = QString(pSymbol->Name);
#ifdef __MINGW32__
demangle(funcName);
#endif
// now ihsf.InstructionOffset points to the instruction that follows CALL instuction
// decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
}
else {
funcName = QString("0x%1").arg(ihsf.InstructionOffset, 8, 16, QLatin1Char('0'));
@@ -248,6 +316,9 @@ const QString straceWin::getBacktrace()
.arg(funcName)
#ifndef __MINGW32__
.arg(params.join(", "));
if (!sourceFile.isEmpty())
debugLine += QString("[ %1 ]").arg(sourceFile);
#else
;
#endif
@@ -262,6 +333,8 @@ const QString straceWin::getBacktrace()
//logStream << "\n\nList of linked Modules:\n";
//EnumModulesContext modulesContext(hProcess, logStream);
//SymEnumerateModules64(hProcess, EnumModulesCB, (PVOID)&modulesContext);
SymCleanup(hProcess);
logStream << "```";
return log;
}

View File

@@ -129,8 +129,8 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
bool upgrade(bool ask = true)
{
// Move RSS cookies to common storage
Preferences::instance()->moveRSSCookies();
// Upgrade preferences
Preferences::instance()->upgrade();
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
QDir backupFolderDir(backupFolderPath);

View File

@@ -1,5 +1,4 @@
find_package(ZLIB REQUIRED)
include_directories(SYSTEM ${ZLIB_INCLUDE_DIRS})
set(QBT_BASE_HEADERS
bittorrent/cachestatus.h
@@ -47,12 +46,14 @@ utils/misc.h
utils/string.h
filesystemwatcher.h
iconprovider.h
indexrange.h
logger.h
preferences.h
qinisettings.h
scanfoldersmodel.h
searchengine.h
settingsstorage.h
torrentfileguard.h
torrentfilter.h
tristatebool.h
types.h
@@ -108,12 +109,13 @@ preferences.cpp
scanfoldersmodel.cpp
searchengine.cpp
settingsstorage.cpp
torrentfileguard.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_libraries(qbt_base ZLIB::ZLIB LibtorrentRasterbar::LibTorrent)
target_link_qt_components(qbt_base Core Network Xml)
if (QT4_FOUND)
if (GUI)
@@ -124,6 +126,11 @@ else (QT4_FOUND)
target_link_libraries(qbt_base Qt5::Gui Qt5::Widgets)
endif (GUI)
endif (QT4_FOUND)
if (DBUS)
target_link_qt_components(qbt_base DBus)
endif ()
if (APPLE)
find_library(IOKit_LIBRARY IOKit)
find_library(Carbon_LIBRARY Carbon)

View File

@@ -4,7 +4,9 @@ HEADERS += \
$$PWD/filesystemwatcher.h \
$$PWD/qinisettings.h \
$$PWD/logger.h \
$$PWD/settingsstorage.h \
$$PWD/preferences.h \
$$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/connection.h \
@@ -50,6 +52,7 @@ HEADERS += \
$$PWD/utils/misc.h \
$$PWD/utils/string.h \
$$PWD/unicodestrings.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h
@@ -58,6 +61,7 @@ SOURCES += \
$$PWD/tristatebool.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/logger.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/preferences.cpp \
$$PWD/iconprovider.cpp \
$$PWD/http/connection.cpp \
@@ -101,6 +105,7 @@ SOURCES += \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/string.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp

View File

@@ -68,7 +68,6 @@ bool InfoHash::isValid() const
return m_valid;
}
InfoHash::operator libtorrent::sha1_hash() const
{
return m_nativeHash;

View File

@@ -401,7 +401,30 @@ void FilterParserThread::processFilterList(libt::session *s, const QStringList &
QString FilterParserThread::cleanupIPAddress(QString _ip)
{
QHostAddress ip(_ip.trimmed());
_ip = _ip.trimmed();
// Emule .DAT files contain leading zeroes in IPv4 addresses
// eg 001.009.106.186
// We need to remove them because both QHostAddress and Boost.Asio fail to parse them.
QStringList octets = _ip.split('.', QString::SkipEmptyParts);
if (octets.size() == 4) {
QString octet; // it is faster to not recreate this object in the loop
for (int i = 0; i < 4; i++) {
octet = octets[i];
if ((octet[0] == QChar('0')) && (octet.count() > 1)) {
if ((octet[1] == QChar('0')) && (octet.count() > 2))
octet.remove(0, 2);
else
octet.remove(0, 1);
octets[i] = octet;
}
}
_ip = octets.join(".");
}
QHostAddress ip(_ip);
if (ip.isNull()) return QString();
return ip.toString();

View File

@@ -27,17 +27,21 @@
* exception statement from your version.
*/
#include <QList>
#include "speedmonitor.h"
SpeedMonitor::SpeedMonitor()
: m_speedSamples(MAX_SAMPLES)
{
}
void SpeedMonitor::addSample(const SpeedSample &sample)
{
if (m_speedSamples.size() >= MAX_SAMPLES) {
m_sum -= m_speedSamples.front();
}
m_speedSamples.push_back(sample);
m_sum += sample;
if (m_speedSamples.size() > MAX_SAMPLES) {
m_sum -= m_speedSamples.front();
m_speedSamples.pop_front();
}
}
SpeedSampleAvg SpeedMonitor::average() const

View File

@@ -30,7 +30,11 @@
#ifndef SPEEDMONITOR_H
#define SPEEDMONITOR_H
template<typename T> class QList;
#ifndef Q_MOC_RUN
#include <boost/circular_buffer.hpp>
#endif
#include <QtGlobal>
template<typename T>
struct Sample
@@ -71,13 +75,15 @@ typedef Sample<qreal> SpeedSampleAvg;
class SpeedMonitor
{
public:
SpeedMonitor();
void addSample(const SpeedSample &sample);
SpeedSampleAvg average() const;
void reset();
private:
static const int MAX_SAMPLES = 30;
QList<SpeedSample> m_speedSamples;
boost::circular_buffer<SpeedSample> m_speedSamples;
SpeedSample m_sum;
};

View File

@@ -3,7 +3,6 @@
#include <libtorrent/session.hpp>
#include "base/qinisettings.h"
#include "base/preferences.h"
#include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/session.h"
#include "statistics.h"
@@ -76,40 +75,9 @@ void Statistics::save() const
void Statistics::load()
{
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
// This code reads the data from there, writes it to the new file, and removes the keys
// from the old file. This code should be removed after some time has passed.
// e.g. When we reach v3.3.0
// Don't forget to remove:
// 1. Preferences::getStats()
// 2. Preferences::removeStats()
// 3. #include "base/preferences.h"
Preferences* const pref = Preferences::instance();
QIniSettings s("qBittorrent", "qBittorrent-data");
QVariantHash v = pref->getStats();
// Let's test if the qbittorrent.ini holds the key
if (!v.isEmpty()) {
m_dirty = true;
// If the user has used qbt > 3.1.5 and then reinstalled/used
// qbt < 3.1.6, there will be stats in qbittorrent-data.ini too
// so we need to merge those 2.
if (s.contains("Stats/AllStats")) {
QVariantHash tmp = s.value("Stats/AllStats").toHash();
v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong();
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
}
}
else {
v = s.value("Stats/AllStats").toHash();
}
QVariantHash v = s.value("Stats/AllStats").toHash();
m_alltimeDL = v["AlltimeDL"].toULongLong();
m_alltimeUL = v["AlltimeUL"].toULongLong();
if (m_dirty) {
save();
pref->removeStats();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@
#include <QFile>
#include <QHash>
#include <QMap>
#include <QPointer>
#include <QVector>
#include <QMutex>
@@ -110,8 +111,19 @@ class FilterParserThread;
class BandwidthScheduler;
class Statistics;
class ResumeDataSavingManager;
class SettingsStorage;
typedef QPair<QString, QString> QStringPair;
enum MaxRatioAction
{
Pause,
Remove
};
enum TorrentExportFolder
{
Regular,
Finished
};
namespace BitTorrent
{
@@ -127,7 +139,7 @@ namespace BitTorrent
struct AddTorrentParams
{
QString name;
QString label;
QString category;
QString savePath;
bool disableTempPath = false; // e.g. for imported torrents
bool sequential = false;
@@ -165,11 +177,50 @@ namespace BitTorrent
bool isPexEnabled() const;
bool isQueueingEnabled() const;
qreal globalMaxRatio() const;
bool isTempPathEnabled() const;
bool isAppendExtensionEnabled() const;
bool useAppendLabelToSavePath() const;
QString defaultSavePath() const;
void setDefaultSavePath(QString path);
QString tempPath() const;
void setTempPath(QString path);
bool isTempPathEnabled() const;
void setTempPathEnabled(bool enabled);
QString torrentTempPath(const InfoHash &hash) const;
static bool isValidCategoryName(const QString &name);
// returns category itself and all top level categories
static QStringList expandCategory(const QString &category);
QStringList categories() const;
QString categorySavePath(const QString &categoryName) const;
bool addCategory(const QString &name, const QString &savePath = "");
bool editCategory(const QString &name, const QString &savePath);
bool removeCategory(const QString &name);
bool isSubcategoriesEnabled() const;
void setSubcategoriesEnabled(bool value);
// Torrent Management Mode subsystem (TMM)
//
// Each torrent can be either in Manual mode or in Automatic mode
// In Manual Mode various torrent properties are set explicitly(eg save path)
// In Automatic Mode various torrent properties are set implicitly(eg save path)
// based on the associated category.
// In Automatic Mode torrent save path can be changed in following cases:
// 1. Default save path changed
// 2. Torrent category save path changed
// 3. Torrent category changed
// (unless otherwise is specified)
bool isAutoTMMDisabledByDefault() const;
void setAutoTMMDisabledByDefault(bool value);
bool isDisableAutoTMMWhenCategoryChanged() const;
void setDisableAutoTMMWhenCategoryChanged(bool value);
bool isDisableAutoTMMWhenDefaultSavePathChanged() const;
void setDisableAutoTMMWhenDefaultSavePathChanged(bool value);
bool isDisableAutoTMMWhenCategorySavePathChanged() const;
void setDisableAutoTMMWhenCategorySavePathChanged(bool value);
bool isAddTorrentPaused() const;
void setAddTorrentPaused(bool value);
TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const;
@@ -184,6 +235,9 @@ namespace BitTorrent
int uploadRateLimit() const;
bool isListening() const;
MaxRatioAction maxRatioAction() const;
void setMaxRatioAction(MaxRatioAction act);
void changeSpeedLimitMode(bool alternative);
void setDownloadRateLimit(int rate);
void setUploadRateLimit(int rate);
@@ -208,7 +262,8 @@ namespace BitTorrent
// TorrentHandle interface
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
void handleTorrentSavingModeChanged(TorrentHandle *const torrent);
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
void handleTorrentPaused(TorrentHandle *const torrent);
void handleTorrentResumed(TorrentHandle *const torrent);
@@ -230,13 +285,15 @@ namespace BitTorrent
void torrentsUpdated();
void addTorrentFailed(const QString &error);
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
void torrentNew(BitTorrent::TorrentHandle *const torrent);
void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent);
void torrentPaused(BitTorrent::TorrentHandle *const torrent);
void torrentResumed(BitTorrent::TorrentHandle *const torrent);
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
void torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel);
void torrentCategoryChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldCategory);
void torrentSavingModeChanged(BitTorrent::TorrentHandle *const torrent);
void allTorrentsFinished();
void metadataLoaded(const BitTorrent::TorrentInfo &info);
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
@@ -254,6 +311,9 @@ namespace BitTorrent
void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless);
void downloadFromUrlFailed(const QString &url, const QString &reason);
void downloadFromUrlFinished(const QString &url);
void categoryAdded(const QString &categoryName);
void categoryRemoved(const QString &categoryName);
void subcategoriesSupportChanged();
private slots:
void configure();
@@ -287,8 +347,6 @@ namespace BitTorrent
void adjustLimits(libtorrent::session_settings &sessionSettings);
const QStringList getListeningIPs();
void setListeningPort();
void setDefaultSavePath(const QString &path);
void setDefaultTempPath(const QString &path = QString());
void preAllocateAllFiles(bool b);
void setMaxConnectionsPerTorrent(int max);
void setMaxUploadsPerTorrent(int max);
@@ -296,13 +354,13 @@ namespace BitTorrent
void enableDHT(bool enable);
void changeSpeedLimitMode_impl(bool alternative);
void setAppendLabelToSavePath(bool append);
void setAppendExtension(bool append);
void startUpTorrents();
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
const TorrentInfo &torrentInfo = TorrentInfo(),
TorrentInfo torrentInfo = TorrentInfo(),
const QByteArray &fastresumeData = QByteArray());
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateRatioTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
@@ -330,8 +388,12 @@ namespace BitTorrent
void saveResumeData();
#if LIBTORRENT_VERSION_NUM < 10100
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
#endif
void getPendingAlerts(std::vector<libtorrent::alert *> &out, ulong time = 0);
SettingsStorage *m_settings;
// BitTorrent
libtorrent::session *m_nativeSession;
@@ -346,10 +408,9 @@ namespace BitTorrent
qreal m_globalMaxRatio;
int m_numResumeData;
int m_extraLimit;
bool m_appendLabelToSavePath;
bool m_appendExtension;
uint m_refreshInterval;
MaxRatioAction m_highRatioAction;
MaxRatioAction m_maxRatioAction;
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
QString m_defaultSavePath;
QString m_tempPath;
@@ -376,10 +437,13 @@ namespace BitTorrent
QHash<InfoHash, AddTorrentData> m_addingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents;
TorrentStatusReport m_torrentStatusReport;
QStringMap m_categories;
#if LIBTORRENT_VERSION_NUM < 10100
QMutex m_alertsMutex;
QWaitCondition m_alertsWaitCondition;
QVector<libtorrent::alert *> m_alerts;
std::vector<libtorrent::alert *> m_alerts;
#endif
QNetworkConfigurationManager m_networkManager;

View File

@@ -60,7 +60,7 @@
#include "trackerentry.h"
#include "torrenthandle.h"
static const char QB_EXT[] = ".!qB";
const QString QB_EXT {".!qB"};
namespace libt = libtorrent;
using namespace BitTorrent;
@@ -80,7 +80,7 @@ AddTorrentData::AddTorrentData()
AddTorrentData::AddTorrentData(const AddTorrentParams &params)
: resumed(false)
, name(params.name)
, label(params.label)
, category(params.category)
, savePath(params.savePath)
, disableTempPath(params.disableTempPath)
, sequential(params.sequential)
@@ -199,9 +199,10 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_nativeHandle(nativeHandle)
, m_state(TorrentState::Unknown)
, m_renameCount(0)
, m_useAutoTMM(data.savePath.isEmpty())
, m_name(data.name)
, m_savePath(Utils::Fs::toNativePath(data.savePath))
, m_label(data.label)
, m_category(data.category)
, m_hasSeedStatus(data.hasSeedStatus)
, m_ratioLimit(data.ratioLimit)
, m_tempPathDisabled(data.disableTempPath)
@@ -209,19 +210,14 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_pauseAfterRecheck(false)
, m_needSaveResumeData(false)
{
Q_ASSERT(!m_savePath.isEmpty());
if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
updateStatus();
m_hash = InfoHash(m_nativeStatus.info_hash);
adjustActualSavePath();
if (!data.resumed) {
if (!data.resumed)
setSequentialDownload(data.sequential);
if (hasMetadata()) {
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
}
}
}
TorrentHandle::~TorrentHandle() {}
@@ -330,6 +326,22 @@ QString TorrentHandle::contentPath(bool actual) const
return rootPath(actual);
}
bool TorrentHandle::isAutoTMMEnabled() const
{
return m_useAutoTMM;
}
void TorrentHandle::setAutoTMMEnabled(bool enabled)
{
if (m_useAutoTMM == enabled) return;
m_useAutoTMM = enabled;
m_session->handleTorrentSavingModeChanged(this);
if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
QString TorrentHandle::nativeActualSavePath() const
{
return Utils::String::fromStdString(m_nativeStatus.save_path);
@@ -507,9 +519,22 @@ qreal TorrentHandle::progress() const
return progress;
}
QString TorrentHandle::label() const
QString TorrentHandle::category() const
{
return m_label;
return m_category;
}
bool TorrentHandle::belongsToCategory(const QString &category) const
{
if (m_category.isEmpty()) return category.isEmpty();
if (!Session::isValidCategoryName(category)) return false;
if (m_category == category) return true;
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/"))
return true;
return false;
}
QDateTime TorrentHandle::addedTime() const
@@ -572,25 +597,6 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
return res;
}
QPair<int, int> TorrentHandle::fileExtremityPieces(int index) const
{
if (!hasMetadata()) return qMakePair(-1, -1);
const int numPieces = piecesCount();
const qlonglong pieceSize = pieceLength();
// Determine the first and last piece of the file
int firstPiece = floor((m_torrentInfo.fileOffset(index) + 1) / (float) pieceSize);
Q_ASSERT((firstPiece >= 0) && (firstPiece < numPieces));
int numPiecesInFile = ceil(fileSize(index) / (float) pieceSize);
int lastPiece = firstPiece + numPiecesInFile - 1;
Q_ASSERT((lastPiece >= 0) && (lastPiece < numPieces));
Q_UNUSED(numPieces)
return qMakePair(firstPiece, lastPiece);
}
QVector<int> TorrentHandle::filePriorities() const
{
std::vector<int> fp;
@@ -708,13 +714,13 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
std::vector<int> fp;
SAFE_GET(fp, file_priorities);
QPair<int, int> extremities;
TorrentInfo::PieceRange extremities;
bool found = false;
int count = static_cast<int>(fp.size());
for (int i = 0; i < count; ++i) {
const QString ext = Utils::Fs::fileExtension(filePath(i));
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
extremities = fileExtremityPieces(i);
extremities = info().filePieces(i);
found = true;
break;
}
@@ -724,8 +730,8 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
int first = 0;
int last = 0;
SAFE_GET(first, piece_priority, extremities.first);
SAFE_GET(last, piece_priority, extremities.second);
SAFE_GET(first, piece_priority, extremities.first());
SAFE_GET(last, piece_priority, extremities.last());
return ((first == 7) && (last == 7));
}
@@ -1110,17 +1116,47 @@ void TorrentHandle::setName(const QString &name)
}
}
void TorrentHandle::setLabel(const QString &label)
bool TorrentHandle::setCategory(const QString &category)
{
if (m_label != label) {
QString oldLabel = m_label;
m_label = label;
if (m_category != category) {
if (!category.isEmpty()) {
if (!Session::isValidCategoryName(category)) return false;
if (!m_session->categories().contains(category))
if (!m_session->addCategory(category))
return false;
}
QString oldCategory = m_category;
m_category = category;
m_needSaveResumeData = true;
m_session->handleTorrentLabelChanged(this, oldLabel);
m_session->handleTorrentCategoryChanged(this, oldCategory);
if (m_useAutoTMM) {
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
move_impl(m_session->categorySavePath(m_category));
else
setAutoTMMEnabled(false);
}
}
return true;
}
void TorrentHandle::move(QString path)
{
m_useAutoTMM = false;
m_session->handleTorrentSavingModeChanged(this);
path = Utils::Fs::fromNativePath(path.trimmed());
if (path.isEmpty())
path = m_session->defaultSavePath();
if (!path.endsWith('/'))
path += '/';
move_impl(path);
}
void TorrentHandle::move_impl(QString path)
{
path = Utils::Fs::toNativePath(path);
if (path == savePath()) return;
@@ -1190,13 +1226,13 @@ void TorrentHandle::setFirstLastPiecePriority(bool b)
// Determine the priority to set
int prio = b ? 7 : fp[index];
QPair<int, int> extremities = fileExtremityPieces(index);
TorrentInfo::PieceRange extremities = info().filePieces(index);
// 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;
pp[extremities.first() + i] = prio;
pp[extremities.last() - i] = prio;
}
}
}
@@ -1337,7 +1373,7 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
// Attempt to remove old folder if empty
QDir oldSaveDir(Utils::Fs::fromNativePath(m_oldPath));
if ((oldSaveDir != QDir(m_session->defaultSavePath())) && (oldSaveDir != QDir(m_session->tempPath()))) {
if (oldSaveDir != QDir(m_session->defaultSavePath())) {
qDebug("Attempting to remove %s", qPrintable(m_oldPath));
QDir().rmpath(m_oldPath);
}
@@ -1414,6 +1450,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
m_hasSeedStatus = true;
adjustActualSavePath();
manageIncompleteFiles();
if (m_pauseAfterRecheck) {
m_pauseAfterRecheck = false;
@@ -1435,13 +1472,19 @@ void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_aler
m_hasSeedStatus = true;
adjustActualSavePath();
if (Preferences::instance()->recheckTorrentsOnCompletion())
forceRecheck();
manageIncompleteFiles();
if (isMoveInProgress() || m_renameCount > 0)
const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
if (isMoveInProgress() || m_renameCount > 0) {
if (recheckTorrentsOnCompletion)
m_moveFinishedTriggers.append(boost::bind(&TorrentHandle::forceRecheck, this));
m_moveFinishedTriggers.append(boost::bind(&Session::handleTorrentFinished, m_session, this));
else
}
else {
if (recheckTorrentsOnCompletion)
forceRecheck();
m_session->handleTorrentFinished(this);
}
}
void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p)
@@ -1469,9 +1512,9 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced();
}
resumeData["qBt-savePath"] = Utils::String::toStdString(m_savePath);
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Utils::String::toStdString(m_savePath);
resumeData["qBt-ratioLimit"] = Utils::String::toStdString(QString::number(m_ratioLimit));
resumeData["qBt-label"] = Utils::String::toStdString(m_label);
resumeData["qBt-category"] = Utils::String::toStdString(m_category);
resumeData["qBt-name"] = Utils::String::toStdString(m_name);
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
@@ -1532,12 +1575,6 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
updateStatus();
if (filesCount() == 1) {
// Single-file torrent
// Renaming a file corresponds to changing the save path
m_session->handleTorrentSavePathChanged(this);
}
--m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
m_moveFinishedTriggers.takeFirst()();
@@ -1561,7 +1598,7 @@ void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p
QString name = filePath(p->index);
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QString(QB_EXT).size());
name.chop(QB_EXT.size());
qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
renameFile(p->index, name);
}
@@ -1582,7 +1619,7 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p
qDebug("Metadata received for torrent %s.", qPrintable(name()));
updateStatus();
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
manageIncompleteFiles();
m_session->handleTorrentMetadataReceived(this);
if (isPaused()) {
@@ -1598,14 +1635,17 @@ void TorrentHandle::handleTempPathChanged()
adjustActualSavePath();
}
void TorrentHandle::handleCategorySavePathChanged()
{
if (m_useAutoTMM)
move_impl(m_session->categorySavePath(m_category));
}
void TorrentHandle::handleAppendExtensionToggled()
{
if (!hasMetadata()) return;
if (m_session->isAppendExtensionEnabled())
appendExtensionsToIncompleteFiles();
else
removeExtensionsFromIncompleteFiles();
manageIncompleteFiles();
}
void TorrentHandle::handleAlert(libtorrent::alert *a)
@@ -1662,30 +1702,26 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
}
}
void TorrentHandle::appendExtensionsToIncompleteFiles()
void TorrentHandle::manageIncompleteFiles()
{
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
QVector<qreal> fp = filesProgress();
for (int i = 0; i < filesCount(); ++i) {
if ((fileSize(i) > 0) && (fp[i] < 1)) {
const QString name = filePath(i);
QString name = filePath(i);
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1)) {
if (!name.endsWith(QB_EXT)) {
const QString newName = name + QB_EXT;
qDebug("Renaming %s to %s", qPrintable(name), qPrintable(newName));
qDebug() << "Renaming" << name << "to" << newName;
renameFile(i, newName);
}
}
}
}
void TorrentHandle::removeExtensionsFromIncompleteFiles()
{
for (int i = 0; i < filesCount(); ++i) {
QString name = filePath(i);
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QString(QB_EXT).size());
qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name));
renameFile(i, name);
else {
if (name.endsWith(QB_EXT)) {
const QString oldName = name;
name.chop(QB_EXT.size());
qDebug() << "Renaming" << oldName << "to" << name;
renameFile(i, name);
}
}
}
}
@@ -1708,8 +1744,8 @@ void TorrentHandle::adjustActualSavePath_impl()
}
else {
// Moving all downloading torrents to temporary save path
path = m_session->tempPath();
qDebug("Moving torrent to its temp save path: %s", qPrintable(path));
path = m_session->torrentTempPath(hash());
qDebug() << "Moving torrent to its temp save path:" << path;
}
moveStorage(Utils::Fs::toNativePath(path));

View File

@@ -54,6 +54,8 @@ class QBitArray;
class QStringList;
template<typename T, typename U> struct QPair;
extern const QString QB_EXT;
namespace libtorrent
{
class alert;
@@ -90,7 +92,7 @@ namespace BitTorrent
bool resumed;
// for both new and resumed torrents
QString name;
QString label;
QString category;
QString savePath;
bool disableTempPath;
bool sequential;
@@ -227,11 +229,16 @@ namespace BitTorrent
QString rootPath(bool actual = false) const;
QString contentPath(bool actual = false) const;
bool isAutoTMMEnabled() const;
void setAutoTMMEnabled(bool enabled);
QString category() const;
bool belongsToCategory(const QString &category) const;
bool setCategory(const QString &category);
int filesCount() const;
int piecesCount() const;
int piecesHave() const;
qreal progress() const;
QString label() const;
QDateTime addedTime() const;
qreal ratioLimit() const;
@@ -240,7 +247,6 @@ namespace BitTorrent
qlonglong fileSize(int index) const;
QStringList absoluteFilePaths() const;
QStringList absoluteFilePathsUnwanted() const;
QPair<int, int> fileExtremityPieces(int index) const;
QVector<int> filePriorities() const;
TorrentInfo info() const;
@@ -307,7 +313,6 @@ namespace BitTorrent
qlonglong nextAnnounce() const;
void setName(const QString &name);
void setLabel(const QString &label);
void setSequentialDownload(bool b);
void toggleSequentialDownload();
void setFirstLastPiecePriority(bool b);
@@ -344,6 +349,7 @@ namespace BitTorrent
void handleAlert(libtorrent::alert *a);
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
@@ -379,9 +385,9 @@ namespace BitTorrent
void adjustActualSavePath();
void adjustActualSavePath_impl();
void move_impl(QString path);
void moveStorage(const QString &newPath);
void appendExtensionsToIncompleteFiles();
void removeExtensionsFromIncompleteFiles();
void manageIncompleteFiles();
bool addTracker(const TrackerEntry &tracker);
bool addUrlSeed(const QUrl &urlSeed);
bool removeUrlSeed(const QUrl &urlSeed);
@@ -405,10 +411,12 @@ namespace BitTorrent
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount;
bool m_useAutoTMM;
// Persistent data
QString m_name;
QString m_savePath;
QString m_label;
QString m_category;
bool m_hasSeedStatus;
qreal m_ratioLimit;
bool m_tempPathDisabled;

View File

@@ -26,6 +26,7 @@
* exception statement from your version.
*/
#include <QDebug>
#include <QString>
#include <QList>
#include <QUrl>
@@ -44,8 +45,8 @@ namespace libt = libtorrent;
using namespace BitTorrent;
TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo)
: m_nativeInfo(nativeInfo)
{
m_nativeInfo = boost::const_pointer_cast<libt::torrent_info>(nativeInfo);
}
TorrentInfo::TorrentInfo(const TorrentInfo &other)
@@ -138,6 +139,12 @@ int TorrentInfo::pieceLength() const
return m_nativeInfo->piece_length();
}
int TorrentInfo::pieceLength(int index) const
{
if (!isValid()) return -1;
return m_nativeInfo->piece_size(index);
}
int TorrentInfo::piecesCount() const
{
if (!isValid()) return -1;
@@ -178,7 +185,7 @@ qlonglong TorrentInfo::fileSize(int index) const
qlonglong TorrentInfo::fileOffset(int index) const
{
if (!isValid()) return -1;
if (!isValid()) return -1;
return m_nativeInfo->file_at(index).offset;
}
@@ -213,25 +220,80 @@ QByteArray TorrentInfo::metadata() const
QStringList TorrentInfo::filesForPiece(int pieceIndex) const
{
if (pieceIndex < 0)
return QStringList();
// no checks here because fileIndicesForPiece() will return an empty list
QVector<int> fileIndices = fileIndicesForPiece(pieceIndex);
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));
}
res.reserve(fileIndices.size());
std::transform(fileIndices.begin(), fileIndices.end(), std::back_inserter(res),
[this](int i) { return filePath(i); });
return res;
}
QVector<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
{
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
return QVector<int>();
std::vector<libt::file_slice> files(
nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
QVector<int> res;
res.reserve(files.size());
std::transform(files.begin(), files.end(), std::back_inserter(res),
[](const libt::file_slice &s) { return s.file_index; });
return res;
}
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
{
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
return {};
int index = fileIndex(file);
if (index == -1) {
qDebug() << "Filename" << file << "was not found in torrent" << name();
return {};
}
return filePieces(index);
}
TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
{
if (!isValid())
return {};
if ((fileIndex < 0) || (fileIndex >= filesCount())) {
qDebug() << "File index (" << fileIndex << ") is out of range for torrent" << name();
return {};
}
const libt::file_storage &files = nativeInfo()->files();
const auto fileSize = files.file_size(fileIndex);
const auto firstOffset = files.file_offset(fileIndex);
return makeInterval(static_cast<int>(firstOffset / pieceLength()),
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
}
void TorrentInfo::renameFile(uint index, const QString &newPath)
{
if (!isValid()) return;
nativeInfo()->rename_file(index, Utils::String::toStdString(newPath));
}
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
{
// the check whether the object valid is not needed here
// because filesCount() returns -1 in that case and the loop exits immediately
for (int i = 0; i < filesCount(); ++i)
if (fileName == filePath(i))
return i;
return -1;
}
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
{
return *reinterpret_cast<const NativePtr *>(&m_nativeInfo);
return m_nativeInfo;
}

View File

@@ -34,12 +34,15 @@
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
#include "base/indexrange.h"
class QString;
class QUrl;
class QDateTime;
class QStringList;
class QByteArray;
template<typename T> class QList;
template<typename T> class QVector;
namespace BitTorrent
{
@@ -75,6 +78,7 @@ namespace BitTorrent
qlonglong totalSize() const;
int filesCount() const;
int pieceLength() const;
int pieceLength(int index) const;
int piecesCount() const;
QString filePath(int index) const;
QStringList filePaths() const;
@@ -86,13 +90,22 @@ namespace BitTorrent
QList<QUrl> urlSeeds() const;
QByteArray metadata() const;
QStringList filesForPiece(int pieceIndex) const;
QVector<int> fileIndicesForPiece(int pieceIndex) const;
using PieceRange = IndexRange<int>;
// returns pair of the first and the last pieces into which
// the given file extends (maybe partially).
PieceRange filePieces(const QString &file) const;
PieceRange filePieces(int fileIndex) const;
void renameFile(uint index, const QString &newPath);
NativePtr nativeInfo() const;
private:
NativeConstPtr m_nativeInfo;
// returns file index or -1 if fileName is not found
int fileIndex(const QString &fileName) const;
NativePtr m_nativeInfo;
};
}

View File

@@ -34,7 +34,7 @@
#include <QHostAddress>
#include <QVector>
typedef QMap<QString, QString> QStringMap;
#include "base/types.h"
namespace Http
{

130
src/base/indexrange.h Normal file
View File

@@ -0,0 +1,130 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
*
* 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 QBT_INDEXRANGE_H
#define QBT_INDEXRANGE_H
#include <QtGlobal>
// Interval is defined via [first;last]
template <typename Index>
class IndexInterval
{
public:
using IndexType = Index;
IndexInterval(IndexType first, IndexType last)
: m_first {first}
, m_last {last}
{
Q_ASSERT(first <= last);
}
IndexType first() const
{
return m_first;
}
IndexType last() const
{
return m_last;
}
private:
IndexType m_first;
IndexType m_last;
};
template <typename T>
inline IndexInterval<T> makeInterval(T first, T last)
{
return {first, last};
}
// range is defined via first index and size
template <typename Index, typename IndexDiff = Index>
class IndexRange
{
public:
using IndexType = Index;
using IndexDiffType = IndexDiff;
constexpr IndexRange()
: m_first {0}
, m_size {0}
{
}
constexpr IndexRange(IndexType first, IndexDiffType size)
: m_first {first}
, m_size {size}
{
}
constexpr IndexRange(const IndexInterval<IndexType> &interval)
: m_first {interval.first()}
, m_size {interval.last() - interval.first() + 1}
{
}
constexpr IndexType begin() const
{
return m_first;
}
constexpr IndexType end() const
{
return m_first + m_size;
}
constexpr IndexDiffType size() const
{
return m_size;
}
constexpr IndexType first() const
{
return m_first;
}
constexpr IndexType last() const
{
return m_first + m_size - 1;
}
constexpr bool isEmpty() const
{
return m_size == 0;
}
private:
IndexType m_first;
IndexDiffType m_size;
};
#endif // QBT_INDEXRANGE_H

View File

@@ -75,6 +75,9 @@ namespace
Preferences::instance()->setNetworkCookies(cookies);
}
using QNetworkCookieJar::allCookies;
using QNetworkCookieJar::setAllCookies;
#ifndef QBT_USES_QT5
virtual bool deleteCookie(const QNetworkCookie &cookie)
{
@@ -188,6 +191,16 @@ bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
}
QList<QNetworkCookie> DownloadManager::allCookies() const
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
}
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
{
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
}
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);

View File

@@ -54,6 +54,8 @@ namespace Net
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
QList<QNetworkCookie> allCookies() const;
void setAllCookies(const QList<QNetworkCookie> &cookieList);
bool deleteCookie(const QNetworkCookie &cookie);
private slots:

View File

@@ -30,15 +30,10 @@
* Contact : hammered999@gmail.com
*/
#include "preferences.h"
#include "qinisettings.h"
#include "logger.h"
#include <QCryptographicHash>
#include <QPair>
#include <QDir>
#include <QReadLocker>
#include <QWriteLocker>
#include <QSettings>
#ifndef DISABLE_GUI
#include <QApplication>
@@ -56,76 +51,18 @@
#endif
#include <cstdlib>
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "utils/fs.h"
#include "utils/misc.h"
#include "settingsstorage.h"
#include "logger.h"
#include "preferences.h"
Preferences* Preferences::m_instance = 0;
static const QString LOG_FOLDER("logs");
Preferences::Preferences()
: m_randomPort(rand() % 64512 + 1024)
, dirty(false)
, lock(QReadWriteLock::Recursive)
{
qRegisterMetaTypeStreamOperators<MaxRatioAction>("MaxRatioAction");
QIniSettings *settings = new QIniSettings;
#ifndef Q_OS_MAC
QIniSettings *settings_new = new QIniSettings("qBittorrent", "qBittorrent_new");
QStringList keys = settings_new->allKeys();
bool use_new = false;
// This means that the PC closed either due to power outage
// or because the disk was full. In any case the settings weren't transfered
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
// contains the most recent settings.
if (!keys.isEmpty()) {
Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
use_new = true;
dirty = true;
}
else {
keys = settings->allKeys();
}
#else
QStringList keys = settings->allKeys();
#endif
// Copy everything into memory. This means even keys inserted in the file manually
// or that we don't touch directly in this code(eg disabled by ifdef). This ensures
// that they will be copied over when save our settings to disk.
for (QStringList::const_iterator i = keys.begin(), e = keys.end(); i != e; ++i) {
#ifndef Q_OS_MAC
if (!use_new)
m_data[*i] = settings->value(*i);
else
m_data[*i] = settings_new->value(*i);
#else
m_data[*i] = settings->value(*i);
#endif
}
//Ensures sync to disk before we attempt to manipulate the files from save().
delete settings;
#ifndef Q_OS_MAC
QString new_path = settings_new->fileName();
delete settings_new;
Utils::Fs::forceRemove(new_path);
if (use_new)
save();
#endif
timer.setSingleShot(true);
timer.setInterval(5 * 1000);
connect(&timer, SIGNAL(timeout()), SLOT(save()));
}
Preferences::~Preferences()
{
save();
}
Preferences *Preferences::instance()
@@ -147,71 +84,14 @@ void Preferences::freeInstance()
}
}
bool Preferences::save()
{
QWriteLocker locker(&lock);
if (!dirty) return false;
#ifndef Q_OS_MAC
// QSettings delete the file before writing it out. This can result in problems
// if the disk is full or a power outage occurs. Those events might occur
// between deleting the file and recreating it. This is a safety measure.
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
// replace qBittorrent_new.ini/qBittorrent.conf with it.
QIniSettings *settings = new QIniSettings("qBittorrent", "qBittorrent_new");
#else
QIniSettings *settings = new QIniSettings;
#endif
for (QHash<QString, QVariant>::const_iterator i = m_data.begin(), e = m_data.end(); i != e; ++i)
settings->setValue(i.key(), i.value());
dirty = false;
locker.unlock();
#ifndef Q_OS_MAC
settings->sync(); // Important to get error status
QString new_path = settings->fileName();
QSettings::Status status = settings->status();
if (status != QSettings::NoError) {
if (status == QSettings::AccessError)
Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
else
Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
delete settings;
Utils::Fs::forceRemove(new_path);
return false;
}
delete settings;
QString final_path = new_path;
int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive);
final_path.remove(index, 4);
Utils::Fs::forceRemove(final_path);
QFile::rename(new_path, final_path);
#else
delete settings;
#endif
return true;
}
const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const
{
QReadLocker locker(&lock);
return m_data.value(key, defaultValue);
return SettingsStorage::instance()->loadValue(key, defaultValue);
}
void Preferences::setValue(const QString &key, const QVariant &value)
{
QWriteLocker locker(&lock);
if (m_data.value(key) == value)
return;
dirty = true;
timer.start();
m_data.insert(key, value);
SettingsStorage::instance()->storeValue(key, value);
}
// General options
@@ -225,16 +105,6 @@ void Preferences::setLocale(const QString &locale)
setValue("Preferences/General/Locale", locale);
}
bool Preferences::useProgramNotification() const
{
return value("Preferences/General/ProgramNotification", true).toBool();
}
void Preferences::useProgramNotification(bool use)
{
setValue("Preferences/General/ProgramNotification", use);
}
bool Preferences::deleteTorrentFilesAsDefault() const
{
return value("Preferences/General/DeleteTorrentsFilesAsDefault", false).toBool();
@@ -397,40 +267,6 @@ void Preferences::setWinStartup(bool b)
#endif
// Downloads
QString Preferences::getSavePath() const
{
QString save_path = value("Preferences/Downloads/SavePath").toString();
if (!save_path.isEmpty())
return Utils::Fs::fromNativePath(save_path);
return Utils::Fs::QDesktopServicesDownloadLocation();
}
void Preferences::setSavePath(const QString &save_path)
{
setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path));
}
bool Preferences::isTempPathEnabled() const
{
return value("Preferences/Downloads/TempPathEnabled", false).toBool();
}
void Preferences::setTempPathEnabled(bool enabled)
{
setValue("Preferences/Downloads/TempPathEnabled", enabled);
}
QString Preferences::getTempPath() const
{
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
}
void Preferences::setTempPath(const QString &path)
{
setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path));
}
bool Preferences::useIncompleteFilesExtension() const
{
return value("Preferences/Downloads/UseIncompleteExtension", false).toBool();
@@ -441,16 +277,6 @@ void Preferences::useIncompleteFilesExtension(bool enabled)
setValue("Preferences/Downloads/UseIncompleteExtension", enabled);
}
bool Preferences::appendTorrentLabel() const
{
return value("Preferences/Downloads/AppendLabel", false).toBool();
}
void Preferences::setAppendTorrentLabel(bool b)
{
setValue("Preferences/Downloads/AppendLabel", b);
}
QString Preferences::lastLocationPath() const
{
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
@@ -471,36 +297,6 @@ void Preferences::preAllocateAllFiles(bool enabled)
return setValue("Preferences/Downloads/PreAllocation", enabled);
}
bool Preferences::useAdditionDialog() const
{
return value("Preferences/Downloads/NewAdditionDialog", true).toBool();
}
void Preferences::useAdditionDialog(bool b)
{
setValue("Preferences/Downloads/NewAdditionDialog", b);
}
bool Preferences::additionDialogFront() const
{
return value("Preferences/Downloads/NewAdditionDialogFront", true).toBool();
}
void Preferences::additionDialogFront(bool b)
{
setValue("Preferences/Downloads/NewAdditionDialogFront", b);
}
bool Preferences::addTorrentsInPause() const
{
return value("Preferences/Downloads/StartInPause", false).toBool();
}
void Preferences::addTorrentsInPause(bool b)
{
setValue("Preferences/Downloads/StartInPause", b);
}
QVariantHash Preferences::getScanDirs() const
{
return value("Preferences/Downloads/ScanDirsV2").toHash();
@@ -645,7 +441,6 @@ void Preferences::setActionOnDblClOnTorrentFn(int act)
// Connection options
int Preferences::getSessionPort() const
{
QReadLocker locker(&lock);
if (useRandomPort())
return m_randomPort;
return value("Preferences/Connection/PortRangeMin", 8999).toInt();
@@ -999,16 +794,6 @@ void Preferences::setGlobalMaxRatio(qreal ratio)
setValue("Preferences/Bittorrent/MaxRatio", ratio);
}
MaxRatioAction Preferences::getMaxRatioAction() const
{
return value("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(MaxRatioAction::Pause)).value<MaxRatioAction>();
}
void Preferences::setMaxRatioAction(MaxRatioAction act)
{
setValue("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(act));
}
// IP Filter
bool Preferences::isFilteringEnabled() const
{
@@ -1065,111 +850,6 @@ void Preferences::setSearchEnabled(bool enabled)
setValue("Preferences/Search/SearchEnabled", enabled);
}
// Execution Log
bool Preferences::isExecutionLogEnabled() const
{
return value("Preferences/ExecutionLog/enabled", false).toBool();
}
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
{
@@ -1664,6 +1344,16 @@ void Preferences::setNetworkInterfaceName(const QString& iface)
setValue("Preferences/Connection/InterfaceName", iface);
}
void Preferences::setNetworkInterfaceAddress(const QString& addr)
{
setValue("Preferences/Connection/InterfaceAddress", addr);
}
QString Preferences::getNetworkInterfaceAddress() const
{
return value("Preferences/Connection/InterfaceAddress").toString();
}
bool Preferences::getListenIPv6() const
{
return value("Preferences/Connection/InterfaceListenIPv6", false).toBool();
@@ -1726,51 +1416,6 @@ void Preferences::useSystemIconTheme(bool enabled)
}
#endif
QStringList Preferences::getTorrentLabels() const
{
return value("TransferListFilters/customLabels").toStringList();
}
void Preferences::setTorrentLabels(const QStringList& labels)
{
setValue("TransferListFilters/customLabels", labels);
}
void Preferences::addTorrentLabelExternal(const QString &label)
{
addTorrentLabel(label);
QString toEmit = label;
emit externalLabelAdded(toEmit);
}
void Preferences::addTorrentLabel(const QString& label)
{
QStringList labels = value("TransferListFilters/customLabels").toStringList();
if (labels.contains(label))
return;
labels << label;
setValue("TransferListFilters/customLabels", labels);
}
void Preferences::removeTorrentLabel(const QString& label)
{
QStringList labels = value("TransferListFilters/customLabels").toStringList();
if (!labels.contains(label))
return;
labels.removeOne(label);
setValue("TransferListFilters/customLabels", labels);
}
QString Preferences::getDefaultLabel() const
{
return value("Preferences/Downloads/DefaultLabel").toString();
}
void Preferences::setDefaultLabel(const QString &defaultLabel)
{
setValue("Preferences/Downloads/DefaultLabel", defaultLabel);
}
bool Preferences::recursiveDownloadDisabled() const
{
return value("Preferences/Advanced/DisableRecursiveDownload", false).toBool();
@@ -2129,63 +1774,6 @@ void Preferences::setTrayIconStyle(TrayIcon::Style style)
// Stuff that don't appear in the Options GUI but are saved
// in the same file.
QByteArray Preferences::getAddNewTorrentDialogState() const
{
#ifdef QBT_USES_QT5
return value("AddNewTorrentDialog/qt5/treeHeaderState").toByteArray();
#else
return value("AddNewTorrentDialog/treeHeaderState").toByteArray();
#endif
}
void Preferences::setAddNewTorrentDialogState(const QByteArray &state)
{
#ifdef QBT_USES_QT5
setValue("AddNewTorrentDialog/qt5/treeHeaderState", state);
#else
setValue("AddNewTorrentDialog/treeHeaderState", state);
#endif
}
int Preferences::getAddNewTorrentDialogPos() const
{
return value("AddNewTorrentDialog/y", -1).toInt();
}
void Preferences::setAddNewTorrentDialogPos(const int &pos)
{
setValue("AddNewTorrentDialog/y", pos);
}
int Preferences::getAddNewTorrentDialogWidth() const
{
return value("AddNewTorrentDialog/width", -1).toInt();
}
void Preferences::setAddNewTorrentDialogWidth(const int &width)
{
setValue("AddNewTorrentDialog/width", width);
}
bool Preferences::getAddNewTorrentDialogExpanded() const
{
return value("AddNewTorrentDialog/expanded", false).toBool();
}
void Preferences::setAddNewTorrentDialogExpanded(const bool expanded)
{
setValue("AddNewTorrentDialog/expanded", expanded);
}
QStringList Preferences::getAddNewTorrentDialogPathHistory() const
{
return value("TorrentAdditionDlg/save_path_history").toStringList();
}
void Preferences::setAddNewTorrentDialogPathHistory(const QStringList &history)
{
setValue("TorrentAdditionDlg/save_path_history", history);
}
QDateTime Preferences::getDNSLastUpd() const
{
@@ -2545,14 +2133,14 @@ void Preferences::setStatusFilterState(const bool checked)
setValue("TransferListFilters/statusFilterState", checked);
}
bool Preferences::getLabelFilterState() const
bool Preferences::getCategoryFilterState() const
{
return value("TransferListFilters/labelFilterState", true).toBool();
return value("TransferListFilters/CategoryFilterState", true).toBool();
}
void Preferences::setLabelFilterState(const bool checked)
void Preferences::setCategoryFilterState(const bool checked)
{
setValue("TransferListFilters/labelFilterState", checked);
setValue("TransferListFilters/CategoryFilterState", checked);
}
bool Preferences::getTrackerFilterState() const
@@ -2593,22 +2181,6 @@ void Preferences::setTransHeaderState(const QByteArray &state)
#endif
}
// Temp code.
// See TorrentStatistics::loadStats() for details.
QVariantHash Preferences::getStats() const
{
return value("Stats/AllStats").toHash();
}
void Preferences::removeStats()
{
QWriteLocker locker(&lock);
dirty = true;
if (!timer.isActive())
timer.start();
m_data.remove("Stats/AllStats");
}
//From old RssSettings class
bool Preferences::isRSSEnabled() const
{
@@ -2622,7 +2194,7 @@ void Preferences::setRSSEnabled(const bool enabled)
uint Preferences::getRSSRefreshInterval() const
{
return value("Preferences/RSS/RSSRefresh", 5).toUInt();
return value("Preferences/RSS/RSSRefresh", 30).toUInt();
}
void Preferences::setRSSRefreshInterval(const uint &interval)
@@ -2680,31 +2252,6 @@ void Preferences::setToolbarTextPosition(const int position)
setValue("Toolbar/textPosition", position);
}
void Preferences::moveRSSCookies()
{
QList<QNetworkCookie> cookies = getNetworkCookies();
QVariantMap hostsTable = value("Rss/hosts_cookies").toMap();
foreach (const QString &key, hostsTable.keys()) {
QVariant value = hostsTable[key];
QList<QByteArray> rawCookies = value.toByteArray().split(':');
foreach (const QByteArray &rawCookie, rawCookies) {
foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) {
cookie.setDomain(key);
cookie.setPath("/");
cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10));
cookies << cookie;
}
}
}
setNetworkCookies(cookies);
QWriteLocker locker(&lock);
dirty = true;
timer.start();
m_data.remove("Rss/hosts_cookies");
}
QList<QNetworkCookie> Preferences::getNetworkCookies() const
{
QList<QNetworkCookie> cookies;
@@ -2745,8 +2292,43 @@ void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable)
setValue("SpeedWidget/graph_enable_" + QString::number(id), enable);
}
void Preferences::upgrade()
{
// Move RSS cookies to global storage
QList<QNetworkCookie> cookies = getNetworkCookies();
QVariantMap hostsTable = value("Rss/hosts_cookies").toMap();
foreach (const QString &key, hostsTable.keys()) {
QVariant value = hostsTable[key];
QList<QByteArray> rawCookies = value.toByteArray().split(':');
foreach (const QByteArray &rawCookie, rawCookies) {
foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) {
cookie.setDomain(key);
cookie.setPath("/");
cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10));
cookies << cookie;
}
}
}
setNetworkCookies(cookies);
QStringList labels = value("TransferListFilters/customLabels").toStringList();
if (!labels.isEmpty()) {
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
foreach (const QString &label, labels) {
if (!categories.contains(label))
categories[label] = "";
}
setValue("BitTorrent/Session/Categories", categories);
SettingsStorage::instance()->removeValue("TransferListFilters/customLabels");
}
SettingsStorage::instance()->removeValue("Rss/hosts_cookies");
SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel");
}
void Preferences::apply()
{
if (save())
if (SettingsStorage::instance()->save())
emit changed();
}

View File

@@ -41,7 +41,7 @@
#include <QNetworkCookie>
#include <QVariant>
#include "base/types.h"
#include "types.h"
enum scheduler_days
{
@@ -89,30 +89,23 @@ namespace DNS
};
}
class SettingsStorage;
class Preferences: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Preferences)
private:
Preferences();
~Preferences();
static Preferences* m_instance;
QHash<QString, QVariant> m_data;
int m_randomPort;
bool dirty;
QTimer timer;
mutable QReadWriteLock lock;
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
private slots:
bool save();
static Preferences* m_instance;
int m_randomPort;
signals:
void changed();
void externalLabelAdded(QString&);
public:
static void initInstance();
@@ -122,8 +115,6 @@ public:
// General options
QString getLocale() const;
void setLocale(const QString &locale);
bool useProgramNotification() const;
void useProgramNotification(bool use);
bool deleteTorrentFilesAsDefault() const;
void setDeleteTorrentFilesAsDefault(bool del);
bool confirmOnExit() const;
@@ -158,28 +149,12 @@ public:
#endif
// Downloads
QString getSavePath() const;
void setSavePath(const QString &save_path);
bool isTempPathEnabled() const;
void setTempPathEnabled(bool enabled);
QString getTempPath() const;
void setTempPath(const QString &path);
QString getDefaultLabel() const;
void setDefaultLabel(const QString &defaultLabel);
bool useIncompleteFilesExtension() const;
void useIncompleteFilesExtension(bool enabled);
bool appendTorrentLabel() const;
void setAppendTorrentLabel(bool b);
QString lastLocationPath() const;
void setLastLocationPath(const QString &path);
bool preAllocateAllFiles() const;
void preAllocateAllFiles(bool enabled);
bool useAdditionDialog() const;
void useAdditionDialog(bool b);
bool additionDialogFront() const;
void additionDialogFront(bool b);
bool addTorrentsInPause() const;
void addTorrentsInPause(bool b);
QVariantHash getScanDirs() const;
void setScanDirs(const QVariantHash &dirs);
QString getScanDirsLastPath() const;
@@ -281,8 +256,6 @@ public:
void setTrackersList(const QString &val);
qreal getGlobalMaxRatio() const;
void setGlobalMaxRatio(qreal ratio);
MaxRatioAction getMaxRatioAction() const;
void setMaxRatioAction(MaxRatioAction act);
// IP Filter
bool isFilteringEnabled() const;
@@ -298,29 +271,6 @@ public:
bool isSearchEnabled() const;
void setSearchEnabled(bool enabled);
// 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;
void setQueueingSystemEnabled(bool enabled);
@@ -413,6 +363,8 @@ public:
void setNetworkInterface(const QString& iface);
QString getNetworkInterfaceName() const;
void setNetworkInterfaceName(const QString& iface);
QString getNetworkInterfaceAddress() const;
void setNetworkInterfaceAddress(const QString& addr);
bool getListenIPv6() const;
void setListenIPv6(bool enable);
QString getNetworkAddress() const;
@@ -427,11 +379,6 @@ public:
bool useSystemIconTheme() const;
void useSystemIconTheme(bool enabled);
#endif
QStringList getTorrentLabels() const;
void setTorrentLabels(const QStringList& labels);
void addTorrentLabelExternal(const QString &label);
void addTorrentLabel(const QString& label);
void removeTorrentLabel(const QString& label);
bool recursiveDownloadDisabled() const;
void disableRecursiveDownload(bool disable = true);
#ifdef Q_OS_WIN
@@ -464,19 +411,8 @@ public:
TrayIcon::Style trayIconStyle() const;
void setTrayIconStyle(TrayIcon::Style style);
// Stuff that don't appear in the Options GUI but are saved
// in the same file.
QByteArray getAddNewTorrentDialogState() const;
void setAddNewTorrentDialogState(const QByteArray &state);
int getAddNewTorrentDialogPos() const;
void setAddNewTorrentDialogPos(const int &pos);
int getAddNewTorrentDialogWidth() const;
void setAddNewTorrentDialogWidth(const int &width);
bool getAddNewTorrentDialogExpanded() const;
void setAddNewTorrentDialogExpanded(const bool expanded);
QStringList getAddNewTorrentDialogPathHistory() const;
void setAddNewTorrentDialogPathHistory(const QStringList &history);
QDateTime getDNSLastUpd() const;
void setDNSLastUpd(const QDateTime &date);
QString getDNSLastIP() const;
@@ -538,7 +474,7 @@ public:
QByteArray getTorImportGeometry() const;
void setTorImportGeometry(const QByteArray &geometry);
bool getStatusFilterState() const;
bool getLabelFilterState() const;
bool getCategoryFilterState() const;
bool getTrackerFilterState() const;
int getTransSelFilter() const;
void setTransSelFilter(const int &index);
@@ -547,11 +483,6 @@ public:
int getToolbarTextPosition() const;
void setToolbarTextPosition(const int position);
// Temp code.
// See TorrentStatistics::loadStats() for details.
QVariantHash getStats() const;
void removeStats();
//From old RssSettings class
bool isRSSEnabled() const;
void setRSSEnabled(const bool enabled);
@@ -569,8 +500,6 @@ public:
// Network
QList<QNetworkCookie> getNetworkCookies() const;
void setNetworkCookies(const QList<QNetworkCookie> &cookies);
// Temporary method for upgrade purposes
void moveRSSCookies();
// SpeedWidget
int getSpeedWidgetPeriod() const;
@@ -578,9 +507,11 @@ public:
bool getSpeedWidgetGraphEnable(int id) const;
void setSpeedWidgetGraphEnable(int id, const bool enable);
void upgrade();
public slots:
void setStatusFilterState(bool checked);
void setLabelFilterState(bool checked);
void setCategoryFilterState(bool checked);
void setTrackerFilterState(bool checked);
void apply();

View File

@@ -178,7 +178,7 @@ DownloadRulePtr DownloadRule::fromVariantHash(const QVariantHash &ruleHash)
rule->setRssFeeds(ruleHash.value("affected_feeds").toStringList());
rule->setEnabled(ruleHash.value("enabled", false).toBool());
rule->setSavePath(ruleHash.value("save_path").toString());
rule->setLabel(ruleHash.value("label_assigned").toString());
rule->setCategory(ruleHash.value("category_assigned").toString());
rule->setAddPaused(AddPausedState(ruleHash.value("add_paused").toUInt()));
rule->setLastMatch(ruleHash.value("last_match").toDateTime());
rule->setIgnoreDays(ruleHash.value("ignore_days").toInt());
@@ -194,7 +194,7 @@ QVariantHash DownloadRule::toVariantHash() const
hash["save_path"] = m_savePath;
hash["affected_feeds"] = m_rssFeeds;
hash["enabled"] = m_enabled;
hash["label_assigned"] = m_label;
hash["category_assigned"] = m_category;
hash["use_regex"] = m_useRegex;
hash["add_paused"] = m_apstate;
hash["episode_filter"] = m_episodeFilter;
@@ -210,10 +210,7 @@ bool DownloadRule::operator==(const DownloadRule &other) const
void DownloadRule::setSavePath(const QString &savePath)
{
if (!savePath.isEmpty() && (QDir(savePath) != QDir(Preferences::instance()->getSavePath())))
m_savePath = Utils::Fs::fromNativePath(savePath);
else
m_savePath = QString();
m_savePath = Utils::Fs::fromNativePath(savePath);
}
DownloadRule::AddPausedState DownloadRule::addPaused() const
@@ -226,14 +223,14 @@ void DownloadRule::setAddPaused(const DownloadRule::AddPausedState &aps)
m_apstate = aps;
}
QString DownloadRule::label() const
QString DownloadRule::category() const
{
return m_label;
return m_category;
}
void DownloadRule::setLabel(const QString &label)
void DownloadRule::setCategory(const QString &category)
{
m_label = label;
m_category = category;
}
bool DownloadRule::isEnabled() const

View File

@@ -69,8 +69,8 @@ namespace Rss
void setSavePath(const QString &savePath);
AddPausedState addPaused() const;
void setAddPaused(const AddPausedState &aps);
QString label() const;
void setLabel(const QString &label);
QString category() const;
void setCategory(const QString &category);
bool isEnabled() const;
void setEnabled(bool enable);
void setLastMatch(const QDateTime &d);
@@ -93,7 +93,7 @@ namespace Rss
QStringList m_mustNotContain;
QString m_episodeFilter;
QString m_savePath;
QString m_label;
QString m_category;
bool m_enabled;
QStringList m_rssFeeds;
bool m_useRegex;

View File

@@ -391,7 +391,7 @@ void Feed::downloadArticleTorrentIfMatching(const ArticlePtr &article)
BitTorrent::AddTorrentParams params;
params.savePath = matchingRule->savePath();
params.label = matchingRule->label();
params.category = matchingRule->category();
if (matchingRule->addPaused() == DownloadRule::ALWAYS_PAUSED)
params.addPaused = TriStateBool::True;
else if (matchingRule->addPaused() == DownloadRule::NEVER_PAUSED)

View File

@@ -28,20 +28,17 @@
* Contact : chris@qbittorrent.org
*/
#include "scanfoldersmodel.h"
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QTemporaryFile>
#include <QTextStream>
#include "utils/misc.h"
#include "utils/fs.h"
#include "preferences.h"
#include "logger.h"
#include "filesystemwatcher.h"
#include "bittorrent/session.h"
#include "scanfoldersmodel.h"
#include "filesystemwatcher.h"
#include "preferences.h"
#include "utils/fs.h"
struct ScanFoldersModel::PathData
{
@@ -128,10 +125,8 @@ QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
else if (role == Qt::DisplayRole) {
switch (pathData->downloadType) {
case DOWNLOAD_IN_WATCH_FOLDER:
value = tr("Watch Folder");
break;
case DEFAULT_LOCATION:
value = tr("Default Folder");
value = pathTypeDisplayName(pathData->downloadType);
break;
case CUSTOM_LOCATION:
value = pathData->downloadPath;
@@ -153,10 +148,10 @@ QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation,
switch (section) {
case WATCH:
title = tr("Watched Folder");
title = tr("Monitored Folder");
break;
case DOWNLOAD:
title = tr("Save Files to");
title = tr("Override Save Location");
break;
}
@@ -392,3 +387,18 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
}
}
}
QString ScanFoldersModel::pathTypeDisplayName(const PathType type)
{
switch(type) {
case DOWNLOAD_IN_WATCH_FOLDER:
return tr("Monitored folder");
case DEFAULT_LOCATION:
return tr("Default save location");
case CUSTOM_LOCATION:
return tr("Browse...");
default:
qDebug("Invalid PathType: %d", type);
};
return QString();
}

View File

@@ -34,13 +34,10 @@
#include <QAbstractListModel>
#include <QList>
QT_BEGIN_NAMESPACE
class QStringList;
QT_END_NAMESPACE
class FileSystemWatcher;
class ScanFoldersModel : public QAbstractListModel
class ScanFoldersModel: public QAbstractListModel
{
Q_OBJECT
Q_DISABLE_COPY(ScanFoldersModel)
@@ -71,7 +68,9 @@ public:
static bool initInstance(QObject *parent = 0);
static void freeInstance();
static ScanFoldersModel *instance();
static ScanFoldersModel* instance();
static QString pathTypeDisplayName(const PathType type);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -81,8 +80,8 @@ public:
// TODO: removePaths(); singular version becomes private helper functions;
// also: remove functions should take modelindexes
PathStatus addPath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath, bool addToFSWatcher = true);
PathStatus updatePath(const QString &watchPath, const PathType& downloadType, const QString &downloadPath);
PathStatus addPath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath, bool addToFSWatcher = true);
PathStatus updatePath(const QString &watchPath, const PathType &downloadType, const QString &downloadPath);
// PRECONDITION: The paths must have been added with addPath() first.
void addToFSWatcher(const QStringList &watchPaths);
void removePath(int row, bool removeFromFSWatcher = true);

View File

@@ -266,6 +266,21 @@ void SearchEngine::cancelSearch()
}
}
void SearchEngine::downloadTorrent(const QString &siteUrl, const QString &url)
{
QProcess *downloadProcess = new QProcess(this);
downloadProcess->setEnvironment(QProcess::systemEnvironment());
connect(downloadProcess, SIGNAL(finished(int)), this, SLOT(torrentFileDownloadFinished(int)));
m_downloaders << downloadProcess;
QStringList params {
Utils::Fs::toNativePath(engineLocation() + "/nova2dl.py"),
siteUrl,
url
};
// Launch search
downloadProcess->start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly);
}
void SearchEngine::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins)
{
// Search process already running or
@@ -295,6 +310,11 @@ QString SearchEngine::categoryFullName(const QString &categoryName)
return tr(m_categoryNames.value(categoryName).toUtf8().constData());
}
QString SearchEngine::pluginFullName(const QString &pluginName)
{
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
}
QString SearchEngine::pluginsLocation()
{
return QString("%1/engines").arg(engineLocation());
@@ -357,6 +377,21 @@ void SearchEngine::pluginDownloadFailed(const QString &url, const QString &reaso
emit pluginInstallationFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason));
}
void SearchEngine::torrentFileDownloadFinished(int exitcode)
{
QProcess *downloadProcess = static_cast<QProcess*>(sender());
if (exitcode == 0) {
QString line = QString::fromUtf8(downloadProcess->readAllStandardOutput()).trimmed();
QStringList parts = line.split(' ');
if (parts.size() == 2)
emit torrentFileDownloaded(parts[0]);
}
qDebug() << "Deleting downloadProcess";
m_downloaders.removeOne(downloadProcess);
downloadProcess->deleteLater();
}
// Update nova.py search plugin if necessary
void SearchEngine::updateNova()
{
@@ -383,6 +418,12 @@ void SearchEngine::updateNova()
QFile::copy(":/" + novaFolder + "/nova2.py", filePath);
}
filePath = searchDir.absoluteFilePath("nova2dl.py");
if (getPluginVersion(":/" + novaFolder + "/nova2dl.py") > getPluginVersion(filePath)) {
removePythonScriptIfExists(filePath);
QFile::copy(":/" + novaFolder + "/nova2dl.py", filePath);
}
filePath = searchDir.absoluteFilePath("fix_encoding.py");
QFile::copy(":/" + novaFolder + "/fix_encoding.py", filePath);

View File

@@ -84,8 +84,11 @@ public:
void startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
void cancelSearch();
void downloadTorrent(const QString &siteUrl, const QString &url);
static qreal getPluginVersion(QString filePath);
static QString categoryFullName(const QString &categoryName);
QString pluginFullName(const QString &pluginName);
static QString pluginsLocation();
signals:
@@ -102,6 +105,8 @@ signals:
void checkForUpdatesFinished(const QHash<QString, qreal> &updateInfo);
void checkForUpdatesFailed(const QString &reason);
void torrentFileDownloaded(const QString &path);
private slots:
void onTimeout();
void readSearchOutput();
@@ -110,6 +115,7 @@ private slots:
void versionInfoDownloadFailed(const QString &url, const QString &reason);
void pluginDownloaded(const QString &url, QString filePath);
void pluginDownloadFailed(const QString &url, const QString &reason);
void torrentFileDownloadFinished(int exitcode);
private:
void update();
@@ -132,6 +138,7 @@ private:
bool m_searchStopped;
QTimer *m_searchTimeout;
QByteArray m_searchResultLineTruncated;
QList<QProcess*> m_downloaders;
};
#endif // SEARCHENGINE_H

View File

@@ -0,0 +1,286 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2014 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 "settingsstorage.h"
#include <memory>
#include <QFile>
#include <QHash>
#include <QStringList>
#include <QSettings>
#include "logger.h"
#include "utils/fs.h"
#ifdef Q_OS_MAC
#define QSETTINGS_SYNC_IS_SAVE // whether QSettings::sync() is "atomic"
#endif
namespace
{
// Encapsulates serialization of settings in "atomic" way.
// write() does not leave half-written files,
// read() has a workaround for a case of power loss during a previous serialization
class TransactionalSettings
{
public:
TransactionalSettings(const QString &name)
: m_name(name)
{
}
QVariantHash read();
bool write(const QVariantHash &data);
private:
// we return actual file names used by QSettings because
// there is no other way to get that name except
// actually create a QSettings object.
// if serialization operation was not successful we return empty string
QString deserialize(const QString &name, QVariantHash &data);
QString serialize(const QString &name, const QVariantHash &data);
using SettingsPtr = std::unique_ptr<QSettings>;
SettingsPtr createSettings(const QString &name)
{
#ifdef Q_OS_WIN
return SettingsPtr(new QSettings(QSettings::IniFormat, QSettings::UserScope, "qBittorrent", name));
#else
return SettingsPtr(new QSettings("qBittorrent", name));
#endif
}
QString m_name;
};
#ifdef QBT_USES_QT5
typedef QHash<QString, QString> MappingTable;
#else
class MappingTable: public QHash<QString, QString>
{
public:
MappingTable(std::initializer_list<std::pair<QString, QString>> list)
{
reserve(static_cast<int>(list.size()));
for (const auto &i : list)
insert(i.first, i.second);
}
};
#endif
QString mapKey(const QString &key)
{
static const MappingTable keyMapping = {
{ "BitTorrent/Session/MaxRatioAction", "Preferences/Bittorrent/MaxRatioAction" },
{ "BitTorrent/Session/DefaultSavePath", "Preferences/Downloads/SavePath" },
{ "BitTorrent/Session/TempPath", "Preferences/Downloads/TempPath" },
{ "BitTorrent/Session/TempPathEnabled", "Preferences/Downloads/TempPathEnabled" },
{ "BitTorrent/Session/AddTorrentPaused", "Preferences/Downloads/StartInPause" },
#ifdef QBT_USES_QT5
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/qt5/treeHeaderState" },
#else
{ "AddNewTorrentDialog/TreeHeaderState", "AddNewTorrentDialog/treeHeaderState" },
#endif
{ "AddNewTorrentDialog/Width", "AddNewTorrentDialog/width" },
{ "AddNewTorrentDialog/Position", "AddNewTorrentDialog/y" },
{ "AddNewTorrentDialog/Expanded", "AddNewTorrentDialog/expanded" },
{ "AddNewTorrentDialog/SavePathHistory", "TorrentAdditionDlg/save_path_history" },
{ "AddNewTorrentDialog/Enabled", "Preferences/Downloads/NewAdditionDialog" },
{ "AddNewTorrentDialog/TopLevel", "Preferences/Downloads/NewAdditionDialogFront" }
};
return keyMapping.value(key, key);
}
}
SettingsStorage *SettingsStorage::m_instance = nullptr;
SettingsStorage::SettingsStorage()
: m_data{TransactionalSettings(QLatin1String("qBittorrent")).read()}
, m_dirty(false)
, m_lock(QReadWriteLock::Recursive)
{
m_timer.setSingleShot(true);
m_timer.setInterval(5 * 1000);
connect(&m_timer, SIGNAL(timeout()), SLOT(save()));
}
SettingsStorage::~SettingsStorage()
{
save();
}
void SettingsStorage::initInstance()
{
if (!m_instance)
m_instance = new SettingsStorage;
}
void SettingsStorage::freeInstance()
{
delete m_instance;
m_instance = nullptr;
}
SettingsStorage *SettingsStorage::instance()
{
return m_instance;
}
bool SettingsStorage::save()
{
if (!m_dirty) return false; // Obtaining the lock is expensive, let's check early
QWriteLocker locker(&m_lock);
if (!m_dirty) return false; // something might have changed while we were getting the lock
TransactionalSettings settings(QLatin1String("qBittorrent"));
if (settings.write(m_data)) {
m_dirty = false;
return true;
}
return false;
}
QVariant SettingsStorage::loadValue(const QString &key, const QVariant &defaultValue) const
{
QReadLocker locker(&m_lock);
return m_data.value(mapKey(key), defaultValue);
}
void SettingsStorage::storeValue(const QString &key, const QVariant &value)
{
QString realKey = mapKey(key);
QWriteLocker locker(&m_lock);
if (m_data.value(realKey) != value) {
m_dirty = true;
m_data.insert(realKey, value);
m_timer.start();
}
}
void SettingsStorage::removeValue(const QString &key)
{
QString realKey = mapKey(key);
QWriteLocker locker(&m_lock);
if (m_data.contains(realKey)) {
m_dirty = true;
m_data.remove(realKey);
m_timer.start();
}
}
QVariantHash TransactionalSettings::read()
{
QVariantHash res;
#ifdef QSETTINGS_SYNC_IS_SAVE
deserialize(m_name, res);
#else
bool writeBackNeeded = false;
QString newPath = deserialize(m_name + QLatin1String("_new"), res);
if (!newPath.isEmpty()) { // "_new" file is NOT empty
// This means that the PC closed either due to power outage
// or because the disk was full. In any case the settings weren't transfered
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
// contains the most recent settings.
Logger::instance()->addMessage(QObject::tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
writeBackNeeded = true;
}
else {
deserialize(m_name, res);
}
Utils::Fs::forceRemove(newPath);
if (writeBackNeeded)
write(res);
#endif
return res;
}
bool TransactionalSettings::write(const QVariantHash &data)
{
#ifdef QSETTINGS_SYNC_IS_SAVE
serialize(m_name, data);
#else
// QSettings delete the file before writing it out. This can result in problems
// if the disk is full or a power outage occurs. Those events might occur
// between deleting the file and recreating it. This is a safety measure.
// Write everything to qBittorrent_new.ini/qBittorrent_new.conf and if it succeeds
// replace qBittorrent.ini/qBittorrent.conf with it.
QString newPath = serialize(m_name + QLatin1String("_new"), data);
if (newPath.isEmpty()) {
Utils::Fs::forceRemove(newPath);
return false;
}
QString finalPath = newPath;
int index = finalPath.lastIndexOf("_new", -1, Qt::CaseInsensitive);
finalPath.remove(index, 4);
Utils::Fs::forceRemove(finalPath);
QFile::rename(newPath, finalPath);
#endif
return true;
}
QString TransactionalSettings::deserialize(const QString &name, QVariantHash &data)
{
SettingsPtr settings = createSettings(name);
if (settings->allKeys().isEmpty())
return QString();
// Copy everything into memory. This means even keys inserted in the file manually
// or that we don't touch directly in this code (eg disabled by ifdef). This ensures
// that they will be copied over when save our settings to disk.
foreach (const QString &key, settings->allKeys())
data.insert(key, settings->value(key));
return settings->fileName();
}
QString TransactionalSettings::serialize(const QString &name, const QVariantHash &data)
{
SettingsPtr settings = createSettings(name);
for (auto i = data.begin(); i != data.end(); ++i)
settings->setValue(i.key(), i.value());
settings->sync(); // Important to get error status
QSettings::Status status = settings->status();
if (status != QSettings::NoError) {
if (status == QSettings::AccessError)
Logger::instance()->addMessage(QObject::tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
else
Logger::instance()->addMessage(QObject::tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
return QString();
}
return settings->fileName();
}

View File

@@ -1,6 +1,7 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2014 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
@@ -24,63 +25,41 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org
*/
#ifndef TORRENTIMPORTDLG_H
#define TORRENTIMPORTDLG_H
#ifndef SETTINGSSTORAGE_H
#define SETTINGSSTORAGE_H
#include <QDialog>
#include <QStringList>
#include <QObject>
#include <QVariantHash>
#include <QTimer>
#include <QReadWriteLock>
#include "base/bittorrent/torrentinfo.h"
namespace Ui
{
class TorrentImportDlg;
}
class TorrentImportDlg: public QDialog
class SettingsStorage: public QObject
{
Q_OBJECT
SettingsStorage();
~SettingsStorage();
public:
explicit TorrentImportDlg(QWidget *parent = 0);
~TorrentImportDlg();
static void initInstance();
static void freeInstance();
static SettingsStorage* instance();
static void importTorrent();
QVariant loadValue(const QString &key, const QVariant &defaultValue = QVariant()) const;
void storeValue(const QString &key, const QVariant &value);
void removeValue(const QString &key);
QString getTorrentPath() const;
QString getContentPath() const;
bool fileRenamed() const;
BitTorrent::TorrentInfo torrent() const;
bool skipFileChecking() const;
protected slots:
void loadTorrent(const QString &torrentPath);
void initializeFilesPath();
private slots:
void on_browseTorrentBtn_clicked();
void on_browseContentBtn_clicked();
void on_importBtn_clicked();
protected:
void closeEvent(QCloseEvent *event);
public slots:
bool save();
private:
void loadSettings();
void saveSettings();
static SettingsStorage *m_instance;
private:
Ui::TorrentImportDlg *ui;
BitTorrent::TorrentInfo m_torrentInfo;
// NOTE: Where do we use it?
QStringList m_filesPath;
QString m_contentPath;
QString m_torrentPath;
bool m_fileRenamed;
QVariantHash m_data;
bool m_dirty;
QTimer m_timer;
mutable QReadWriteLock m_lock;
};
#endif // TORRENTIMPORTDLG_H
#endif // SETTINGSSTORAGE_H

View File

@@ -0,0 +1,100 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@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 "torrentfileguard.h"
#include <QMetaEnum>
#include "settingsstorage.h"
#include "utils/fs.h"
namespace
{
const QLatin1String KEY_AUTO_DELETE_ENABLED ("Core/AutoDeleteAddedTorrentFile");
}
FileGuard::FileGuard(const QString &path)
: m_path {path}
, m_remove {true}
{
}
void FileGuard::setAutoRemove(bool remove) noexcept
{
m_remove = remove;
}
FileGuard::~FileGuard()
{
if (m_remove && !m_path.isEmpty())
Utils::Fs::forceRemove(m_path); // forceRemove() checks for file existence
}
TorrentFileGuard::TorrentFileGuard(const QString &path)
: m_mode {autoDeleteMode()}
, m_wasAdded {false}
, m_guard {m_mode != Never ? path : QString()}
{
}
TorrentFileGuard::~TorrentFileGuard()
{
if (!m_wasAdded && (m_mode != Always))
m_guard.setAutoRemove(false);
}
void TorrentFileGuard::markAsAddedToSession()
{
m_wasAdded = true;
}
void TorrentFileGuard::setAutoRemove(bool remove)
{
m_guard.setAutoRemove(remove);
}
TorrentFileGuard::AutoDeleteMode TorrentFileGuard::autoDeleteMode()
{
QMetaEnum meta {modeMetaEnum()};
return static_cast<AutoDeleteMode>(meta.keyToValue(SettingsStorage::instance()->loadValue(
KEY_AUTO_DELETE_ENABLED, meta.valueToKey(Never)).toByteArray()));
}
void TorrentFileGuard::setAutoDeleteMode(TorrentFileGuard::AutoDeleteMode mode)
{
QMetaEnum meta {modeMetaEnum()};
SettingsStorage::instance()->storeValue(KEY_AUTO_DELETE_ENABLED, meta.valueToKey(mode));
}
QMetaEnum TorrentFileGuard::modeMetaEnum()
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
return QMetaEnum::fromType<AutoDeleteMode>();
#else
return staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("AutoDeleteMode"));
#endif
}

View File

@@ -0,0 +1,95 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@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 <QString>
#include <QMetaType>
class QMetaEnum;
/// Utility class to defer file deletion
class FileGuard
{
public:
FileGuard(const QString &path = QString());
~FileGuard();
/// Cancels or re-enables deferred file deletion
void setAutoRemove(bool remove) noexcept;
private:
QString m_path;
bool m_remove;
};
/// Reads settings for .torrent files from preferences
/// and sets the file guard up accordingly
class TorrentFileGuard
{
Q_GADGET
public:
TorrentFileGuard(const QString &path = QString());
~TorrentFileGuard();
/// marks the torrent file as loaded (added) into the BitTorrent::Session
void markAsAddedToSession();
void setAutoRemove(bool remove);
enum AutoDeleteMode // do not change these names: they are stored in config file
{
Never,
IfAdded,
Always
};
// static interface to get/set preferences
static AutoDeleteMode autoDeleteMode();
static void setAutoDeleteMode(AutoDeleteMode mode);
private:
static QMetaEnum modeMetaEnum();
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
Q_ENUMS(AutoDeleteMode)
#else
Q_ENUM(AutoDeleteMode)
#endif
AutoDeleteMode m_mode;
bool m_wasAdded;
// Qt 4 moc has troubles with Q_GADGET: if Q_GADGET is present in a class, moc unconditionally
// references in the generated code the statiMetaObject from the class ancestor.
// Moreover, if the ancestor class has Q_GADGET but does not have other
// Q_ declarations, moc does not generate staticMetaObject for it. These results
// in referencing the non existent staticMetaObject and such code fails to compile.
// This problem is NOT present in Qt 5.7.0 and maybe in some older Qt 5 versions too
// Qt 4.8.7 has it.
// Therefore, we can't inherit FileGuard :(
FileGuard m_guard;
};
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
Q_DECLARE_METATYPE(TorrentFileGuard::AutoDeleteMode)
#endif

View File

@@ -29,7 +29,7 @@
#include "bittorrent/torrenthandle.h"
#include "torrentfilter.h"
const QString TorrentFilter::AnyLabel;
const QString TorrentFilter::AnyCategory;
const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString());
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
@@ -49,16 +49,16 @@ TorrentFilter::TorrentFilter()
{
}
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label)
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString category)
: m_type(type)
, m_label(label)
, m_category(category)
, m_hashSet(hashSet)
{
}
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label)
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString category)
: m_type(All)
, m_label(label)
, m_category(category)
, m_hashSet(hashSet)
{
setTypeByName(filter);
@@ -108,13 +108,13 @@ bool TorrentFilter::setHashSet(const QStringSet &hashSet)
return false;
}
bool TorrentFilter::setLabel(const QString &label)
bool TorrentFilter::setCategory(const QString &category)
{
// QString::operator==() doesn't distinguish between empty and null strings.
if ((m_label != label)
|| (m_label.isNull() && !label.isNull())
|| (!m_label.isNull() && label.isNull())) {
m_label = label;
if ((m_category != category)
|| (m_category.isNull() && !category.isNull())
|| (!m_category.isNull() && category.isNull())) {
m_category = category;
return true;
}
@@ -125,7 +125,7 @@ bool TorrentFilter::match(TorrentHandle *const torrent) const
{
if (!torrent) return false;
return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent));
return (matchState(torrent) && matchHash(torrent) && matchCategory(torrent));
}
bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const
@@ -160,9 +160,8 @@ bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const
else return m_hashSet.contains(torrent->hash());
}
bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const
bool TorrentFilter::matchCategory(BitTorrent::TorrentHandle *const torrent) const
{
if (m_label.isNull()) return true;
else if (m_label.isEmpty()) return torrent->label().isEmpty();
else return (torrent->label() == m_label);
if (m_category.isNull()) return true;
else return (torrent->belongsToCategory(m_category));
}

View File

@@ -58,7 +58,7 @@ public:
Errored
};
static const QString AnyLabel;
static const QString AnyCategory;
static const QStringSet AnyHash;
static const TorrentFilter DownloadingTorrent;
@@ -71,24 +71,24 @@ public:
static const TorrentFilter ErroredTorrent;
TorrentFilter();
// label: pass empty string for "no label" or null string (QString()) for "any label"
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel);
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel);
// category: pass empty string for "no category" or null string (QString()) for "any category"
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString category = AnyCategory);
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString category = AnyCategory);
bool setType(Type type);
bool setTypeByName(const QString &filter);
bool setHashSet(const QStringSet &hashSet);
bool setLabel(const QString &label);
bool setCategory(const QString &category);
bool match(BitTorrent::TorrentHandle *const torrent) const;
private:
bool matchState(BitTorrent::TorrentHandle *const torrent) const;
bool matchHash(BitTorrent::TorrentHandle *const torrent) const;
bool matchLabel(BitTorrent::TorrentHandle *const torrent) const;
bool matchCategory(BitTorrent::TorrentHandle *const torrent) const;
Type m_type;
QString m_label;
QString m_category;
QStringSet m_hashSet;
};

View File

@@ -29,46 +29,18 @@
#ifndef TYPES_H
#define TYPES_H
#include <QVariant>
#include <QDataStream>
#include <QMap>
const qlonglong MAX_ETA = 8640000;
enum class MaxRatioAction
enum class ShutdownDialogAction
{
Pause,
Remove
};
Q_DECLARE_METATYPE(MaxRatioAction)
enum class TorrentExportFolder
{
Regular,
Finished
};
enum class ShutdownAction
{
None,
Exit,
Shutdown,
Suspend,
Hibernate
};
template<typename T>
inline QDataStream &operator<<(QDataStream &out, const T &val)
{
return (out << static_cast<int>(val));
}
template<typename T>
inline QDataStream &operator>>(QDataStream &in, T &val)
{
int tmp;
in >> tmp;
val = static_cast<T>(tmp);
return in;
}
typedef QMap<QString, QString> QStringMap;
#endif // TYPES_H

View File

@@ -250,22 +250,23 @@ bool Utils::Fs::sameFiles(const QString& path1, const QString& path2)
return same;
}
QString Utils::Fs::toValidFileSystemName(const QString &filename)
QString Utils::Fs::toValidFileSystemName(const QString &name, bool allowSeparators)
{
static const QRegExp regex("[\\\\/:?\"*<>|]");
QRegExp regex(allowSeparators ? "[:?\"*<>|]+" : "[\\\\/:?\"*<>|]+");
QString validName = filename.trimmed();
QString validName = name.trimmed();
validName.replace(regex, " ");
qDebug() << "toValidFileSystemName:" << filename << "=>" << validName;
qDebug() << "toValidFileSystemName:" << name << "=>" << validName;
return validName;
}
bool Utils::Fs::isValidFileSystemName(const QString& filename)
bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
{
if (filename.isEmpty()) return false;
const QRegExp regex("[\\\\/:?\"*<>|]");
return !filename.contains(regex);
if (name.isEmpty()) return false;
QRegExp regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]");
return !name.contains(regex);
}
qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path)

View File

@@ -48,8 +48,8 @@ namespace Utils
QString folderName(const QString& file_path);
qint64 computePathSize(const QString& path);
bool sameFiles(const QString& path1, const QString& path2);
QString toValidFileSystemName(const QString &filename);
bool isValidFileSystemName(const QString& filename);
QString toValidFileSystemName(const QString &name, bool allowSeparators = false);
bool isValidFileSystemName(const QString& name, bool allowSeparators = false);
qlonglong freeDiskSpaceOnPath(QString path);
QString branchPath(const QString& file_path, QString* removed = 0);
bool sameFileNames(const QString& first, const QString& second);

View File

@@ -86,20 +86,21 @@ static struct { const char *source; const char *comment; } units[] = {
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
};
#ifndef DISABLE_GUI
void Utils::Misc::shutdownComputer(ShutdownAction action)
void Utils::Misc::shutdownComputer(const ShutdownDialogAction &action)
{
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
// Use dbus to power off / suspend the system
if (action != ShutdownAction::Shutdown) {
if (action != ShutdownDialogAction::Shutdown) {
// Some recent systems use systemd's logind
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
if (login1Iface.isValid()) {
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
login1Iface.call("Suspend", false);
else
login1Iface.call("Hibernate", false);
@@ -109,7 +110,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
"org.freedesktop.UPower", QDBusConnection::systemBus());
if (upowerIface.isValid()) {
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
upowerIface.call("Suspend");
else
upowerIface.call("Hibernate");
@@ -119,7 +120,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
"org.freedesktop.Hal.Device.SystemPowerManagement",
QDBusConnection::systemBus());
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
halIface.call("Suspend", 5);
else
halIface.call("Hibernate");
@@ -148,7 +149,7 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
#endif
#ifdef Q_OS_MAC
AEEventID EventToSend;
if (action != ShutdownAction::Shutdown)
if (action != ShutdownDialogAction::Shutdown)
EventToSend = kAESleep;
else
EventToSend = kAEShutDown;
@@ -201,9 +202,9 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
if (GetLastError() != ERROR_SUCCESS)
return;
if (action == ShutdownAction::Suspend)
if (action == ShutdownDialogAction::Suspend)
SetSuspendState(false, false, false);
else if (action == ShutdownAction::Hibernate)
else if (action == ShutdownDialogAction::Hibernate)
SetSuspendState(true, false, false);
else
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
@@ -214,7 +215,6 @@ void Utils::Misc::shutdownComputer(ShutdownAction action)
(PTOKEN_PRIVILEGES) NULL, 0);
#endif
}
#endif // DISABLE_GUI
#ifndef DISABLE_GUI
// Get screen center
@@ -318,30 +318,58 @@ QString Utils::Misc::pythonVersionComplete() {
return version;
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
QString Utils::Misc::unitString(Utils::Misc::SizeUnit unit)
{
return QCoreApplication::translate("misc",
units[static_cast<int>(unit)].source, units[static_cast<int>(unit)].comment);
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
// value must be given in bytes
// to send numbers instead of strings with suffixes
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
bool Utils::Misc::friendlyUnit(qint64 sizeInBytes, qreal &val, Utils::Misc::SizeUnit &unit)
{
if (val < 0)
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
if (sizeInBytes < 0) return false;
int i = 0;
while(val >= 1024. && i < 4) {
val /= 1024.;
qreal rawVal = static_cast<qreal>(sizeInBytes);
while ((rawVal >= 1024.) && (i <= static_cast<int>(SizeUnit::ExbiByte))) {
rawVal /= 1024.;
++i;
}
val = rawVal;
unit = static_cast<SizeUnit>(i);
return true;
}
QString Utils::Misc::friendlyUnit(qint64 bytesValue, bool isSpeed)
{
SizeUnit unit;
qreal friendlyVal;
if (!friendlyUnit(bytesValue, friendlyVal, unit)) {
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
}
QString ret;
if (i == 0)
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
if (unit == SizeUnit::Byte)
ret = QString::number(bytesValue) + " " + unitString(unit);
else
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
if (is_speed)
ret = Utils::String::fromDouble(friendlyVal, 1) + " " + unitString(unit);
if (isSpeed)
ret += QCoreApplication::translate("misc", "/s", "per second");
return ret;
}
qlonglong Utils::Misc::sizeInBytes(qreal size, Utils::Misc::SizeUnit unit)
{
for (int i = 0; i < static_cast<int>(unit); ++i) {
size *= 1024;
}
return size;
}
bool Utils::Misc::isPreviewable(const QString& extension)
{
static QSet<QString> multimedia_extensions;

View File

@@ -39,6 +39,7 @@
#include <QFile>
#include <QDir>
#include <QUrl>
#include <QSize>
#include "base/types.h"
/* Miscellaneous functions that can be useful */
@@ -47,11 +48,28 @@ namespace Utils
{
namespace Misc
{
// use binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
enum class SizeUnit
{
Byte, // 1024^0,
KibiByte, // 1024^1,
MebiByte, // 1024^2,
GibiByte, // 1024^3,
TebiByte, // 1024^4,
PebiByte, // 1024^5,
ExbiByte // 1024^6,
// int64 is used for sizes and thus the next units can not be handled
// ZebiByte, // 1024^7,
// YobiByte, // 1024^8
};
QString parseHtmlLinks(const QString &raw_text);
bool isUrl(const QString &s);
void shutdownComputer(const ShutdownDialogAction &action);
#ifndef DISABLE_GUI
void shutdownComputer(ShutdownAction action);
// Get screen center
QPoint screenCenter(QWidget *win);
QSize smallIconSize();
@@ -63,11 +81,15 @@ namespace Utils
int pythonVersion();
QString pythonExecutable();
QString pythonVersionComplete();
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
QString unitString(SizeUnit unit);
// return best user friendly storage unit (B, KiB, MiB, GiB, TiB)
// value must be given in bytes
QString friendlyUnit(qreal val, bool is_speed = false);
bool friendlyUnit(qint64 sizeInBytes, qreal& val, SizeUnit& unit);
QString friendlyUnit(qint64 bytesValue, bool isSpeed = false);
qint64 sizeInBytes(qreal size, SizeUnit unit);
bool isPreviewable(const QString& extension);
// Take a number of seconds and return an user-friendly

View File

@@ -27,12 +27,149 @@
* exception statement from your version.
*/
#include <QByteArray>
#include <QString>
#include <QLocale>
#include <cmath>
#include "string.h"
#include <cmath>
#include <QByteArray>
#include <QtGlobal>
#include <QLocale>
#ifdef QBT_USES_QT5
#include <QCollator>
#endif
#ifdef Q_OS_MAC
#include <QThreadStorage>
#endif
namespace
{
class NaturalCompare
{
public:
explicit NaturalCompare(const bool caseSensitive = true)
: m_caseSensitive(caseSensitive)
{
#ifdef QBT_USES_QT5
#if defined(Q_OS_WIN)
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
#endif
}
bool operator()(const QString &left, const QString &right) const
{
#ifdef QBT_USES_QT5
#if defined(Q_OS_WIN)
// Without ICU library, QCollator uses the native API on Windows 7+. But that API
// sorts older versions of μTorrent differently than the newer ones because the
// 'μ' character is encoded differently and the native API can't cope with that.
// So default to using our custom natural sorting algorithm instead.
// See #5238 and #5240
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
// if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(left, right);
#endif
return (m_collator.compare(left, right) < 0);
#else
return lessThan(left, right);
#endif
}
bool lessThan(const QString &left, const QString &right) const
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if ((posL == left.size()) || (posR == right.size()))
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = m_caseSensitive ? left[posL] : left[posL].toLower();
QChar rightChar = m_caseSensitive ? right[posR] : right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#ifdef QBT_USES_QT5
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#ifdef QBT_USES_QT5
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
}
private:
#ifdef QBT_USES_QT5
QCollator m_collator;
#endif
const bool m_caseSensitive;
};
}
bool Utils::String::naturalCompareCaseSensitive(const QString &left, const QString &right)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(true));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(true);
return nCmp(left, right);
#endif
}
bool Utils::String::naturalCompareCaseInsensitive(const QString &left, const QString &right)
{
// provide a single `NaturalCompare` instance for easy use
// https://doc.qt.io/qt-5/threads-reentrancy.html
#ifdef Q_OS_MAC // workaround for Apple xcode: https://stackoverflow.com/a/29929949
static QThreadStorage<NaturalCompare> nCmp;
if (!nCmp.hasLocalData()) nCmp.setLocalData(NaturalCompare(false));
return (nCmp.localData())(left, right);
#else
thread_local NaturalCompare nCmp(false);
return nCmp(left, right);
#endif
}
QString Utils::String::fromStdString(const std::string &str)
{
return QString::fromUtf8(str.c_str());
@@ -44,145 +181,6 @@ std::string Utils::String::toStdString(const QString &str)
return std::string(utf8.constData(), utf8.length());
}
// uses lessThan comparison
bool Utils::String::naturalSort(const QString &left, const QString &right, bool &result)
{
// Return value indicates if functions was successful
// result argument will contain actual comparison result if function was successful
int posL = 0;
int posR = 0;
do {
forever {
if (posL == left.size() || posR == right.size())
return false; // No data
QChar leftChar = left.at(posL);
QChar rightChar = right.at(posR);
bool leftCharIsDigit = leftChar.isDigit();
bool rightCharIsDigit = rightChar.isDigit();
if (leftCharIsDigit != rightCharIsDigit)
return false; // Digit positions mismatch
if (leftCharIsDigit)
break; // Both are digit, break this loop and compare numbers
if (leftChar != rightChar)
return false; // Strings' subsets before digit do not match
++posL;
++posR;
}
QString temp;
while (posL < left.size()) {
if (left.at(posL).isDigit())
temp += left.at(posL);
else
break;
posL++;
}
int numL = temp.toInt();
temp.clear();
while (posR < right.size()) {
if (right.at(posR).isDigit())
temp += right.at(posR);
else
break;
posR++;
}
int numR = temp.toInt();
if (numL != numR) {
result = (numL < numR);
return true;
}
// Strings + digits do match and we haven't hit string end
// Do another round
} while (true);
return false;
}
Utils::String::NaturalCompare::NaturalCompare()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
#endif
}
bool Utils::String::NaturalCompare::operator()(const QString &l, const QString &r)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(l, r);
#endif
return (m_collator.compare(l, r) < 0);
#else
return lessThan(l, r);
#endif
}
bool Utils::String::NaturalCompare::lessThan(const QString &left, const QString &right)
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if (posL == left.size() || posR == right.size())
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = left[posL].toLower();
QChar rightChar = right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
return false;
}
// to send numbers instead of strings with suffixes
QString Utils::String::fromDouble(double n, int precision)
{

View File

@@ -31,13 +31,9 @@
#define UTILS_STRING_H
#include <string>
#include <QtGlobal>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#include <QCollator>
#endif
class QString;
class QByteArray;
class QString;
namespace Utils
{
@@ -51,19 +47,8 @@ namespace Utils
// Taken from https://crackstation.net/hashing-security.htm
bool slowEquals(const QByteArray &a, const QByteArray &b);
bool naturalSort(const QString &left, const QString &right, bool &result);
class NaturalCompare
{
public:
NaturalCompare();
bool operator()(const QString &l, const QString &r);
bool lessThan(const QString &left, const QString &right);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
private:
QCollator m_collator;
#endif
};
bool naturalCompareCaseSensitive(const QString &left, const QString &right);
bool naturalCompareCaseInsensitive(const QString &left, const QString &right);
}
}

View File

@@ -8,6 +8,7 @@ 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)
@@ -31,6 +32,8 @@ addnewtorrentdialog.h
advancedsettings.h
advancedsettings.h
autoexpandabledialog.h
cookiesdialog.h
cookiesmodel.h
deletionconfirmationdlg.h
downloadfromurldlg.h
executionlog.h
@@ -40,11 +43,11 @@ ico.h
loglistwidget.h
mainwindow.h
messageboxraised.h
options_imp.h
optionsdlg.h
previewlistdelegate.h
previewselect.h
scanfoldersdelegate.h
shutdownconfirm.h
shutdownconfirmdlg.h
speedlimitdlg.h
statsdialog.h
statusbar.h
@@ -55,7 +58,6 @@ torrentcontentmodelfolder.h
torrentcontentmodelitem.h
torrentcontenttreeview.h
torrentcreatordlg.h
torrentimportdlg.h
torrentmodel.h
trackerlogin.h
transferlistdelegate.h
@@ -69,16 +71,18 @@ set(QBT_GUI_SOURCES
addnewtorrentdialog.cpp
advancedsettings.cpp
autoexpandabledialog.cpp
cookiesdialog.cpp
cookiesmodel.cpp
executionlog.cpp
guiiconprovider.cpp
ico.cpp
loglistwidget.cpp
mainwindow.cpp
messageboxraised.cpp
options_imp.cpp
optionsdlg.cpp
previewselect.cpp
scanfoldersdelegate.cpp
shutdownconfirm.cpp
shutdownconfirmdlg.cpp
speedlimitdlg.cpp
statsdialog.cpp
statusbar.cpp
@@ -89,7 +93,6 @@ torrentcontentmodelfolder.cpp
torrentcontentmodelitem.cpp
torrentcontenttreeview.cpp
torrentcreatordlg.cpp
torrentimportdlg.cpp
torrentmodel.cpp
trackerlogin.cpp
transferlistdelegate.cpp
@@ -107,22 +110,26 @@ endif (WIN32 OR APPLE)
set(QBT_GUI_FORMS
mainwindow.ui
about.ui
cookiesdialog.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
optionsdlg.ui
torrentcreatordlg.ui
shutdownconfirmdlg.ui
)
set(QBT_GUI_RESOURCES about.qrc)
qbt_target_sources(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)
add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES})
target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine
${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base
QtSingleApplication::QtSingleApplication
)

View File

@@ -71,11 +71,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>

View File

@@ -35,7 +35,7 @@
#include <QMenu>
#include <QFileDialog>
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "base/bittorrent/session.h"
@@ -44,16 +44,35 @@
#include "base/bittorrent/torrenthandle.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/torrentfileguard.h"
#include "base/unicodestrings.h"
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
#include "messageboxraised.h"
#include "ui_addnewtorrentdialog.h"
#include "proplistdelegate.h"
#include "torrentcontentmodel.h"
#include "torrentcontentfiltermodel.h"
#include "ui_addnewtorrentdialog.h"
#include "addnewtorrentdialog.h"
#define SETTINGS_KEY(name) "AddNewTorrentDialog/" name
const QString KEY_ENABLED = SETTINGS_KEY("Enabled");
const QString KEY_DEFAULTSAVEPATH = SETTINGS_KEY("DefaultSavePath");
const QString KEY_DEFAULTCATEGORY = SETTINGS_KEY("DefaultCategory");
const QString KEY_TREEHEADERSTATE = SETTINGS_KEY("TreeHeaderState");
const QString KEY_WIDTH = SETTINGS_KEY("Width");
const QString KEY_EXPANDED = SETTINGS_KEY("Expanded");
const QString KEY_POSITION = SETTINGS_KEY("Position");
const QString KEY_TOPLEVEL = SETTINGS_KEY("TopLevel");
const QString KEY_SAVEPATHHISTORY = SETTINGS_KEY("SavePathHistory");
namespace
{
//just a shortcut
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
}
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::AddNewTorrentDialog)
@@ -67,34 +86,41 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
ui->lblMetaLoading->setVisible(false);
ui->progMetaLoading->setVisible(false);
Preferences* const pref = Preferences::instance();
ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause());
ui->save_path_combo->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath());
loadSavePathHistory();
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked()));
ui->default_save_path_cb->setVisible(false); // Default path is selected by default
auto session = BitTorrent::Session::instance();
// Load labels
const QStringList customLabels = pref->getTorrentLabels();
const QString defaultLabel = pref->getDefaultLabel();
ui->startTorrentCheckBox->setChecked(!session->isAddTorrentPaused());
ui->comboTTM->blockSignals(true); //the TreeView size isn't correct if the slot does it job at this point
ui->comboTTM->setCurrentIndex(!session->isAutoTMMDisabledByDefault());
ui->comboTTM->blockSignals(false);
populateSavePathComboBox();
connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
connect(ui->browseButton, SIGNAL(clicked()), SLOT(browseButton_clicked()));
ui->defaultSavePathCheckBox->setVisible(false); // Default path is selected by default
if (!defaultLabel.isEmpty())
ui->label_combo->addItem(defaultLabel);
ui->label_combo->addItem("");
ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
foreach (const QString& label, customLabels)
if (label != defaultLabel)
ui->label_combo->addItem(label);
// Load categories
QStringList categories = session->categories();
std::sort(categories.begin(), categories.end(), Utils::String::naturalCompareCaseInsensitive);
QString defaultCategory = settings()->loadValue(KEY_DEFAULTCATEGORY).toString();
ui->label_combo->model()->sort(0);
ui->content_tree->header()->setSortIndicator(0, Qt::AscendingOrder);
if (!defaultCategory.isEmpty())
ui->categoryComboBox->addItem(defaultCategory);
ui->categoryComboBox->addItem("");
foreach (const QString &category, categories)
if (category != defaultCategory)
ui->categoryComboBox->addItem(category);
ui->categoryComboBox->model()->sort(0);
ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder);
loadState();
// Signal / slots
connect(ui->adv_button, SIGNAL(clicked(bool)), SLOT(showAdvancedSettings(bool)));
editHotkey = new QShortcut(QKeySequence("F2"), ui->content_tree, 0, 0, Qt::WidgetShortcut);
connect(ui->doNotDeleteTorrentCheckBox, SIGNAL(clicked(bool)), SLOT(doNotDeleteTorrentClicked(bool)));
editHotkey = new QShortcut(QKeySequence("F2"), ui->contentTreeView, 0, 0, Qt::WidgetShortcut);
connect(editHotkey, SIGNAL(activated()), SLOT(renameSelectedFile()));
connect(ui->content_tree, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile()));
connect(ui->contentTreeView, SIGNAL(doubleClicked(QModelIndex)), SLOT(renameSelectedFile()));
ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
}
@@ -108,27 +134,45 @@ AddNewTorrentDialog::~AddNewTorrentDialog()
delete editHotkey;
}
bool AddNewTorrentDialog::isEnabled()
{
return SettingsStorage::instance()->loadValue(KEY_ENABLED, true).toBool();
}
void AddNewTorrentDialog::setEnabled(bool value)
{
SettingsStorage::instance()->storeValue(KEY_ENABLED, value);
}
bool AddNewTorrentDialog::isTopLevel()
{
return SettingsStorage::instance()->loadValue(KEY_TOPLEVEL, true).toBool();
}
void AddNewTorrentDialog::setTopLevel(bool value)
{
SettingsStorage::instance()->storeValue(KEY_TOPLEVEL, value);
}
void AddNewTorrentDialog::loadState()
{
const Preferences* const pref = Preferences::instance();
m_headerState = pref->getAddNewTorrentDialogState();
int width = pref->getAddNewTorrentDialogWidth();
m_headerState = settings()->loadValue(KEY_TREEHEADERSTATE).toByteArray();
int width = settings()->loadValue(KEY_WIDTH, -1).toInt();
if (width >= 0) {
QRect geo = geometry();
geo.setWidth(width);
setGeometry(geo);
}
ui->adv_button->setChecked(pref->getAddNewTorrentDialogExpanded());
ui->adv_button->setChecked(settings()->loadValue(KEY_EXPANDED).toBool());
}
void AddNewTorrentDialog::saveState()
{
Preferences* const pref = Preferences::instance();
if (m_contentModel)
pref->setAddNewTorrentDialogState(ui->content_tree->header()->saveState());
pref->setAddNewTorrentDialogPos(pos().y());
pref->setAddNewTorrentDialogWidth(width());
pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked());
settings()->storeValue(KEY_TREEHEADERSTATE, ui->contentTreeView->header()->saveState());
settings()->storeValue(KEY_POSITION, pos().y());
settings()->storeValue(KEY_WIDTH, width());
settings()->storeValue(KEY_EXPANDED, ui->adv_button->isChecked());
}
void AddNewTorrentDialog::show(QString source, QWidget *parent)
@@ -165,13 +209,13 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
m_filePath = torrentPath;
if (!QFile::exists(m_filePath)) {
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file '%1' does not exist.").arg(Utils::Fs::toNativePath(m_filePath)));
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."));
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file '%1' cannot be read from the disk. Probably you don't have enough permissions.").arg(Utils::Fs::toNativePath(m_filePath)));
return false;
}
@@ -179,10 +223,11 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
QString error;
m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(m_filePath, error);
if (!m_torrentInfo.isValid()) {
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(error));
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1.\nError: %2", "Don't remove the '\n' characters. They insert a newline.").arg(Utils::Fs::toNativePath(m_filePath)).arg(error));
return false;
}
m_torrentGuard.reset(new TorrentFileGuard(m_filePath));
m_hash = m_torrentInfo.hash();
// Prevent showing the dialog if download is already present
@@ -206,6 +251,7 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
ui->lblhash->setText(m_hash);
setupTreeview();
TMMChanged(ui->comboTTM->currentIndex());
return true;
}
@@ -216,6 +262,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
return false;
}
m_torrentGuard.reset(new TorrentFileGuard(QString()));
m_hash = magnetUri.hash();
// Prevent showing the dialog if download is already present
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
@@ -243,6 +290,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
setupTreeview();
TMMChanged(ui->comboTTM->currentIndex());
// Set dialog position
setdialogPosition();
@@ -256,9 +304,8 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
void AddNewTorrentDialog::showEvent(QShowEvent *event)
{
QDialog::showEvent(event);
Preferences* const pref = Preferences::instance();
if (!pref->additionDialogFront())
return;
if (!isTopLevel()) return;
activateWindow();
raise();
}
@@ -272,12 +319,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show)
ui->adv_button->setText(QString::fromUtf8(C_UP));
ui->settings_group->setVisible(true);
ui->infoGroup->setVisible(true);
if (m_hasMetadata && (m_torrentInfo.filesCount() > 1)) {
ui->content_tree->setVisible(true);
}
else {
ui->content_tree->setVisible(false);
}
ui->contentTreeView->setVisible(m_hasMetadata);
static_cast<QVBoxLayout*>(layout())->insertWidget(layout()->indexOf(ui->never_show_cb) + 1, ui->adv_button);
}
else {
@@ -292,21 +334,20 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show)
void AddNewTorrentDialog::saveSavePathHistory() const
{
QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
Preferences* const pref = Preferences::instance();
QDir selectedSavePath(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString());
// Get current history
QStringList history = pref->getAddNewTorrentDialogPathHistory();
QList<QDir> history_dirs;
QStringList history = settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList();
QList<QDir> historyDirs;
foreach(const QString dir, history)
history_dirs << QDir(dir);
if (!history_dirs.contains(selected_save_path)) {
historyDirs << QDir(dir);
if (!historyDirs.contains(selectedSavePath)) {
// Add save path to history
history.push_front(selected_save_path.absolutePath());
history.push_front(selectedSavePath.absolutePath());
// Limit list size
if (history.size() > 8)
history.pop_back();
// Save history
pref->setAddNewTorrentDialogPathHistory(history);
settings()->storeValue(KEY_SAVEPATHHISTORY, history);
}
}
@@ -314,20 +355,12 @@ void AddNewTorrentDialog::saveSavePathHistory() const
int AddNewTorrentDialog::indexOfSavePath(const QString &save_path)
{
QDir saveDir(save_path);
for(int i = 0; i < ui->save_path_combo->count(); ++i)
if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir)
for (int i = 0; i < ui->savePathComboBox->count(); ++i)
if (QDir(ui->savePathComboBox->itemData(i).toString()) == saveDir)
return i;
return -1;
}
void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename)
{
for(int i = 0; i < ui->save_path_combo->count(); ++i) {
const QDir folder(ui->save_path_combo->itemData(i).toString());
ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(folder.absoluteFilePath(new_filename)));
}
}
void AddNewTorrentDialog::updateDiskSpaceLabel()
{
// Determine torrent size
@@ -349,8 +382,8 @@ 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 space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
ui->save_path_combo->itemData(
ui->save_path_combo->currentIndex()).toString())));
ui->savePathComboBox->itemData(
ui->savePathComboBox->currentIndex()).toString())));
size_string += ")";
ui->size_lbl->setText(size_string);
}
@@ -358,8 +391,10 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
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()));
ui->defaultSavePathCheckBox->setChecked(false);
ui->defaultSavePathCheckBox->setVisible(
QDir(ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString())
!= QDir(defaultSavePath()));
// Remember index
m_oldIndex = index;
@@ -367,58 +402,54 @@ void AddNewTorrentDialog::onSavePathChanged(int index)
updateDiskSpaceLabel();
}
void AddNewTorrentDialog::categoryChanged(int index)
{
Q_UNUSED(index);
if (ui->comboTTM->currentIndex() == 1) {
QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText());
ui->savePathComboBox->setItemText(0, Utils::Fs::toNativePath(savePath));
ui->savePathComboBox->setItemData(0, savePath);
}
}
void AddNewTorrentDialog::browseButton_clicked()
{
disconnect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int)));
// User is asking for a new save path
QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex);
QString new_path, old_filename, new_filename;
disconnect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onSavePathChanged(int)));
if (m_torrentInfo.isValid() && (m_torrentInfo.filesCount() == 1)) {
old_filename = Utils::Fs::fileName(cur_save_path);
new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite);
if (!new_path.isEmpty())
new_path = Utils::Fs::branchPath(new_path, &new_filename);
qDebug() << "new_path: " << new_path;
qDebug() << "new_filename: " << new_filename;
}
else {
if (!cur_save_path.isEmpty() && QDir(cur_save_path).exists())
new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), cur_save_path);
else
new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
}
if (!new_path.isEmpty()) {
const int existing_index = indexOfSavePath(new_path);
if (existing_index >= 0) {
ui->save_path_combo->setCurrentIndex(existing_index);
// User is asking for a new save path
QString curSavePath = ui->savePathComboBox->itemText(m_oldIndex);
QString newPath;
if (!curSavePath.isEmpty() && QDir(curSavePath).exists())
newPath = QFileDialog::getExistingDirectory(this, tr("Choose save path"), curSavePath);
else
newPath = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
if (!newPath.isEmpty()) {
const int existingIndex = indexOfSavePath(newPath);
if (existingIndex >= 0) {
ui->savePathComboBox->setCurrentIndex(existingIndex);
}
else {
// New path, prepend to combo box
if (!new_filename.isEmpty())
ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path);
else
ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(new_path), new_path);
ui->save_path_combo->setCurrentIndex(0);
}
// Update file name in all save_paths
if (!new_filename.isEmpty() && !Utils::Fs::sameFileNames(old_filename, new_filename)) {
m_torrentInfo.renameFile(0, new_filename);
updateFileNameInSavePaths(new_filename);
ui->savePathComboBox->insertItem(0, Utils::Fs::toNativePath(newPath), newPath);
ui->savePathComboBox->setCurrentIndex(0);
}
onSavePathChanged(0);
}
else {
// Restore index
ui->save_path_combo->setCurrentIndex(m_oldIndex);
ui->savePathComboBox->setCurrentIndex(m_oldIndex);
}
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
}
void AddNewTorrentDialog::renameSelectedFile()
{
const QModelIndexList selectedIndexes = ui->content_tree->selectionModel()->selectedRows(0);
const QModelIndexList selectedIndexes = ui->contentTreeView->selectionModel()->selectedRows(0);
if (selectedIndexes.size() != 1)
return;
const QModelIndex &index = selectedIndexes.first();
@@ -522,7 +553,7 @@ void AddNewTorrentDialog::setdialogPosition()
qApp->processEvents();
QPoint center(Utils::Misc::screenCenter(this));
// Adjust y
int y = Preferences::instance()->getAddNewTorrentDialogPos();
int y = settings()->loadValue(KEY_POSITION, -1).toInt();
if (y >= 0) {
center.setY(y);
}
@@ -534,22 +565,25 @@ void AddNewTorrentDialog::setdialogPosition()
move(center);
}
void AddNewTorrentDialog::loadSavePathHistory()
void AddNewTorrentDialog::populateSavePathComboBox()
{
QDir default_save_path(Preferences::instance()->getSavePath());
QString defSavePath = defaultSavePath();
ui->savePathComboBox->clear();
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(defSavePath), defSavePath);
QDir defaultSaveDir(defSavePath);
// Load save path history
QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory();
foreach (const QString &sp, raw_path_history)
if (QDir(sp) != default_save_path)
ui->save_path_combo->addItem(Utils::Fs::toNativePath(sp), sp);
foreach (const QString &savePath, settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList())
if (QDir(savePath) != defaultSaveDir)
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath);
}
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
{
QMenu myFilesLlistMenu;
const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0);
const QModelIndexList selectedRows = ui->contentTreeView->selectionModel()->selectedRows(0);
QAction *actRename = 0;
if ((selectedRows.size() == 1) && (m_torrentInfo.filesCount() > 1)) {
if (selectedRows.size() == 1) {
actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
myFilesLlistMenu.addSeparator();
}
@@ -589,38 +623,33 @@ void AddNewTorrentDialog::accept()
if (!m_hasMetadata)
disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo &)));
Preferences *const pref = Preferences::instance();
BitTorrent::AddTorrentParams params;
if (ui->skip_check_cb->isChecked())
// TODO: Check if destination actually exists
params.skipChecking = true;
// Label
params.label = ui->label_combo->currentText();
// Category
params.category = ui->categoryComboBox->currentText();
if (ui->defaultLabel->isChecked())
pref->setDefaultLabel(params.label);
if (ui->defaultCategoryCheckbox->isChecked())
settings()->storeValue(KEY_DEFAULTCATEGORY, params.category);
// Save file priorities
if (m_contentModel)
params.filePriorities = m_contentModel->model()->getFilePriorities();
params.addPaused = !ui->start_torrent_cb->isChecked();
params.addPaused = !ui->startTorrentCheckBox->isChecked();
saveSavePathHistory();
pref->useAdditionDialog(!ui->never_show_cb->isChecked());
QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString();
if (ui->comboTTM->currentIndex() != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
params.savePath = savePath;
saveSavePathHistory();
if (ui->defaultSavePathCheckBox->isChecked())
settings()->storeValue(KEY_DEFAULTSAVEPATH, savePath);
}
QString savePath = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString();
if (ui->default_save_path_cb->isChecked()) {
pref->setSavePath(savePath);
pref->apply();
}
else {
// if we don't use default save path...
if (QDir(savePath) != QDir(pref->getSavePath()))
params.savePath = savePath;
}
setEnabled(!ui->never_show_cb->isChecked());
// Add torrent
if (!m_hasMetadata)
@@ -628,6 +657,7 @@ void AddNewTorrentDialog::accept()
else
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params);
m_torrentGuard->markAsAddedToSession();
QDialog::accept();
}
@@ -660,6 +690,7 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &info)
// Update UI
setupTreeview();
TMMChanged(ui->comboTTM->currentIndex());
setMetadataProgressIndicator(false, tr("Metadata retrieval complete"));
}
@@ -686,41 +717,40 @@ void AddNewTorrentDialog::setupTreeview()
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);
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 &)));
connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &)));
m_contentModel = new TorrentContentFilterModel(this);
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
ui->contentTreeView->setModel(m_contentModel);
m_contentDelegate = new PropListDelegate();
ui->contentTreeView->setItemDelegate(m_contentDelegate);
connect(ui->contentTreeView, SIGNAL(clicked(const QModelIndex &)), ui->contentTreeView, SLOT(edit(const QModelIndex &)));
connect(ui->contentTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &)));
// List files in torrent
m_contentModel->model()->setupModelData(m_torrentInfo);
if (!m_headerState.isEmpty())
ui->content_tree->header()->restoreState(m_headerState);
// List files in torrent
m_contentModel->model()->setupModelData(m_torrentInfo);
if (!m_headerState.isEmpty())
ui->contentTreeView->header()->restoreState(m_headerState);
// Hide useless columns after loading the header state
ui->content_tree->hideColumn(PROGRESS);
ui->content_tree->hideColumn(REMAINING);
ui->contentTreeView->hideColumn(PROGRESS);
ui->contentTreeView->hideColumn(REMAINING);
// Expand root folder
ui->content_tree->setExpanded(m_contentModel->index(0, 0), true);
}
else {
// Update save paths (append file name to them)
QString single_file_relpath = m_torrentInfo.filePath(0);
for (int i = 0; i < ui->save_path_combo->count(); ++i)
ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath)));
}
// Expand root folder
ui->contentTreeView->setExpanded(m_contentModel->index(0, 0), true);
}
updateDiskSpaceLabel();
showAdvancedSettings(Preferences::instance()->getAddNewTorrentDialogExpanded());
showAdvancedSettings(settings()->loadValue(KEY_EXPANDED, false).toBool());
// Set dialog position
setdialogPosition();
}
QString AddNewTorrentDialog::defaultSavePath() const
{
return Utils::Fs::fromNativePath(
settings()->loadValue(KEY_DEFAULTSAVEPATH,
BitTorrent::Session::instance()->defaultSavePath()).toString());
}
void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason)
{
MessageBoxRaised::critical(0, tr("Download Error"), QString("Cannot download '%1': %2").arg(url).arg(reason));
@@ -745,6 +775,28 @@ void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QStri
this->deleteLater();
}
void AddNewTorrentDialog::TMMChanged(int index)
{
if (index != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
populateSavePathComboBox();
ui->groupBoxSavePath->setEnabled(true);
ui->savePathComboBox->blockSignals(false);
ui->savePathComboBox->setCurrentIndex(m_oldIndex < ui->savePathComboBox->count() ? m_oldIndex : ui->savePathComboBox->count() - 1);
ui->adv_button->setEnabled(true);
}
else {
ui->groupBoxSavePath->setEnabled(false);
ui->savePathComboBox->blockSignals(true);
ui->savePathComboBox->clear();
QString savePath = BitTorrent::Session::instance()->categorySavePath(ui->categoryComboBox->currentText());
ui->savePathComboBox->addItem(Utils::Fs::toNativePath(savePath), savePath);
ui->defaultSavePathCheckBox->setVisible(false);
ui->adv_button->setChecked(true);
ui->adv_button->setEnabled(false);
showAdvancedSettings(true);
}
}
void AddNewTorrentDialog::setCommentText(const QString &str) const
{
ui->commentLabel->setText(str);
@@ -755,3 +807,8 @@ void AddNewTorrentDialog::setCommentText(const QString &str) const
int height = lineHeight * lines;
ui->scrollArea->setMaximumHeight(height);
}
void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)
{
m_torrentGuard->setAutoRemove(!checked);
}

View File

@@ -31,8 +31,9 @@
#ifndef ADDNEWTORRENTDIALOG_H
#define ADDNEWTORRENTDIALOG_H
#include <QShortcut>
#include <QDialog>
#include <QScopedPointer>
#include <QShortcut>
#include <QUrl>
#include "base/bittorrent/infohash.h"
@@ -49,6 +50,7 @@ namespace Ui
}
class TorrentContentFilterModel;
class TorrentFileGuard;
class PropListDelegate;
class AddNewTorrentDialog: public QDialog
@@ -58,10 +60,12 @@ class AddNewTorrentDialog: public QDialog
public:
~AddNewTorrentDialog();
static void show(QString source, QWidget *parent = 0);
static bool isEnabled();
static void setEnabled(bool value);
static bool isTopLevel();
static void setTopLevel(bool value);
protected:
void showEvent(QShowEvent *event);
static void show(QString source, QWidget *parent = 0);
private slots:
void showAdvancedSettings(bool show);
@@ -75,26 +79,29 @@ private slots:
void handleDownloadFailed(const QString &url, const QString &reason);
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
void handleDownloadFinished(const QString &url, const QString &filePath);
void TMMChanged(int index);
void categoryChanged(int index);
void doNotDeleteTorrentClicked(bool checked);
protected slots:
virtual void accept();
virtual void reject();
void accept() override;
void reject() override;
private:
explicit AddNewTorrentDialog(QWidget *parent = 0);
bool loadTorrent(const QString &torrentPath);
bool loadMagnet(const BitTorrent::MagnetUri &magnetUri);
void loadSavePathHistory();
void populateSavePathComboBox();
void saveSavePathHistory() const;
int indexOfSavePath(const QString& save_path);
void updateFileNameInSavePaths(const QString& new_filename);
void loadState();
void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
void setupTreeview();
QString defaultSavePath() const;
void setCommentText(const QString &str) const;
private:
void showEvent(QShowEvent *event) override;
Ui::AddNewTorrentDialog *ui;
TorrentContentFilterModel *m_contentModel;
PropListDelegate *m_contentDelegate;
@@ -105,6 +112,7 @@ private:
QShortcut *editHotkey;
QByteArray m_headerState;
int m_oldIndex;
QScopedPointer<TorrentFileGuard> m_torrentGuard;
};
#endif // ADDNEWTORRENTDIALOG_H

View File

@@ -7,12 +7,53 @@
<x>0</x>
<y>0</y>
<width>414</width>
<height>590</height>
<height>661</height>
</rect>
</property>
<layout class="QVBoxLayout" name="AddNewTorrentDialogLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelTorrentManagementMode">
<property name="text">
<string>Torrent Management Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboTTM">
<property name="toolTip">
<string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string>
</property>
<item>
<property name="text">
<string>Manual</string>
</property>
</item>
<item>
<property name="text">
<string>Automatic</string>
</property>
</item>
</widget>
</item>
<item>
<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>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSavePath">
<property name="title">
<string>Save at</string>
</property>
@@ -20,7 +61,7 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="save_path_combo">
<widget class="QComboBox" name="savePathComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -33,7 +74,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_button">
<widget class="QPushButton" name="browseButton">
<property name="text">
<string>Browse...</string>
</property>
@@ -42,7 +83,7 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="default_save_path_cb">
<widget class="QCheckBox" name="defaultSavePathCheckBox">
<property name="text">
<string>Set as default save path</string>
</property>
@@ -51,6 +92,16 @@
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted despite the settings at the &quot;Download&quot; page of the options dialog</string>
</property>
<property name="text">
<string>Do not delete .torrent file</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="never_show_cb">
<property name="text">
@@ -75,34 +126,28 @@
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="2">
<widget class="QCheckBox" name="defaultLabel">
<widget class="QCheckBox" name="defaultCategoryCheckbox">
<property name="text">
<string>Set as default label</string>
<string>Set as default category</string>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<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>Set label:</string>
<string>Category:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="label_combo">
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
<widget class="QComboBox" name="categoryComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
@@ -115,7 +160,7 @@
</layout>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="start_torrent_cb">
<widget class="QCheckBox" name="startTorrentCheckBox">
<property name="text">
<string>Start torrent</string>
</property>
@@ -131,27 +176,17 @@
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<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">
<spacer name="horizontalSpacer2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<width>35</width>
<height>0</height>
</size>
</property>
@@ -165,110 +200,29 @@
<property name="title">
<string>Torrent information</string>
</property>
<layout class="QVBoxLayout" name="infoGroupLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="size_lbl"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="date_lbl"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<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>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QLabel" name="lblhash">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="TorrentContentTreeView" name="content_tree">
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@@ -280,6 +234,100 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="date_lbl">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="size_lbl">
<property name="text">
<string/>
</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>308</width>
<height>74</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="text">
<string/>
</property>
<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>
</widget>
</item>
@@ -348,6 +396,19 @@
<header>torrentcontenttreeview.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>savePathComboBox</tabstop>
<tabstop>browseButton</tabstop>
<tabstop>defaultSavePathCheckBox</tabstop>
<tabstop>never_show_cb</tabstop>
<tabstop>adv_button</tabstop>
<tabstop>startTorrentCheckBox</tabstop>
<tabstop>skip_check_cb</tabstop>
<tabstop>categoryComboBox</tabstop>
<tabstop>defaultCategoryCheckbox</tabstop>
<tabstop>scrollArea</tabstop>
<tabstop>contentTreeView</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
@@ -357,8 +418,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
<x>403</x>
<y>579</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -373,8 +434,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
<x>403</x>
<y>579</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -382,5 +443,42 @@
</hint>
</hints>
</connection>
<connection>
<sender>categoryComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>categoryChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>337</x>
<y>205</y>
</hint>
<hint type="destinationlabel">
<x>403</x>
<y>160</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboTTM</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>TMMChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>200</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
<y>294</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>savingModeChanged(bool)</slot>
<slot>categoryChanged(int)</slot>
<slot>TMMChanged(int)</slot>
</slots>
</ui>

View File

@@ -31,7 +31,9 @@
#include <QHeaderView>
#include <QHostAddress>
#include <QNetworkInterface>
#include "app/application.h"
#include "base/preferences.h"
#include "gui/mainwindow.h"
enum AdvSettingsCols
{
@@ -45,6 +47,8 @@ enum AdvSettingsRows
QBITTORRENT_HEADER,
// network interface
NETWORK_IFACE,
//Optional network address
NETWORK_IFACE_ADDRESS,
NETWORK_LISTEN_IPV6,
// behavior
SAVE_RESUME_DATA_INTERVAL,
@@ -58,6 +62,7 @@ enum AdvSettingsRows
RESOLVE_HOSTS,
RESOLVE_COUNTRIES,
PROGRAM_NOTIFICATIONS,
TORRENT_ADDED_NOTIFICATIONS,
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
USE_ICON_THEME,
#endif
@@ -101,6 +106,7 @@ AdvancedSettings::AdvancedSettings(QWidget *parent)
setEditTriggers(QAbstractItemView::NoEditTriggers);
// Signals
connect(&spin_cache, SIGNAL(valueChanged(int)), SLOT(updateCacheSpinSuffix(int)));
connect(&combo_iface, SIGNAL(currentIndexChanged(int)), SLOT(updateInterfaceAddressCombo()));
// Load settings
loadAdvancedSettings();
resizeColumnToContents(0);
@@ -143,14 +149,26 @@ void AdvancedSettings::saveAdvancedSettings()
}
// Listen on IPv6 address
pref->setListenIPv6(cb_listen_ipv6.isChecked());
// Network address
QHostAddress addr(txt_network_address.text().trimmed());
if (addr.isNull())
// Interface address
if (combo_iface_address.currentIndex() == 0) {
// All addresses (default)
pref->setNetworkInterfaceAddress(QString::null);
}
else {
QHostAddress ifaceAddr(combo_iface_address.currentText().trimmed());
ifaceAddr.isNull() ? pref->setNetworkInterfaceAddress(QString::null) : pref->setNetworkInterfaceAddress(ifaceAddr.toString());
}
// Network Announce address
QHostAddress networkAddr(txt_network_address.text().trimmed());
if (networkAddr.isNull())
pref->setNetworkAddress("");
else
pref->setNetworkAddress(addr.toString());
pref->setNetworkAddress(networkAddr.toString());
// Program notification
pref->useProgramNotification(cb_program_notifications.isChecked());
MainWindow * const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
mainWindow->setNotificationsEnabled(cb_program_notifications.isChecked());
mainWindow->setTorrentAddedNotificationsEnabled(cb_torrent_added_notifications.isChecked());
// Tracker
pref->setTrackerEnabled(cb_tracker_status.isChecked());
pref->setTrackerPort(spin_tracker_port.value());
@@ -175,6 +193,43 @@ void AdvancedSettings::updateCacheSpinSuffix(int value)
spin_cache.setSuffix(tr(" MiB"));
}
void AdvancedSettings::updateInterfaceAddressCombo()
{
// Try to get the currently selected interface name
const QString ifaceName = combo_iface.itemData(combo_iface.currentIndex()).toString(); // Empty string for the first element
const QString currentAddress = Preferences::instance()->getNetworkInterfaceAddress();
//Clear all items and reinsert them, default to all
combo_iface_address.clear();
combo_iface_address.addItem(tr("All addresses"));
combo_iface_address.setCurrentIndex(0);
auto populateCombo = [this, &currentAddress](const QString &ip, const QAbstractSocket::NetworkLayerProtocol &protocol)
{
Q_ASSERT(protocol == QAbstractSocket::IPv4Protocol || protocol == QAbstractSocket::IPv6Protocol);
//Only take ipv4 for now?
if (protocol != QAbstractSocket::IPv4Protocol && protocol != QAbstractSocket::IPv6Protocol)
return;
combo_iface_address.addItem(ip);
//Try to select the last added one
if (ip == currentAddress)
combo_iface_address.setCurrentIndex(combo_iface_address.count() - 1);
};
if (ifaceName.isEmpty()) {
foreach (const QHostAddress &ip, QNetworkInterface::allAddresses())
populateCombo(ip.toString(), ip.protocol());
}
else {
const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
foreach (const QNetworkAddressEntry &entry, addresses) {
const QHostAddress ip = entry.ip();
populateCombo(ip.toString(), ip.protocol());
}
}
}
void AdvancedSettings::loadAdvancedSettings()
{
const Preferences* const pref = Preferences::instance();
@@ -257,6 +312,12 @@ void AdvancedSettings::loadAdvancedSettings()
bool interface_exists = current_iface.isEmpty();
int i = 1;
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
// This line fixes a Qt bug => https://bugreports.qt.io/browse/QTBUG-52633
// Tested in Qt 5.6.0. For more info see:
// https://github.com/qbittorrent/qBittorrent/issues/5131
// https://github.com/qbittorrent/qBittorrent/pull/5135
if (iface.addressEntries().isEmpty()) continue;
if (iface.flags() & QNetworkInterface::IsLoopBack) continue;
combo_iface.addItem(iface.humanReadableName(), iface.name());
if (!current_iface.isEmpty() && (iface.name() == current_iface)) {
@@ -271,15 +332,23 @@ void AdvancedSettings::loadAdvancedSettings()
combo_iface.setCurrentIndex(i);
}
addRow(NETWORK_IFACE, tr("Network Interface (requires restart)"), &combo_iface);
// Network interface address
updateInterfaceAddressCombo();
addRow(NETWORK_IFACE_ADDRESS, tr("Optional IP Address to bind to (requires restart)"), &combo_iface_address);
// 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
// Announce 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);
const MainWindow * const mainWindow = static_cast<Application*>(QCoreApplication::instance())->mainWindow();
cb_program_notifications.setChecked(mainWindow->isNotificationsEnabled());
addRow(PROGRAM_NOTIFICATIONS, tr("Display notifications"), &cb_program_notifications);
// Torrent added notifications
cb_torrent_added_notifications.setChecked(mainWindow->isTorrentAddedNotificationsEnabled());
addRow(TORRENT_ADDED_NOTIFICATIONS, tr("Display notifications for added torrents"), &cb_torrent_added_notifications);
// Tracker State
cb_tracker_status.setChecked(pref->isTrackerEnabled());
addRow(TRACKER_STATUS, tr("Enable embedded tracker"), &cb_tracker_status);

View File

@@ -52,6 +52,7 @@ signals:
private slots:
void updateCacheSpinSuffix(int value);
void updateInterfaceAddressCombo();
private:
void loadAdvancedSettings();
@@ -60,9 +61,9 @@ private:
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_super_seeding, cb_program_notifications, cb_torrent_added_notifications, cb_tracker_status,
cb_confirm_torrent_recheck, cb_enable_tracker_ext, cb_listen_ipv6, cb_announce_all_trackers;
QComboBox combo_iface;
QComboBox combo_iface, combo_iface_address;
QLineEdit txt_network_address;
// OS dependent settings

89
src/gui/cookiesdialog.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 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 "cookiesdialog.h"
#include "base/settingsstorage.h"
#include "base/net/downloadmanager.h"
#include "guiiconprovider.h"
#include "cookiesmodel.h"
#include "ui_cookiesdialog.h"
#define SETTINGS_KEY(name) "CookiesDialog/" name
const QString KEY_GEOMETRY = SETTINGS_KEY("Geometry");
const QString KEY_COOKIESVIEWSTATE = SETTINGS_KEY("CookiesViewState");
CookiesDialog::CookiesDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::CookiesDialog)
, m_cookiesModel(new CookiesModel(Net::DownloadManager::instance()->allCookies(), this))
{
m_ui->setupUi(this);
setWindowIcon(GuiIconProvider::instance()->getIcon("preferences-web-browser-cookies"));
m_ui->buttonAdd->setIcon(GuiIconProvider::instance()->getIcon("list-add"));
m_ui->buttonDelete->setIcon(GuiIconProvider::instance()->getIcon("list-remove"));
m_ui->treeView->setModel(m_cookiesModel);
if (m_cookiesModel->rowCount() > 0)
m_ui->treeView->selectionModel()->setCurrentIndex(
m_cookiesModel->index(0, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
restoreGeometry(SettingsStorage::instance()->loadValue(KEY_GEOMETRY).toByteArray());
m_ui->treeView->header()->restoreState(
SettingsStorage::instance()->loadValue(KEY_COOKIESVIEWSTATE).toByteArray());
}
CookiesDialog::~CookiesDialog()
{
SettingsStorage::instance()->storeValue(KEY_GEOMETRY, saveGeometry());
SettingsStorage::instance()->storeValue(
KEY_COOKIESVIEWSTATE, m_ui->treeView->header()->saveState());
delete m_ui;
}
void CookiesDialog::accept()
{
Net::DownloadManager::instance()->setAllCookies(m_cookiesModel->cookies());
QDialog::accept();
}
void CookiesDialog::onButtonAddClicked()
{
int row = m_ui->treeView->selectionModel()->currentIndex().row() + 1;
m_cookiesModel->insertRow(row);
m_ui->treeView->selectionModel()->setCurrentIndex(
m_cookiesModel->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
void CookiesDialog::onButtonDeleteClicked()
{
m_cookiesModel->removeRow(m_ui->treeView->selectionModel()->currentIndex().row());
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 Christophe Dumez
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 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
@@ -24,39 +24,38 @@
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
*/
#ifndef COOKIESDLG_H
#define COOKIESDLG_H
#ifndef COOKIESDIALOG_H
#define COOKIESDIALOG_H
#include <QDialog>
#include <QList>
class QNetworkCookie;
class QUrl;
namespace Ui {
class CookiesDlg;
namespace Ui
{
class CookiesDialog;
}
class CookiesDlg : public QDialog
class CookiesModel;
class CookiesDialog : public QDialog
{
Q_OBJECT
public:
explicit CookiesDlg(const QUrl &url, QWidget *parent = 0);
~CookiesDlg();
QList<QNetworkCookie> getCookies() const;
static bool askForCookies(QWidget *parent, const QUrl &url, QList<QNetworkCookie> &out);
explicit CookiesDialog(QWidget *parent = 0);
~CookiesDialog();
protected slots:
void on_add_btn_clicked();
void on_del_btn_clicked();
public slots:
void accept() override;
private slots:
void onButtonAddClicked();
void onButtonDeleteClicked();
private:
Ui::CookiesDlg *ui;
Ui::CookiesDialog *m_ui;
CookiesModel *m_cookiesModel;
};
#endif // COOKIESDLG_H
#endif // COOKIESDIALOG_H

179
src/gui/cookiesdialog.ui Normal file
View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CookiesDialog</class>
<widget class="QDialog" name="CookiesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>369</height>
</rect>
</property>
<property name="windowTitle">
<string>Manage Cookies</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="treeView"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonAdd">
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonDelete">
<property name="text">
<string notr="true"/>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</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>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CookiesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>406</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CookiesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>406</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonAdd</sender>
<signal>clicked()</signal>
<receiver>CookiesDialog</receiver>
<slot>onButtonAddClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>484</x>
<y>174</y>
</hint>
<hint type="destinationlabel">
<x>486</x>
<y>93</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonDelete</sender>
<signal>clicked()</signal>
<receiver>CookiesDialog</receiver>
<slot>onButtonDeleteClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>483</x>
<y>226</y>
</hint>
<hint type="destinationlabel">
<x>485</x>
<y>296</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>onButtonAddClicked()</slot>
<slot>onButtonDeleteClicked()</slot>
</slots>
</ui>

178
src/gui/cookiesmodel.cpp Normal file
View File

@@ -0,0 +1,178 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 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 <QDateTime>
#include "cookiesmodel.h"
CookiesModel::CookiesModel(const QList<QNetworkCookie> &cookies, QObject *parent)
: QAbstractItemModel(parent)
, m_cookies(cookies)
{
}
QList<QNetworkCookie> CookiesModel::cookies() const
{
return m_cookies;
}
QVariant CookiesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) {
switch (section)
{
case COL_DOMAIN:
return tr("Domain");
case COL_PATH:
return tr("Path");
case COL_NAME:
return tr("Name");
case COL_VALUE:
return tr("Value");
case COL_EXPDATE:
return tr("Expiration Date");
}
}
return QVariant();
}
QModelIndex CookiesModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() // no items with valid parent
|| (row < 0) || (row >= m_cookies.size())
|| (column < 0) || (column >= NB_COLUMNS))
return QModelIndex();
return createIndex(row, column, &m_cookies[row]);
}
QModelIndex CookiesModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index);
return QModelIndex();
}
int CookiesModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) return 0;
return m_cookies.size();
}
int CookiesModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return NB_COLUMNS;
}
QVariant CookiesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || (index.row() >= m_cookies.size())
|| ((role != Qt::DisplayRole) && (role != Qt::EditRole)))
return QVariant();
switch (index.column()) {
case COL_DOMAIN:
return m_cookies[index.row()].domain();
case COL_PATH:
return m_cookies[index.row()].path();
case COL_NAME:
return QString::fromLatin1(m_cookies[index.row()].name());
case COL_VALUE:
return QString::fromLatin1(m_cookies[index.row()].value());
case COL_EXPDATE:
return m_cookies[index.row()].expirationDate();
}
return QVariant();
}
bool CookiesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole) return false;
switch (index.column()) {
case COL_DOMAIN:
m_cookies[index.row()].setDomain(value.toString());
break;
case COL_PATH:
m_cookies[index.row()].setPath(value.toString());
break;
case COL_NAME:
m_cookies[index.row()].setName(value.toString().toLatin1());
break;
case COL_VALUE:
m_cookies[index.row()].setValue(value.toString().toLatin1());
break;
case COL_EXPDATE:
m_cookies[index.row()].setExpirationDate(value.toDateTime());
break;
default:
return false;
}
emit dataChanged(index, index);
return true;
}
bool CookiesModel::insertRows(int row, int count, const QModelIndex &parent)
{
if ((row < 0) || (row > m_cookies.size())) return false;
QNetworkCookie newCookie;
newCookie.setExpirationDate(QDateTime::currentDateTime().addYears(99));
beginInsertRows(parent, row, row + count - 1);
while (count-- > 0)
m_cookies.insert(row, newCookie);
endInsertRows();
return true;
}
bool CookiesModel::removeRows(int row, int count, const QModelIndex &parent)
{
if ((m_cookies.size() == 0)
|| (row >= m_cookies.size())
|| ((row + count) > m_cookies.size()))
return false;
beginRemoveRows(parent, row, row + count - 1);
while (count-- > 0)
m_cookies.removeAt(row);
endRemoveRows();
return true;
}
Qt::ItemFlags CookiesModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) return 0;
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
}

74
src/gui/cookiesmodel.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 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 COOKIESMODEL_H
#define COOKIESMODEL_H
#include <QAbstractItemModel>
#include <QList>
#include <QNetworkCookie>
class CookiesModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Column
{
COL_DOMAIN,
COL_PATH,
COL_NAME,
COL_VALUE,
COL_EXPDATE,
NB_COLUMNS
};
explicit CookiesModel(const QList<QNetworkCookie> &cookies, QObject *parent = 0);
QList<QNetworkCookie> cookies() const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
private:
mutable QList<QNetworkCookie> m_cookies;
};
#endif // COOKIESMODEL_H

View File

@@ -35,19 +35,17 @@
#include <QPalette>
#include "executionlog.h"
#include "ui_executionlog.h"
#include "base/preferences.h"
#include "guiiconprovider.h"
#include "loglistwidget.h"
ExecutionLog::ExecutionLog(QWidget *parent)
ExecutionLog::ExecutionLog(QWidget *parent, const Log::MsgTypes &types)
: QWidget(parent)
, ui(new Ui::ExecutionLog)
, m_peerList(new LogListWidget(MAX_LOG_MESSAGES))
{
ui->setupUi(this);
m_msgList = new LogListWidget(MAX_LOG_MESSAGES,
Log::MsgTypes(Preferences::instance()->executionLogMessageTypes()));
m_msgList = new LogListWidget(MAX_LOG_MESSAGES, Log::MsgTypes(types));
ui->tabConsole->setTabIcon(0, GuiIconProvider::instance()->getIcon("view-calendar-journal"));
ui->tabConsole->setTabIcon(1, GuiIconProvider::instance()->getIcon("view-filter"));

View File

@@ -46,7 +46,7 @@ class ExecutionLog: public QWidget
Q_OBJECT
public:
explicit ExecutionLog(QWidget *parent = 0);
ExecutionLog(QWidget *parent, const Log::MsgTypes &types);
void showMsgTypes(const Log::MsgTypes &types);
~ExecutionLog();

View File

@@ -28,7 +28,6 @@ HEADERS += \
$$PWD/downloadfromurldlg.h \
$$PWD/trackerlogin.h \
$$PWD/hidabletabwidget.h \
$$PWD/torrentimportdlg.h \
$$PWD/executionlog.h \
$$PWD/guiiconprovider.h \
$$PWD/updownratiodlg.h \
@@ -37,9 +36,9 @@ HEADERS += \
$$PWD/autoexpandabledialog.h \
$$PWD/statsdialog.h \
$$PWD/messageboxraised.h \
$$PWD/options_imp.h \
$$PWD/optionsdlg.h \
$$PWD/advancedsettings.h \
$$PWD/shutdownconfirm.h \
$$PWD/shutdownconfirmdlg.h \
$$PWD/torrentmodel.h \
$$PWD/torrentcreatordlg.h \
$$PWD/scanfoldersdelegate.h \
@@ -48,7 +47,9 @@ HEADERS += \
$$PWD/search/pluginselectdlg.h \
$$PWD/search/pluginsourcedlg.h \
$$PWD/search/searchlistdelegate.h \
$$PWD/search/searchsortmodel.h
$$PWD/search/searchsortmodel.h \
$$PWD/cookiesmodel.h \
$$PWD/cookiesdialog.h
SOURCES += \
$$PWD/mainwindow.cpp \
@@ -63,7 +64,6 @@ SOURCES += \
$$PWD/torrentcontentmodelfile.cpp \
$$PWD/torrentcontentfiltermodel.cpp \
$$PWD/torrentcontenttreeview.cpp \
$$PWD/torrentimportdlg.cpp \
$$PWD/executionlog.cpp \
$$PWD/speedlimitdlg.cpp \
$$PWD/previewselect.cpp \
@@ -77,8 +77,8 @@ SOURCES += \
$$PWD/statusbar.cpp \
$$PWD/advancedsettings.cpp \
$$PWD/trackerlogin.cpp \
$$PWD/options_imp.cpp \
$$PWD/shutdownconfirm.cpp \
$$PWD/optionsdlg.cpp \
$$PWD/shutdownconfirmdlg.cpp \
$$PWD/torrentmodel.cpp \
$$PWD/torrentcreatordlg.cpp \
$$PWD/scanfoldersdelegate.cpp \
@@ -87,7 +87,9 @@ SOURCES += \
$$PWD/search/pluginselectdlg.cpp \
$$PWD/search/pluginsourcedlg.cpp \
$$PWD/search/searchlistdelegate.cpp \
$$PWD/search/searchsortmodel.cpp
$$PWD/search/searchsortmodel.cpp \
$$PWD/cookiesmodel.cpp \
$$PWD/cookiesdialog.cpp
win32|macx {
HEADERS += $$PWD/programupdater.h
@@ -103,16 +105,17 @@ FORMS += \
$$PWD/bandwidth_limit.ui \
$$PWD/updownratiodlg.ui \
$$PWD/confirmdeletiondlg.ui \
$$PWD/confirmshutdowndlg.ui \
$$PWD/torrentimportdlg.ui \
$$PWD/shutdownconfirmdlg.ui \
$$PWD/executionlog.ui \
$$PWD/addnewtorrentdialog.ui \
$$PWD/autoexpandabledialog.ui \
$$PWD/statsdialog.ui \
$$PWD/options.ui \
$$PWD/optionsdlg.ui \
$$PWD/torrentcreatordlg.ui \
$$PWD/search/searchwidget.ui \
$$PWD/search/pluginselectdlg.ui \
$$PWD/search/pluginsourcedlg.ui
$$PWD/search/pluginsourcedlg.ui \
$$PWD/search/searchtab.ui \
$$PWD/cookiesdialog.ui
RESOURCES += $$PWD/about.qrc

View File

@@ -10,9 +10,11 @@ set(QBT_LINEEDIT_RESOURCES
resources/lineeditimages.qrc
)
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS} ${QBT_LINEEDIT_RESOURCES})
add_library(qbt_lineedit STATIC ${QBT_LINEEDIT_SOURCES} ${QBT_LINEEDIT_HEADERS})
if (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt4::QtGui)
else (QT4_FOUND)
target_link_libraries(qbt_lineedit Qt5::Widgets)
endif (QT4_FOUND)
qbt_target_sources(${QBT_LINEEDIT_RESOURCES})

View File

@@ -14,59 +14,57 @@
#include <QResizeEvent>
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
: QLineEdit(parent)
{
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
QPixmap pixmap1(":/lineeditimages/search.png");
searchButton = new QToolButton(this);
searchButton->setIcon(QIcon(pixmap1));
searchButton->setIconSize(pixmap1.size());
searchButton->setCursor(Qt::ArrowCursor);
searchButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
QSize searchButtonHint = searchButton->sizeHint();
QPixmap pixmap1(":/lineeditimages/search.png");
searchButton = new QToolButton(this);
searchButton->setIcon(QIcon(pixmap1));
searchButton->setIconSize(pixmap1.size());
searchButton->setCursor(Qt::ArrowCursor);
searchButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
int clearButtonSizeHintWidth = 0;
int clearButtonSizeHintHeight = 0;
QSize clearButtonHint(0, 0);
#ifndef QBT_USES_QT5
QPixmap pixmap2(":/lineeditimages/clear_left.png");
clearButton = new QToolButton(this);
clearButton->setIcon(QIcon(pixmap2));
clearButton->setIconSize(pixmap2.size());
clearButton->setCursor(Qt::ArrowCursor);
clearButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
clearButton->setToolTip(tr("Clear the text"));
clearButton->hide();
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
QPixmap pixmap2(":/lineeditimages/clear_left.png");
clearButton = new QToolButton(this);
clearButton->setIcon(QIcon(pixmap2));
clearButton->setIconSize(pixmap2.size());
clearButton->setCursor(Qt::ArrowCursor);
clearButton->setStyleSheet("QToolButton { border: none; padding: 2px; }");
clearButton->setToolTip(tr("Clear the text"));
clearButton->hide();
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(updateCloseButton(const QString &)));
clearButtonSizeHintWidth = clearButton->sizeHint().width();
clearButtonSizeHintHeight = clearButton->sizeHint().height();
setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; }").arg(searchButton->sizeHint().width()).arg(clearButtonSizeHintWidth));
clearButtonHint = clearButton->sizeHint();
setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; }").arg(searchButtonHint.width()).arg(clearButtonHint.width()));
#else
setClearButtonEnabled(true);
setStyleSheet(QString("QLineEdit { padding-left: %1px; }").arg(searchButton->sizeHint().width())); // padding between text and widget borders
setClearButtonEnabled(true);
setStyleSheet(QString("QLineEdit { padding-left: %1px; }").arg(searchButtonHint.width())); // padding between text and widget borders
#endif
QSize msz = sizeHint();
setMinimumSize(qMax(msz.width(), searchButton->sizeHint().width() + clearButtonSizeHintWidth),
std::max({ msz.height(), searchButton->sizeHint().height(), clearButtonSizeHintHeight }) + frameWidth * 2);
QSize widgetHint = sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setMaximumHeight(std::max({ widgetHint.height(), searchButtonHint.height(), clearButtonHint.height() }) + frameWidth * 2);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
}
void LineEdit::resizeEvent(QResizeEvent *e)
{
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
QSize sz = searchButton->sizeHint();
searchButton->move(frameWidth, (e->size().height() - sz.height()) / 2);
QSize sz = searchButton->sizeHint();
searchButton->move(frameWidth, (e->size().height() - sz.height()) / 2);
#ifndef QBT_USES_QT5
QSize cz = clearButton->sizeHint();
clearButton->move((e->size().width() - frameWidth - cz.width()), (e->size().height() - sz.height()) / 2);
QSize cz = clearButton->sizeHint();
clearButton->move((e->size().width() - frameWidth - cz.width()), (e->size().height() - sz.height()) / 2);
#endif
}
#ifndef QBT_USES_QT5
void LineEdit::updateCloseButton(const QString& text)
void LineEdit::updateCloseButton(const QString &text)
{
clearButton->setVisible(!text.isEmpty());
clearButton->setVisible(!text.isEmpty());
}
#endif

View File

@@ -16,23 +16,23 @@ class QToolButton;
class LineEdit : public QLineEdit
{
Q_OBJECT
Q_OBJECT
public:
LineEdit(QWidget *parent);
LineEdit(QWidget *parent);
protected:
void resizeEvent(QResizeEvent *e);
void resizeEvent(QResizeEvent *e);
#ifndef QBT_USES_QT5
private slots:
void updateCloseButton(const QString &text);
void updateCloseButton(const QString &text);
#endif
private:
QToolButton *searchButton;
QToolButton *searchButton;
#ifndef QBT_USES_QT5
QToolButton *clearButton;
QToolButton *clearButton;
#endif
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -28,20 +28,25 @@
* Contact : chris@qbittorrent.org
*/
#ifndef GUI_H
#define GUI_H
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QProcess>
#include <QMainWindow>
#include <QSystemTrayIcon>
#include <QPointer>
#include "ui_mainwindow.h"
#include "statsdialog.h"
class QCloseEvent;
class QFileSystemWatcher;
class QShortcut;
class QSplitter;
class QTabWidget;
class QTimer;
class downloadFromURL;
class SearchWidget;
class RSSImp;
class about;
class options_imp;
class OptionsDialog;
class TransferListWidget;
class TransferListFiltersWidget;
class PropertiesWidget;
@@ -52,61 +57,59 @@ class downloadFromURL;
class LineEdit;
class ExecutionLog;
class PowerManagement;
QT_BEGIN_NAMESPACE
class QCloseEvent;
class QFileSystemWatcher;
class QShortcut;
class QSplitter;
class QTabWidget;
class QTimer;
QT_END_NAMESPACE
class StatsDialog;
namespace BitTorrent
{
class TorrentHandle;
}
class MainWindow: public QMainWindow, private Ui::MainWindow
namespace Ui
{
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// Construct / Destruct
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
// Methods
QWidget* getCurrentTabWidget() const;
TransferListWidget* getTransferList() const { return transferList; }
QMenu* getTrayIconMenu();
PropertiesWidget *getProperties() const { return properties; }
~MainWindow() override;
QWidget* currentTabWidget() const;
TransferListWidget* transferListWidget() const;
PropertiesWidget *propertiesWidget() const;
QMenu* trayIconMenu();
// ExecutionLog properties
bool isExecutionLogEnabled() const;
void setExecutionLogEnabled(bool value);
int executionLogMsgTypes() const;
void setExecutionLogMsgTypes(const int value);
// Notifications properties
bool isNotificationsEnabled() const;
void setNotificationsEnabled(bool value);
bool isTorrentAddedNotificationsEnabled() const;
void setTorrentAddedNotificationsEnabled(bool value);
public slots:
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void setTabText(int index, QString text) const;
void showNotificationBaloon(QString title, QString msg) const;
void downloadFromURLList(const QStringList& urls);
void updateAltSpeedsBtn(bool alternative);
void updateNbTorrents();
void activate();
void cleanup();
protected slots:
// GUI related slots
void showNotificationBaloon(QString title, QString msg) const;
private slots:
void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger);
void on_actionAbout_triggered();
void on_actionStatistics_triggered();
void on_actionCreate_torrent_triggered();
void balloonClicked();
void writeSettings();
void readSettings();
void on_actionExit_triggered();
void createTrayIcon();
void fullDiskError(BitTorrent::TorrentHandle *const torrent, QString msg) const;
void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
void tab_changed(int);
void on_actionLock_qBittorrent_triggered();
void tabChanged(int newTab);
void defineUILockPassword();
void clearUILockPassword();
bool unlockUI();
@@ -119,110 +122,56 @@ protected slots:
void displayTransferTab() const;
void displaySearchTab() const;
void displayRSSTab() const;
// Torrent actions
void on_actionSet_global_upload_limit_triggered();
void on_actionSet_global_download_limit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void updateGUI();
void loadPreferences(bool configure_session = true);
void loadPreferences(bool configureSession = true);
void addUnauthenticatedTracker(const QPair<BitTorrent::TorrentHandle*, QString> &tracker);
void addTorrentFailed(const QString &error) const;
void torrentNew(BitTorrent::TorrentHandle *const torrent) const;
void finishedTorrent(BitTorrent::TorrentHandle *const torrent) const;
void askRecursiveTorrentDownloadConfirmation(BitTorrent::TorrentHandle *const torrent);
// Options slots
void on_actionOptions_triggered();
void optionsSaved();
// HTTP slots
void on_actionDownload_from_URL_triggered();
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
void handleUpdateCheckFinished(bool update_available, QString new_version, bool invokedByUser);
void handleUpdateCheckFinished(bool updateAvailable, QString newVersion, bool invokedByUser);
#endif
void updateRSSTabLabel(int count);
protected:
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void closeEvent(QCloseEvent *);
void showEvent(QShowEvent *);
bool event(QEvent * event);
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
private:
QIcon getSystrayIcon() const;
#ifdef Q_OS_WIN
bool addPythonPathToEnv();
void installPython();
private slots:
void pythonDownloadSuccess(const QString &url, const QString &filePath);
void pythonDownloadFailure(const QString &url, const QString &error);
#endif
void addToolbarContextMenu();
void manageCookies();
private:
QFileSystemWatcher *executable_watcher;
// Bittorrent
QList<QPair<BitTorrent::TorrentHandle*, QString>> unauthenticated_trackers; // Still needed?
// GUI related
bool m_posInitialized;
QTabWidget *tabs;
StatusBar *status_bar;
QPointer<options_imp> options;
QPointer<about> aboutDlg;
QPointer<StatsDialog> statsDlg;
QPointer<TorrentCreatorDlg> createTorrentDlg;
QPointer<downloadFromURL> downloadFromURLDialog;
QPointer<QSystemTrayIcon> systrayIcon;
QPointer<QTimer> systrayCreator;
QPointer<QMenu> myTrayIconMenu;
TransferListWidget *transferList;
TransferListFiltersWidget *transferListFilters;
PropertiesWidget *properties;
bool displaySpeedInTitle;
bool force_exit;
bool ui_locked;
bool unlockDlgShowing;
LineEdit *search_filter;
QAction *searchFilterAct;
// Widgets
QAction *prioSeparator;
QAction *prioSeparatorMenu;
QSplitter *hSplitter;
QSplitter *vSplitter;
// Search
QPointer<SearchWidget> searchEngine;
// RSS
QPointer<RSSImp> rssWidget;
// Execution Log
QPointer<ExecutionLog> m_executionLog;
// Power Management
PowerManagement *m_pwr;
QTimer *preventTimer;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QTimer programUpdateTimer;
bool m_wasUpdateCheckEnabled;
#endif
bool has_python;
QMenu* toolbarMenu;
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
void downloadFromURLList(const QStringList &urlList);
void updateAltSpeedsBtn(bool alternative);
void updateNbTorrents();
private slots:
void on_actionSearch_engine_triggered();
void on_actionRSS_Reader_triggered();
void on_actionSpeed_in_title_bar_triggered();
void on_actionTop_tool_bar_triggered();
void on_action_Import_Torrent_triggered();
void on_actionDonate_money_triggered();
void on_actionSearchWidget_triggered();
void on_actionRSSReader_triggered();
void on_actionSpeedInTitleBar_triggered();
void on_actionTopToolBar_triggered();
void on_actionDonateMoney_triggered();
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 );
void on_actionAutoShutdown_system_toggled(bool );
void on_actionAutoExit_toggled(bool);
void on_actionAutoSuspend_toggled(bool);
void on_actionAutoHibernate_toggled(bool);
void on_actionAutoShutdown_toggled(bool);
void on_actionAbout_triggered();
void on_actionStatistics_triggered();
void on_actionCreateTorrent_triggered();
void on_actionOptions_triggered();
void on_actionSetGlobalUploadLimit_triggered();
void on_actionSetGlobalDownloadLimit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void on_actionDownloadFromURL_triggered();
void on_actionExit_triggered();
void on_actionLock_triggered();
// Check for active torrents and set preventing from suspend state
void checkForActiveTorrents();
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
@@ -234,6 +183,64 @@ private slots:
void toolbarTextBeside();
void toolbarTextUnder();
void toolbarFollowSystem();
private:
QIcon getSystrayIcon() const;
#ifdef Q_OS_WIN
bool addPythonPathToEnv();
void installPython();
#endif
void dropEvent(QDropEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void closeEvent(QCloseEvent *) override;
void showEvent(QShowEvent *) override;
bool event(QEvent * event) override;
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
Ui::MainWindow *m_ui;
QFileSystemWatcher *m_executableWatcher;
// Bittorrent
QList<QPair<BitTorrent::TorrentHandle*, QString>> m_unauthenticatedTrackers; // Still needed?
// GUI related
bool m_posInitialized;
QTabWidget *m_tabs;
StatusBar *m_statusBar;
QPointer<OptionsDialog> m_options;
QPointer<about> m_aboutDlg;
QPointer<StatsDialog> m_statsDlg;
QPointer<TorrentCreatorDlg> m_createTorrentDlg;
QPointer<downloadFromURL> m_downloadFromURLDialog;
QPointer<QSystemTrayIcon> m_systrayIcon;
QPointer<QTimer> m_systrayCreator;
QPointer<QMenu> m_trayIconMenu;
TransferListWidget *m_transferListWidget;
TransferListFiltersWidget *m_transferListFiltersWidget;
PropertiesWidget *m_propertiesWidget;
bool m_displaySpeedInTitle;
bool m_forceExit;
bool m_uiLocked;
bool m_unlockDlgShowing;
LineEdit *m_searchFilter;
QAction *m_searchFilterAction;
// Widgets
QAction *m_prioSeparator;
QAction *m_prioSeparatorMenu;
QSplitter *m_splitter;
QPointer<SearchWidget> m_searchWidget;
QPointer<RSSImp> m_rssWidget;
QPointer<ExecutionLog> m_executionLog;
// Power Management
PowerManagement *m_pwr;
QTimer *m_preventTimer;
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
QTimer *m_programUpdateTimer;
bool m_wasUpdateCheckEnabled;
#endif
bool m_hasPython;
QMenu *m_toolbarMenu;
};
#endif
#endif // MAINWINDOW_H

View File

@@ -38,7 +38,7 @@
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menu_Edit">
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>&amp;Edit</string>
</property>
@@ -51,43 +51,43 @@
<addaction name="actionDecreasePriority"/>
<addaction name="actionBottomPriority"/>
</widget>
<widget class="QMenu" name="menu_Help">
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="actionDocumentation"/>
<addaction name="actionCheck_for_updates"/>
<addaction name="actionCheckForUpdates"/>
<addaction name="separator"/>
<addaction name="actionDonate_money"/>
<addaction name="actionDonateMoney"/>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menu_Options">
<widget class="QMenu" name="menuOptions">
<property name="title">
<string>&amp;Tools</string>
</property>
<widget class="QMenu" name="menuAuto_Shutdown_on_downloads_completion">
<widget class="QMenu" name="menuAutoShutdownOnDownloadsCompletion">
<property name="title">
<string>On Downloads &amp;Done</string>
</property>
<addaction name="actionAutoShutdown_Disabled"/>
<addaction name="actionAutoExit_qBittorrent"/>
<addaction name="actionAutoSuspend_system"/>
<addaction name="actionAutoHibernate_system"/>
<addaction name="actionAutoShutdown_system"/>
<addaction name="actionAutoShutdownDisabled"/>
<addaction name="actionAutoExit"/>
<addaction name="actionAutoSuspend"/>
<addaction name="actionAutoHibernate"/>
<addaction name="actionAutoShutdown"/>
</widget>
<addaction name="actionCreate_torrent"/>
<addaction name="actionCreateTorrent"/>
<addaction name="separator"/>
<addaction name="actionManageCookies"/>
<addaction name="actionOptions"/>
<addaction name="separator"/>
<addaction name="menuAuto_Shutdown_on_downloads_completion"/>
<addaction name="menuAutoShutdownOnDownloadsCompletion"/>
</widget>
<widget class="QMenu" name="menu_File">
<widget class="QMenu" name="menuFile">
<property name="title">
<string>&amp;File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionDownload_from_URL"/>
<addaction name="action_Import_Torrent"/>
<addaction name="actionDownloadFromURL"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
@@ -106,22 +106,23 @@
<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="actionTopToolBar"/>
<addaction name="actionSpeedInTitleBar"/>
<addaction name="separator"/>
<addaction name="actionSearchWidget"/>
<addaction name="actionRSSReader"/>
<addaction name="menuLog"/>
<addaction name="separator"/>
<addaction name="actionStatistics"/>
<addaction name="separator"/>
<addaction name="actionLock_qBittorrent"/>
<addaction name="actionLock"/>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menu_Options"/>
<addaction name="menu_Help"/>
<addaction name="menuOptions"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="movable">
@@ -142,7 +143,7 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionDownload_from_URL"/>
<addaction name="actionDownloadFromURL"/>
<addaction name="actionOpen"/>
<addaction name="actionDelete"/>
<addaction name="separator"/>
@@ -154,7 +155,7 @@
<addaction name="actionBottomPriority"/>
<addaction name="separator"/>
<addaction name="actionOptions"/>
<addaction name="actionLock_qBittorrent"/>
<addaction name="actionLock"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionOpen">
@@ -195,7 +196,7 @@
<string>&amp;Delete</string>
</property>
</action>
<action name="actionDownload_from_URL">
<action name="actionDownloadFromURL">
<property name="text">
<string>Add Torrent &amp;Link...</string>
</property>
@@ -203,17 +204,17 @@
<string>Open URL</string>
</property>
</action>
<action name="actionCreate_torrent">
<action name="actionCreateTorrent">
<property name="text">
<string>Torrent &amp;Creator</string>
</property>
</action>
<action name="actionSet_upload_limit">
<action name="actionSetUploadLimit">
<property name="text">
<string>Set Upload Limit...</string>
</property>
</action>
<action name="actionSet_download_limit">
<action name="actionSetDownloadLimit">
<property name="text">
<string>Set Download Limit...</string>
</property>
@@ -223,12 +224,12 @@
<string>&amp;Documentation</string>
</property>
</action>
<action name="actionSet_global_download_limit">
<action name="actionSetGlobalDownloadLimit">
<property name="text">
<string>Set Global Download Limit...</string>
</property>
</action>
<action name="actionSet_global_upload_limit">
<action name="actionSetGlobalUploadLimit">
<property name="text">
<string>Set Global Upload Limit...</string>
</property>
@@ -265,7 +266,7 @@
<bool>true</bool>
</property>
</action>
<action name="actionUse_alternative_speed_limits">
<action name="actionUseAlternativeSpeedLimits">
<property name="checkable">
<bool>true</bool>
</property>
@@ -276,7 +277,7 @@
<string>Alternative Speed Limits</string>
</property>
</action>
<action name="actionTop_tool_bar">
<action name="actionTopToolBar">
<property name="checkable">
<bool>true</bool>
</property>
@@ -287,7 +288,7 @@
<string>Display Top Toolbar</string>
</property>
</action>
<action name="actionSpeed_in_title_bar">
<action name="actionSpeedInTitleBar">
<property name="checkable">
<bool>true</bool>
</property>
@@ -298,7 +299,7 @@
<string>Show Transfer Speed in Title Bar</string>
</property>
</action>
<action name="actionRSS_Reader">
<action name="actionRSSReader">
<property name="checkable">
<bool>true</bool>
</property>
@@ -306,7 +307,7 @@
<string>&amp;RSS Reader</string>
</property>
</action>
<action name="actionSearch_engine">
<action name="actionSearchWidget">
<property name="checkable">
<bool>true</bool>
</property>
@@ -314,7 +315,7 @@
<string>Search &amp;Engine</string>
</property>
</action>
<action name="actionLock_qBittorrent">
<action name="actionLock">
<property name="text">
<string>L&amp;ock qBittorrent</string>
</property>
@@ -325,15 +326,7 @@
<string notr="true">Ctrl+L</string>
</property>
</action>
<action name="action_Import_Torrent">
<property name="text">
<string>&amp;Import Existing Torrent...</string>
</property>
<property name="toolTip">
<string>Import Torrent...</string>
</property>
</action>
<action name="actionDonate_money">
<action name="actionDonateMoney">
<property name="text">
<string>Do&amp;nate!</string>
</property>
@@ -341,17 +334,17 @@
<string>If you like qBittorrent, please donate!</string>
</property>
</action>
<action name="actionStart_All">
<action name="actionStartAll">
<property name="text">
<string>R&amp;esume All</string>
</property>
</action>
<action name="actionPause_All">
<action name="actionPauseAll">
<property name="text">
<string>P&amp;ause All</string>
</property>
</action>
<action name="actionAutoExit_qBittorrent">
<action name="actionAutoExit">
<property name="checkable">
<bool>true</bool>
</property>
@@ -359,7 +352,7 @@
<string>&amp;Exit qBittorrent</string>
</property>
</action>
<action name="actionAutoSuspend_system">
<action name="actionAutoSuspend">
<property name="checkable">
<bool>true</bool>
</property>
@@ -367,7 +360,7 @@
<string>&amp;Suspend System</string>
</property>
</action>
<action name="actionAutoHibernate_system">
<action name="actionAutoHibernate">
<property name="checkable">
<bool>true</bool>
</property>
@@ -375,7 +368,7 @@
<string>&amp;Hibernate System</string>
</property>
</action>
<action name="actionAutoShutdown_system">
<action name="actionAutoShutdown">
<property name="checkable">
<bool>true</bool>
</property>
@@ -383,7 +376,7 @@
<string>S&amp;hutdown System</string>
</property>
</action>
<action name="actionAutoShutdown_Disabled">
<action name="actionAutoShutdownDisabled">
<property name="checkable">
<bool>true</bool>
</property>
@@ -406,7 +399,7 @@
<string>&amp;Statistics</string>
</property>
</action>
<action name="actionCheck_for_updates">
<action name="actionCheckForUpdates">
<property name="text">
<string>Check for Updates</string>
</property>
@@ -414,6 +407,14 @@
<string>Check for Program Updates</string>
</property>
</action>
<action name="actionManageCookies">
<property name="text">
<string>Manage Cookies...</string>
</property>
<property name="toolTip">
<string>Manage stored network cookies</string>
</property>
</action>
<action name="actionExecutionLogs">
<property name="checkable">
<bool>true</bool>

File diff suppressed because it is too large Load Diff

1667
src/gui/optionsdlg.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -28,10 +28,16 @@
* Contact : chris@qbittorrent.org
*/
#ifndef OPTIONS_IMP_H
#define OPTIONS_IMP_H
#ifndef OPTIONSDLG_H
#define OPTIONSDLG_H
#include "ui_options.h"
#include <QButtonGroup>
#include <QDialog>
class QAbstractButton;
class QCloseEvent;
class QListWidgetItem;
class AdvancedSettings;
// actions on double-click on torrents
enum DoubleClickAction
@@ -41,13 +47,12 @@ enum DoubleClickAction
NO_ACTION
};
class AdvancedSettings;
namespace Ui
{
class OptionsDialog;
}
QT_BEGIN_NAMESPACE
class QCloseEvent;
QT_END_NAMESPACE
class options_imp: public QDialog, private Ui_Preferences
class OptionsDialog: public QDialog
{
Q_OBJECT
private:
@@ -64,8 +69,8 @@ private:
public:
// Constructor / Destructor
options_imp(QWidget *parent = 0);
~options_imp();
OptionsDialog(QWidget *parent = 0);
~OptionsDialog();
public slots:
void showConnectionTab();
@@ -115,9 +120,6 @@ private:
bool WinStartup() const;
#endif
// Downloads
QString getSavePath() const;
bool isTempPathEnabled() const;
QString getTempPath() const;
bool preAllocateAllFiles() const;
bool useAdditionDialog() const;
bool addTorrentsInPause() const;
@@ -170,6 +172,7 @@ private:
bool webUIAuthenticationOk();
private:
Ui::OptionsDialog *m_ui;
QButtonGroup choiceLanguage;
QAbstractButton *applyButton;
AdvancedSettings *advancedSettings;

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ peerlistwidget.h
proplistdelegate.h
trackerlist.h
downloadedpiecesbar.h
piecesbar.h
peerlistdelegate.h
peerlistsortmodel.h
peersadditiondlg.h
@@ -33,6 +34,7 @@ peerlistwidget.cpp
trackerlist.cpp
peersadditiondlg.cpp
downloadedpiecesbar.cpp
piecesbar.cpp
trackersadditiondlg.cpp
pieceavailabilitybar.cpp
proptabbar.cpp

View File

@@ -28,216 +28,153 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include <QDebug>
#include "downloadedpiecesbar.h"
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent)
#include <cmath>
#include <QDebug>
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent)
: base {parent}
, m_dlPieceColor {0, 0xd0, 0}
{
setToolTip(QString("%1\n%2\n%3").arg(tr("White: Missing pieces")).arg(tr("Green: Partial pieces")).arg(tr("Blue: Completed pieces")));
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
m_dlPieceColor = 0x00d000;
updatePieceColors();
}
QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize)
{
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
const float ratio = vecin.size() / (float)reqSize;
const float ratio = vecin.size() / static_cast<float>(reqSize);
// simple linear transformation algorithm
// for example:
// image.x(0) = pieces.x(0.0 >= x < 1.7)
// image.x(1) = pieces.x(1.7 >= x < 3.4)
// simple linear transformation algorithm
// for example:
// image.x(0) = pieces.x(0.0 >= x < 1.7)
// image.x(1) = pieces.x(1.7 >= x < 3.4)
for (int x = 0; x < reqSize; ++x) {
// R - real
const float fromR = x * ratio;
const float toR = (x + 1) * ratio;
for (int x = 0; x < reqSize; ++x) {
// R - real
const float fromR = x * ratio;
const float toR = (x + 1) * ratio;
// C - integer
int fromC = fromR;// std::floor not needed
int toC = std::ceil(toR);
if (toC > vecin.size())
--toC;
// C - integer
int fromC = fromR; // std::floor not needed
int toC = std::ceil(toR);
if (toC > vecin.size())
--toC;
// position in pieces table
int x2 = fromC;
// position in pieces table
int x2 = fromC;
// little speed up for really big pieces table, 10K+ size
const int toCMinusOne = toC - 1;
// little speed up for really big pieces table, 10K+ size
const int toCMinusOne = toC - 1;
// value in returned vector
float value = 0;
// value in returned vector
float value = 0;
// case when calculated range is (15.2 >= x < 15.7)
if (x2 == toCMinusOne) {
if (vecin[x2]) {
value += ratio;
}
++x2;
}
// case when (15.2 >= x < 17.8)
else {
// subcase (15.2 >= x < 16)
if (x2 != fromR) {
if (vecin[x2]) {
value += 1.0 - (fromR - fromC);
// case when calculated range is (15.2 >= x < 15.7)
if (x2 == toCMinusOne) {
if (vecin[x2])
value += ratio;
++x2;
}
++x2;
}
// case when (15.2 >= x < 17.8)
else {
// subcase (15.2 >= x < 16)
if (x2 != fromR) {
if (vecin[x2])
value += 1.0 - (fromR - fromC);
++x2;
}
// subcase (16 >= x < 17)
for (; x2 < toCMinusOne; ++x2) {
if (vecin[x2]) {
value += 1.0;
}
}
// subcase (16 >= x < 17)
for (; x2 < toCMinusOne; ++x2)
if (vecin[x2])
value += 1.0;
// subcase (17 >= x < 17.8)
if (x2 == toCMinusOne) {
if (vecin[x2]) {
value += 1.0 - (toC - toR);
// subcase (17 >= x < 17.8)
if (x2 == toCMinusOne) {
if (vecin[x2])
value += 1.0 - (toC - toR);
++x2;
}
}
++x2;
}
// normalization <0, 1>
value /= ratio;
// float precision sometimes gives > 1, because in not possible to store irrational numbers
value = qMin(value, 1.0f);
result[x] = value;
}
// normalization <0, 1>
value /= ratio;
// float precision sometimes gives > 1, because in not possible to store irrational numbers
value = qMin(value, (float)1.0);
result[x] = value;
}
return result;
return result;
}
int DownloadedPiecesBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
bool DownloadedPiecesBar::updateImage(QImage &image)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratio_n = 1.0 - ratio;
int r = (r1 * ratio_n) + (r2 * ratio);
int g = (g1 * ratio_n) + (g2 * ratio);
int b = (b1 * ratio_n) + (b2 * ratio);
return qRgb(r, g, b);
}
void DownloadedPiecesBar::updateImage()
{
// qDebug() << "updateImage";
QImage image2(width() - 2, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return;
}
if (m_pieces.isEmpty()) {
image2.fill(0xffffff);
m_image = image2;
update();
return;
}
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x)
{
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
if (pieces2_val_dl != 0)
{
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
int mixedColor = mixTwoColors(m_pieceColor, m_dlPieceColor, ratio);
mixedColor = mixTwoColors(m_bgColor, mixedColor, fill_ratio);
image2.setPixel(x, 0, mixedColor);
// qDebug() << "updateImage";
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return false;
}
else
{
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
if (m_pieces.isEmpty()) {
image2.fill(Qt::white);
image = image2;
return true;
}
}
m_image = image2;
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
if (pieces2_val_dl != 0) {
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
QRgb mixedColor = mixTwoColors(pieceColor().rgb(), m_dlPieceColor.rgb(), ratio);
mixedColor = mixTwoColors(backgroundColor().rgb(), mixedColor, fill_ratio);
image2.setPixel(x, 0, mixedColor);
}
else {
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
}
}
image = image2;
return true;
}
void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces)
{
m_pieces = pieces;
m_downloadedPieces = downloadedPieces;
m_pieces = pieces;
m_downloadedPieces = downloadedPieces;
updateImage();
update();
requestImageUpdate();
}
void DownloadedPiecesBar::updatePieceColors()
void DownloadedPiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete)
{
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
m_dlPieceColor = incomplete;
base::setColors(background, border, complete);
}
void DownloadedPiecesBar::clear()
{
m_image = QImage();
update();
m_pieces.clear();
m_downloadedPieces.clear();
base::clear();
}
void DownloadedPiecesBar::paintEvent(QPaintEvent *)
QString DownloadedPiecesBar::simpleToolTipText() const
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (m_image.isNull())
{
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else
{
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(m_borderColor);
painter.drawPath(border);
return tr("White: Missing pieces") + '\n'
+ tr("Green: Partial pieces") + '\n'
+ tr("Blue: Completed pieces") + '\n';
}
void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete)
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = complete;
m_dlPieceColor = incomplete;
updatePieceColors();
updateImage();
update();
}

View File

@@ -32,54 +32,39 @@
#define DOWNLOADEDPIECESBAR_H
#include <QWidget>
#include <QPainter>
#include <QImage>
#include <QBitArray>
#include <QVector>
class DownloadedPiecesBar: public QWidget {
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
#include "piecesbar.h"
private:
QImage m_image;
// I used values, because it should be possible to change colors in runtime
// background color
int m_bgColor;
// border color
int m_borderColor;
// complete piece color
int m_pieceColor;
// incomplete piece color
int m_dlPieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<int> m_pieceColors;
// last used bitfields, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
QBitArray m_pieces;
QBitArray m_downloadedPieces;
// scale bitfield vector to float vector
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
// draw new image and replace actual image
void updateImage();
class DownloadedPiecesBar: public PiecesBar
{
using base = PiecesBar;
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
public:
DownloadedPiecesBar(QWidget *parent);
DownloadedPiecesBar(QWidget *parent);
void setProgress(const QBitArray &m_pieces, const QBitArray &downloadedPieces);
void updatePieceColors();
void clear();
void setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces);
void setColors(int background, int border, int complete, int incomplete);
void setColors(const QColor &background, const QColor &border, const QColor &complete, const QColor &incomplete);
protected:
void paintEvent(QPaintEvent *);
// PiecesBar interface
void clear() override;
private:
// scale bitfield vector to float vector
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
virtual bool updateImage(QImage &image) override;
QString simpleToolTipText() const override;
// incomplete piece color
QColor m_dlPieceColor;
// last used bitfields, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
QBitArray m_pieces;
QBitArray m_downloadedPieces;
};
#endif // DOWNLOADEDPIECESBAR_H

View File

@@ -43,21 +43,16 @@ public:
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const {
if (sortColumn() == PeerListDelegate::IP || sortColumn() == PeerListDelegate::CLIENT) {
QVariant vL = sourceModel()->data(left);
QVariant vR = sourceModel()->data(right);
if (!(vL.isValid() && vR.isValid()))
return QSortFilterProxyModel::lessThan(left, right);
Q_ASSERT(vL.isValid());
Q_ASSERT(vR.isValid());
switch (sortColumn()) {
case PeerListDelegate::IP:
case PeerListDelegate::CLIENT: {
QString vL = left.data().toString();
QString vR = right.data().toString();
return Utils::String::naturalCompareCaseSensitive(vL, vR);
}
};
bool res = false;
if (Utils::String::naturalSort(vL.toString(), vR.toString(), res))
return res;
return QSortFilterProxyModel::lessThan(left, right);
}
return QSortFilterProxyModel::lessThan(left, right);
return QSortFilterProxyModel::lessThan(left, right);
}
};

View File

@@ -57,7 +57,6 @@
PeerListWidget::PeerListWidget(PropertiesWidget *parent)
: QTreeView(parent)
, m_properties(parent)
, m_resolveCountries(false)
{
// Load settings
loadSettings();
@@ -91,9 +90,9 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
setModel(m_proxyModel);
hideColumn(PeerListDelegate::IP_HIDDEN);
hideColumn(PeerListDelegate::COL_COUNT);
if (!Preferences::instance()->resolvePeerCountries())
m_resolveCountries = Preferences::instance()->resolvePeerCountries();
if (!m_resolveCountries)
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++) {
@@ -108,7 +107,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
//its size is 0, because explicitly 'showing' the column isn't enough
//in the above scenario.
for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
if (!columnWidth(i))
if ((columnWidth(i) <= 0) && !isColumnHidden(i))
resizeColumnToContents(i);
// Context menu
setContextMenuPolicy(Qt::CustomContextMenu);
@@ -126,7 +125,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
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
@@ -208,14 +207,12 @@ void PeerListWidget::updatePeerCountryResolutionState()
m_resolveCountries = !m_resolveCountries;
if (m_resolveCountries) {
loadPeers(m_properties->getCurrentTorrent());
if (!m_wasCountryColHidden) {
showColumn(PeerListDelegate::COUNTRY);
showColumn(PeerListDelegate::COUNTRY);
if (columnWidth(PeerListDelegate::COUNTRY) <= 0)
resizeColumnToContents(PeerListDelegate::COUNTRY);
}
}
else {
hideColumn(PeerListDelegate::COUNTRY);
m_wasCountryColHidden = false; // to forcefully enable that column if the user decides to resolve countries again
}
}
}
@@ -236,7 +233,7 @@ void PeerListWidget::showPeerListMenu(const QPoint&)
QAction *banAct = 0;
QAction *copyPeerAct = 0;
if (!selectionModel()->selectedRows().isEmpty()) {
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy IP:port"));
menu.addSeparator();
banAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently"));
emptyMenu = false;

View File

@@ -94,7 +94,6 @@ private:
QPointer<Net::ReverseResolution> m_resolver;
PropertiesWidget *m_properties;
bool m_resolveCountries;
bool m_wasCountryColHidden;
QShortcut *m_copyHotkey;
};

View File

@@ -28,21 +28,15 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include <QDebug>
#include "pieceavailabilitybar.h"
#include <cmath>
#include <QDebug>
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent)
: QWidget(parent)
: base {parent}
{
setToolTip(QString("%1\n%2").arg(tr("White: Unavailable pieces")).arg(tr("Blue: Available pieces")));
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
updatePieceColors();
}
QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin, int reqSize)
@@ -126,37 +120,18 @@ QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin,
return result;
}
int PieceAvailabilityBar::mixTwoColors(int &rgb1, int &rgb2, float ratio)
bool PieceAvailabilityBar::updateImage(QImage &image)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratio_n = 1.0 - ratio;
int r = (r1 * ratio_n) + (r2 * ratio);
int g = (g1 * ratio_n) + (g2 * ratio);
int b = (b1 * ratio_n) + (b2 * ratio);
return qRgb(r, g, b);
}
void PieceAvailabilityBar::updateImage()
{
QImage image2(width() - 2, 1, QImage::Format_RGB888);
QImage image2(width() - 2 * borderWidth, 1, QImage::Format_RGB888);
if (image2.isNull()) {
qDebug() << "QImage image2() allocation failed, width():" << width();
return;
return false;
}
if (m_pieces.empty()) {
image2.fill(0xffffff);
m_image = image2;
update();
return;
image2.fill(Qt::white);
image = image2;
return true;
}
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
@@ -164,61 +139,32 @@ void PieceAvailabilityBar::updateImage()
// filling image
for (int x = 0; x < scaled_pieces.size(); ++x) {
float pieces2_val = scaled_pieces.at(x);
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
image2.setPixel(x, 0, pieceColors()[pieces2_val * 255]);
}
m_image = image2;
image = image2;
return true;
}
void PieceAvailabilityBar::setAvailability(const QVector<int> &avail)
{
m_pieces = avail;
updateImage();
update();
}
void PieceAvailabilityBar::updatePieceColors()
{
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
requestImageUpdate();
}
void PieceAvailabilityBar::clear()
{
m_image = QImage();
update();
m_pieces.clear();
base::clear();
}
void PieceAvailabilityBar::paintEvent(QPaintEvent *)
QString PieceAvailabilityBar::simpleToolTipText() const
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (m_image.isNull()) {
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else {
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(m_borderColor);
painter.drawPath(border);
return tr("White: Unavailable pieces") + '\n'
+ tr("Blue: Available pieces") + '\n';
}
void PieceAvailabilityBar::setColors(int background, int border, int available)
bool PieceAvailabilityBar::isFileNameCorrectionNeeded() const
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = available;
updatePieceColors();
updateImage();
update();
return true;
}

View File

@@ -31,28 +31,26 @@
#ifndef PIECEAVAILABILITYBAR_H
#define PIECEAVAILABILITYBAR_H
#include <QWidget>
#include <QPainter>
#include <QImage>
#include "piecesbar.h"
class PieceAvailabilityBar: public QWidget
class PieceAvailabilityBar: public PiecesBar
{
using base = PiecesBar;
Q_OBJECT
Q_DISABLE_COPY(PieceAvailabilityBar)
public:
PieceAvailabilityBar(QWidget *parent);
void setAvailability(const QVector<int> &avail);
// PiecesBar interface
void clear() override;
private:
QImage m_image;
// I used values, because it should be possible to change colors in runtime
// background color
int m_bgColor;
// border color
int m_borderColor;
// complete piece color
int m_pieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<int> m_pieceColors;
bool updateImage(QImage &image) override;
QString simpleToolTipText() const override;
bool isFileNameCorrectionNeeded() const override;
// last used int vector, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
@@ -60,23 +58,6 @@ private:
// scale int vector to float vector
QVector<float> intToFloatVector(const QVector<int> &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
// draw new image and replace actual image
void updateImage();
public:
PieceAvailabilityBar(QWidget *parent);
void setAvailability(const QVector<int> &avail);
void updatePieceColors();
void clear();
void setColors(int background, int border, int available);
protected:
void paintEvent(QPaintEvent *);
};
#endif // PIECEAVAILABILITYBAR_H

View File

@@ -0,0 +1,334 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
* Copyright (C) 2006 Christophe Dumez
*
* 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 "piecesbar.h"
#include <QApplication>
#include <QDebug>
#include <QHelpEvent>
#include <QPainter>
#include <QTextStream>
#include <QToolTip>
#include "base/bittorrent/torrenthandle.h"
#include "base/utils/misc.h"
namespace
{
using ImageRange = IndexRange<int>;
// Computes approximate mapping from image scale (measured in pixels) onto the torrent contents scale (in pieces)
// However, taking the size of a screen to be ~ 1000 px and the torrent size larger than 10 MiB, the pointing error
// is well below 0.5 px and thus is negligible.
class PieceIndexToImagePos
{
public:
PieceIndexToImagePos(const BitTorrent::TorrentInfo &torrentInfo, const QImage &image)
: m_bytesPerPixel {image.width() > 0 ? torrentInfo.totalSize() / image.width() : -1}
, m_torrentInfo {torrentInfo}
{
if ((m_bytesPerPixel > 0) && (m_bytesPerPixel < 10))
qDebug() << "PieceIndexToImagePos: torrent size is too small for correct computaions."
<< "Torrent size =" << torrentInfo.totalSize() << "Image width = " << image.width();
}
ImageRange imagePos(const BitTorrent::TorrentInfo::PieceRange &pieces) const
{
if (m_bytesPerPixel < 0)
return {0, 0};
// the type conversion is used to prevent integer overflow with torrents of 2+ GiB size
const qlonglong pieceLength = m_torrentInfo.pieceLength();
return makeInterval<ImageRange::IndexType>(
(pieces.first() * pieceLength) / m_bytesPerPixel,
(pieces.last() * pieceLength + m_torrentInfo.pieceLength(pieces.last()) - 1) / m_bytesPerPixel);
}
int pieceIndex(int imagePos) const
{
return m_bytesPerPixel < 0 ? 0 : (imagePos * m_bytesPerPixel + m_bytesPerPixel / 2) / m_torrentInfo.pieceLength();
}
private:
const qlonglong m_bytesPerPixel; // how many bytes of the torrent are squeezed into a bar's pixel
const BitTorrent::TorrentInfo m_torrentInfo;
};
class DetailedTooltipRenderer
{
public:
DetailedTooltipRenderer(QTextStream &stream, const QString &header)
: m_stream(stream)
{
m_stream << header
<< R"(<table style="width:100%; padding: 3px; vertical-align: middle;">)";
}
~DetailedTooltipRenderer()
{
m_stream << "</table>";
}
void operator()(const QString &size, const QString &path)
{
m_stream << R"(<tr><td style="white-space:nowrap">)" << size << "</td><td>" << path << "</td></tr>";
}
private:
QTextStream &m_stream;
};
}
PiecesBar::PiecesBar(QWidget *parent)
: QWidget {parent}
, m_torrent {nullptr}
, m_borderColor {palette().color(QPalette::Dark)}
, m_bgColor {Qt::white}
, m_pieceColor {Qt::blue}
, m_hovered {false}
{
updatePieceColors();
setMouseTracking(true);
}
void PiecesBar::setTorrent(BitTorrent::TorrentHandle *torrent)
{
m_torrent = torrent;
if (!m_torrent)
clear();
}
void PiecesBar::clear()
{
m_image = QImage();
update();
}
void PiecesBar::setColors(const QColor &background, const QColor &border, const QColor &complete)
{
m_bgColor = background;
m_borderColor = border;
m_pieceColor = complete;
updatePieceColors();
requestImageUpdate();
}
bool PiecesBar::event(QEvent *e)
{
if (e->type() == QEvent::ToolTip) {
showToolTip(static_cast<QHelpEvent *>(e));
return true;
}
else {
return base::event(e);
}
}
void PiecesBar::enterEvent(QEvent *e)
{
m_hovered = true;
base::enterEvent(e);
}
void PiecesBar::leaveEvent(QEvent *e)
{
m_hovered = false;
m_highlitedRegion = QRect();
requestImageUpdate();
base::leaveEvent(e);
}
void PiecesBar::mouseMoveEvent(QMouseEvent *e)
{
// if user pointed to a piece which is a part of a single large file,
// we highlight the space, occupied by this file
highlightFile(e->pos().x() - borderWidth);
base::mouseMoveEvent(e);
}
void PiecesBar::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect imageRect(borderWidth, borderWidth, width() - 2 * borderWidth, height() - 2 * borderWidth);
if (m_image.isNull()) {
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else {
if (m_image.width() != imageRect.width())
updateImage(m_image);
painter.drawImage(imageRect, m_image);
}
if (!m_highlitedRegion.isNull()) {
QColor highlightColor {this->palette().color(QPalette::Active, QPalette::Highlight)};
highlightColor.setAlphaF(0.35);
QRect targetHighlightRect {m_highlitedRegion.adjusted(borderWidth, borderWidth, borderWidth, height() - 2 * borderWidth)};
painter.fillRect(targetHighlightRect, highlightColor);
}
QPainterPath border;
border.addRect(0, 0, width(), height());
painter.setPen(m_borderColor);
painter.drawPath(border);
}
void PiecesBar::requestImageUpdate()
{
if (updateImage(m_image))
update();
}
QColor PiecesBar::backgroundColor() const
{
return m_bgColor;
}
QColor PiecesBar::borderColor() const
{
return m_borderColor;
}
QColor PiecesBar::pieceColor() const
{
return m_pieceColor;
}
const QVector<QRgb> &PiecesBar::pieceColors() const
{
return m_pieceColors;
}
QRgb PiecesBar::mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio)
{
int r1 = qRed(rgb1);
int g1 = qGreen(rgb1);
int b1 = qBlue(rgb1);
int r2 = qRed(rgb2);
int g2 = qGreen(rgb2);
int b2 = qBlue(rgb2);
float ratioN = 1.0f - ratio;
int r = (r1 * ratioN) + (r2 * ratio);
int g = (g1 * ratioN) + (g2 * ratio);
int b = (b1 * ratioN) + (b2 * ratio);
return qRgb(r, g, b);
}
void PiecesBar::showToolTip(const QHelpEvent *e)
{
if (!m_torrent)
return;
QString toolTipText;
QTextStream stream(&toolTipText, QIODevice::WriteOnly);
bool showDetailedInformation = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (showDetailedInformation) {
const int imagePos = e->pos().x() - borderWidth;
if ((imagePos >=0) && (imagePos < m_image.width())) {
stream << "<html><body>";
PieceIndexToImagePos transform {m_torrent->info(), m_image};
int pieceIndex = transform.pieceIndex(imagePos);
QVector<int> files {m_torrent->info().fileIndicesForPiece(pieceIndex)};
QString tooltipTitle;
if (files.count() > 1) {
tooltipTitle = tr("Files in this piece:");
}
else {
if (m_torrent->info().fileSize(files.front()) == m_torrent->info().pieceLength(pieceIndex))
tooltipTitle = tr("File in this piece");
else
tooltipTitle = tr("File in these pieces");
}
DetailedTooltipRenderer renderer(stream, tooltipTitle);
const bool isFileNameCorrectionNeeded = this->isFileNameCorrectionNeeded();
for (int f: files) {
QString filePath {m_torrent->info().filePath(f)};
if (isFileNameCorrectionNeeded)
filePath.replace(QLatin1String("/.unwanted"), QString());
renderer(Utils::Misc::friendlyUnit(m_torrent->info().fileSize(f)), filePath);
}
stream << "</body></html>";
}
}
else {
stream << simpleToolTipText();
stream << '\n' << tr("Hold Shift key for detailed information");
}
stream.flush();
QToolTip::showText(e->globalPos(), toolTipText, this);
}
void PiecesBar::highlightFile(int imagePos)
{
if (!m_torrent || (imagePos < 0) || (imagePos >= m_image.width()))
return;
PieceIndexToImagePos transform {m_torrent->info(), m_image};
int pieceIndex = transform.pieceIndex(imagePos);
QVector<int> fileIndices {m_torrent->info().fileIndicesForPiece(pieceIndex)};
if (fileIndices.count() == 1) {
BitTorrent::TorrentInfo::PieceRange filePieces = m_torrent->info().filePieces(fileIndices.first());
ImageRange imageRange = transform.imagePos(filePieces);
QRect newHighlitedRegion {imageRange.first(), 0, imageRange.size(), m_image.height()};
if (newHighlitedRegion != m_highlitedRegion) {
m_highlitedRegion = newHighlitedRegion;
update();
}
}
else if (!m_highlitedRegion.isEmpty()) {
m_highlitedRegion = QRect();
update();
}
}
void PiecesBar::updatePieceColors()
{
m_pieceColors = QVector<QRgb>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
m_pieceColors[i] = mixTwoColors(backgroundColor().rgb(), m_pieceColor.rgb(), ratio);
}
}
bool PiecesBar::isFileNameCorrectionNeeded() const
{
return false;
}

View File

@@ -0,0 +1,109 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin
* Copyright (C) 2006 Christophe Dumez
*
* 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 PIECESBAR_H
#define PIECESBAR_H
#include <QColor>
#include <QImage>
#include <QWidget>
class QHelpEvent;
namespace BitTorrent
{
class TorrentHandle;
}
class PiecesBar: public QWidget
{
using base = QWidget;
Q_OBJECT
Q_DISABLE_COPY(PiecesBar)
public:
explicit PiecesBar(QWidget *parent = nullptr);
void setTorrent(BitTorrent::TorrentHandle *torrent);
void setColors(const QColor &background, const QColor &border, const QColor &complete);
virtual void clear();
// QObject interface
virtual bool event(QEvent*) override;
protected:
// QWidget interface
void enterEvent(QEvent*) override;
void leaveEvent(QEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void paintEvent(QPaintEvent*) override;
void requestImageUpdate();
QColor backgroundColor() const;
QColor borderColor() const;
QColor pieceColor() const;
const QVector<QRgb> &pieceColors() const;
// mix two colors by light model, ratio <0, 1>
static QRgb mixTwoColors(QRgb rgb1, QRgb rgb2, float ratio);
static constexpr int borderWidth = 1;
private:
void showToolTip(const QHelpEvent*);
void highlightFile(int imagePos);
virtual QString simpleToolTipText() const = 0;
/// whether to perform removing of ".unwanted" directory from paths
virtual bool isFileNameCorrectionNeeded() const;
// draw new image to replace the actual image
// returns true if image was successfully updated
virtual bool updateImage(QImage &image) = 0;
void updatePieceColors();
const BitTorrent::TorrentHandle *m_torrent;
QImage m_image;
// I used values, because it should be possible to change colors at run time
// border color
QColor m_borderColor;
// background color
QColor m_bgColor;
// complete piece color
QColor m_pieceColor;
// buffered 256 levels gradient from bg_color to piece_color
QVector<QRgb> m_pieceColors;
bool m_hovered;
QRect m_highlitedRegion; //!< part of the bar can be highlighted; this rectangle is in the same frame as m_image
};
#endif // PIECESBAR_H

View File

@@ -16,7 +16,8 @@ HEADERS += $$PWD/propertieswidget.h \
$$PWD/pieceavailabilitybar.h \
$$PWD/proptabbar.h \
$$PWD/speedwidget.h \
$$PWD/speedplotview.h
$$PWD/speedplotview.h \
$$PWD/piecesbar.h
SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/proplistdelegate.cpp \
@@ -28,4 +29,5 @@ SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/pieceavailabilitybar.cpp \
$$PWD/proptabbar.cpp \
$$PWD/speedwidget.cpp \
$$PWD/speedplotview.cpp
$$PWD/speedplotview.cpp \
$$PWD/piecesbar.cpp

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