mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-06 18:22:32 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6154d16987 | |||
| a5e8631cba | |||
| 07dcd04357 | |||
| 863de44c18 | |||
| 24135ed48e | |||
| 83709d28c9 | |||
| 859d5a9578 | |||
| adc9b1dec5 | |||
| 285972a20d | |||
| 3ec2f52db5 | |||
| ac64fc3f63 | |||
| b26dc42f3a | |||
| 6572088aec | |||
| 31a0ea1a2d | |||
| 2d72a0d2c0 | |||
| a3a384b049 | |||
| f37dd0d5c2 | |||
| 78c25011cd | |||
| 35d98c91e0 | |||
| 27811db659 | |||
| 4e73ce6bfd | |||
| 8c8e73bb72 | |||
| 0d5b344d2a | |||
| 703cac9a42 | |||
| e6c008b2b7 | |||
| 81de60e0ae | |||
| c361d6010e | |||
| 724dc088ca | |||
| c62fa6a756 | |||
| 656d735de7 | |||
| 408013478c | |||
| fa61b8c11c | |||
| 1339a3ace9 | |||
| 4ba128163f | |||
| 908a99751f | |||
| c115640934 | |||
| 9043c46815 | |||
| 1f418366d4 | |||
| 30c78c7034 | |||
| 74435f6c40 | |||
| a4b281afa4 | |||
| f84670dd63 | |||
| f0207ce654 | |||
| 9cce5a0bd1 | |||
| 95cff549c6 | |||
| 92898a9831 | |||
| fcfbf0e79f | |||
| 680b47c197 | |||
| 7804ed3009 | |||
| d9b39f5ac6 | |||
| 08aa287cfc | |||
| 851d1902a0 | |||
| b600ff0c0e | |||
| a639461c4a | |||
| c1057d5ac7 | |||
| 2ac65ad7e1 | |||
| a4939cb9fd | |||
| 3d88ac2cc8 | |||
| f37fefcc54 | |||
| 885836f68b | |||
| ca4c3bee8d | |||
| 6c278668e4 | |||
| 1665437588 | |||
| 6dc87e499a | |||
| 6c11a1dbb0 | |||
| 29d57e858a | |||
| 7ec9b6685b | |||
| d5de8a91d1 | |||
| 4d6f6fe335 | |||
| 13676f06e5 | |||
| 6cb176ada3 | |||
| 8b6a1ad079 | |||
| 3002c95b2c | |||
| 9590949fa7 | |||
| 6ee61ee6b5 | |||
| e15270271d |
+13
-3
@@ -1,5 +1,9 @@
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
global:
|
||||
- MAKEFLAGS="-j 12"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: clang
|
||||
@@ -67,8 +71,14 @@ matrix:
|
||||
# TODO: Use the same branch name if libtorrent has it.
|
||||
before_install:
|
||||
- git clone https://github.com/rakshasa/libtorrent.git
|
||||
- cd libtorrent && ./autogen.sh && CXX="$COMPILER" ./configure && make -j12 && sudo make install
|
||||
- |
|
||||
pushd libtorrent \
|
||||
&& ./autogen.sh \
|
||||
&& CXX="$COMPILER" ./configure --prefix=/usr \
|
||||
&& make \
|
||||
&& sudo make install \
|
||||
&& popd
|
||||
|
||||
script:
|
||||
- ./autogen.sh && CXX="$COMPILER" ./configure && make -j12
|
||||
- if [ ! $SKIP_CHECK ]; then make -j12 check; fi
|
||||
- ./autogen.sh && CXX="$COMPILER" ./configure && make
|
||||
- if [ ! $SKIP_CHECK ]; then make check; fi
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
BUILDING
|
||||
|
||||
Run "./autogen.sh" to generate the configure scripts if
|
||||
nessesary. The man page "doc/rtorrent.1" must be generated with
|
||||
necessary. The man page "doc/rtorrent.1" must be generated with
|
||||
"docbook2man rtorrent.1.xml" if it is missing.
|
||||
|
||||
Note that rtorrent follows the development of libtorrent closely, and
|
||||
|
||||
+10
-8
@@ -1,6 +1,6 @@
|
||||
AC_INIT(rtorrent, 0.9.7, sundell.software@gmail.com)
|
||||
AC_INIT(rtorrent, 0.9.8, sundell.software@gmail.com)
|
||||
|
||||
AC_DEFINE(API_VERSION, 9, api version)
|
||||
AC_DEFINE(API_VERSION, 10, api version)
|
||||
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
@@ -38,13 +38,9 @@ if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([LIBCURL], [libcurl], , [LIBCURL_CHECK_CONFIG])
|
||||
PKG_CHECK_MODULES([LIBCURL], [libcurl],, [LIBCURL_CHECK_CONFIG])
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.13.7])
|
||||
|
||||
LIBS="$PTHREAD_LIBS $CURSES_LIB $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.13.8])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
@@ -62,6 +58,12 @@ CC_ATTRIBUTE_UNUSED(
|
||||
AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported])
|
||||
)
|
||||
|
||||
dnl Only update global build variables immediately before generating the output,
|
||||
dnl to avoid affecting the global build environment for other autoconf checks.
|
||||
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
doc/Makefile
|
||||
|
||||
+4
-1
@@ -52,7 +52,7 @@ Decrease the download throttle by 1/5/50 KB.
|
||||
\fB->\fR
|
||||
View download.
|
||||
.TP
|
||||
\fB1 - 7\fR
|
||||
\fB0 - 9\fR
|
||||
Change view.
|
||||
.TP
|
||||
\fB^S\fR
|
||||
@@ -99,6 +99,9 @@ Delete the file the torrent is tied to, and clear the association.
|
||||
.TP
|
||||
\fBI\fR
|
||||
Toggle whether torrent ignores ratio settings.
|
||||
.TP
|
||||
\fBF\fR
|
||||
Add a temporary name based regex filter to the current view.
|
||||
.SS "DOWNLOAD VIEW KEYS"
|
||||
.TP
|
||||
\fB->\fR
|
||||
|
||||
@@ -122,7 +122,7 @@ View download.
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>1 - 7</term>
|
||||
<term>0 - 9</term>
|
||||
<listitem><para>
|
||||
Change view.
|
||||
</para></listitem>
|
||||
@@ -225,6 +225,13 @@ Toggle whether torrent ignores ratio settings.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>F</term>
|
||||
<listitem><para>
|
||||
Add a temporary name based regex filter to the current view.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect2>
|
||||
|
||||
+39
-23
@@ -1,22 +1,36 @@
|
||||
# This is an example resource file for rTorrent. Copy to
|
||||
# ~/.rtorrent.rc and enable/modify the options as needed. Remember to
|
||||
# uncomment the options you wish to enable.
|
||||
#############################################################################
|
||||
# This is an (old) example resource file for rTorrent.
|
||||
# Copy to ~/.rtorrent.rc and enable/modify the options as needed.
|
||||
# Remember to uncomment the options you wish to enable.
|
||||
#
|
||||
# See 'rtorrent.rc-example' for a newer, basic configuration.
|
||||
#
|
||||
# Sample: https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
|
||||
# Complete: https://rtorrent-docs.readthedocs.io/en/latest/cmd-ref.html
|
||||
# Useful: https://rtorrent-docs.readthedocs.io/en/latest/use-cases.html
|
||||
# Manual: https://rtorrent-docs.readthedocs.io/en/latest/
|
||||
# Convert: https://github.com/rakshasa/rtorrent/wiki/rTorrent-0.9-Comprehensive-Command-list-(WIP)
|
||||
# Handbook: https://media.readthedocs.org/pdf/rtorrent-docs/latest/rtorrent-docs.pdf
|
||||
# File: https://github.com/rakshasa/rtorrent/blob/master/doc/rtorrent.rc
|
||||
#############################################################################
|
||||
|
||||
# Maximum and minimum number of peers to connect to per torrent.
|
||||
#
|
||||
#throttle.min_peers.normal.set = 40
|
||||
#throttle.max_peers.normal.set = 100
|
||||
|
||||
# Same as above but for seeding completed torrents (-1 = same as downloading)
|
||||
# Same as above but for seeding completed torrents.
|
||||
# "-1" = same as downloading.
|
||||
#
|
||||
#throttle.min_peers.seed.set = 10
|
||||
#throttle.max_peers.seed.set = 50
|
||||
|
||||
# Maximum number of simultanious uploads per torrent.
|
||||
# Maximum number of simultaneous uploads per torrent.
|
||||
#
|
||||
#throttle.max_uploads.set = 15
|
||||
|
||||
# Global upload and download rate in KiB. "0" for unlimited.
|
||||
# Global upload and download rate in KiB.
|
||||
# "0" for unlimited.
|
||||
#
|
||||
#throttle.global_down.max_rate.set_kb = 0
|
||||
#throttle.global_up.max_rate.set_kb = 0
|
||||
@@ -26,7 +40,7 @@
|
||||
#directory.default.set = ./
|
||||
|
||||
# Default session directory. Make sure you don't run multiple instance
|
||||
# of rtorrent using the same session directory. Perhaps using a
|
||||
# of rTorrent using the same session directory. Perhaps using a
|
||||
# relative path?
|
||||
#
|
||||
#session.path.set = ./session
|
||||
@@ -35,18 +49,17 @@
|
||||
# deleted.
|
||||
#
|
||||
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
|
||||
#schedule2 = untied_directory,5,5,stop_untied=
|
||||
|
||||
# Close torrents when diskspace is low.
|
||||
# Close torrents when disk-space is low.
|
||||
#
|
||||
#schedule2 = low_diskspace,5,60,close_low_diskspace=100M
|
||||
|
||||
# The ip address reported to the tracker.
|
||||
# The IP address reported to the tracker.
|
||||
#
|
||||
#network.local_address.set = 127.0.0.1
|
||||
#network.local_address.set = rakshasa.no
|
||||
|
||||
# The ip address the listening socket and outgoing connections is
|
||||
# The IP address the listening socket and outgoing connections is
|
||||
# bound to.
|
||||
#
|
||||
#network.bind_address.set = 127.0.0.1
|
||||
@@ -60,8 +73,8 @@
|
||||
#
|
||||
#network.port_random.set = no
|
||||
|
||||
# Check hash for finished torrents. Might be usefull until the bug is
|
||||
# fixed that causes lack of diskspace not to be properly reported.
|
||||
# Check hash for finished torrents. Might be useful until the bug is
|
||||
# fixed that causes lack of disk-space not to be properly reported.
|
||||
#
|
||||
#pieces.hash.on_completion.set = no
|
||||
|
||||
@@ -69,7 +82,7 @@
|
||||
#
|
||||
#trackers.use_udp.set = yes
|
||||
|
||||
# Alternative calls to bind and ip that should handle dynamic ip's.
|
||||
# Alternative calls to bind and IP that should handle dynamic IP's.
|
||||
#
|
||||
#schedule2 = ip_tick,0,1800,ip=rakshasa
|
||||
#schedule2 = bind_tick,0,1800,bind=rakshasa
|
||||
@@ -79,7 +92,7 @@
|
||||
#
|
||||
# The example value allows incoming encrypted connections, starts unencrypted
|
||||
# outgoing connections but retries with encryption if they fail, preferring
|
||||
# plaintext to RC4 encryption after the encrypted handshake
|
||||
# plain-text to RC4 encryption after the encrypted handshake.
|
||||
#
|
||||
# protocol.encryption.set = allow_incoming,enable_retry,prefer_plaintext
|
||||
|
||||
@@ -87,26 +100,29 @@
|
||||
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
|
||||
# "auto" (start and stop DHT as needed), or "on" (start DHT immediately).
|
||||
# The default is "off". For DHT to work, a session directory must be defined.
|
||||
#
|
||||
#
|
||||
# dht.mode.set = auto
|
||||
|
||||
# UDP port to use for DHT.
|
||||
# UDP port to use for DHT.
|
||||
#
|
||||
#dht.port.set = 6881
|
||||
|
||||
# Enable peer exchange (for torrents not marked private)
|
||||
# Enable peer exchange (for torrents not marked private).
|
||||
#
|
||||
#protocol.pex.set = yes
|
||||
|
||||
# Set downlad list layout style. ("full", "compact")
|
||||
# Set download list layout style ("full", "compact").
|
||||
#
|
||||
#ui.torrent_list.layout.set = "full"
|
||||
|
||||
# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC)
|
||||
# Run rTorrent as a daemon, controlled via XMLRPC.
|
||||
#
|
||||
#system.daemon.set = false
|
||||
|
||||
# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC)
|
||||
# Use a IP socket with scgi_port, or a Unix socket with scgi_local.
|
||||
# schedule can be used to set permissions on the unix socket.
|
||||
#
|
||||
#scgi_port = 127.0.0.1:5000
|
||||
#scgi_local = /home/user/rtorrent/rpc.socket
|
||||
#schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",/home/user/rtorrent/rpc.socket"
|
||||
#network.scgi.open_port = "127.0.0.1:5000"
|
||||
#network.scgi.open_local = (cat,(session.path),/rpc.sock)
|
||||
#schedule2 = socket_chmod, 0, 0, "execute.nothrow=chmod,770,(cat,(session.path),/rpc.sock)"
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#############################################################################
|
||||
# A minimal rTorrent configuration that provides the basic features
|
||||
# you want to have in addition to the built-in defaults.
|
||||
#
|
||||
# See https://github.com/rakshasa/rtorrent/wiki/CONFIG-Template
|
||||
# for an up-to-date version.
|
||||
#############################################################################
|
||||
|
||||
# Instance layout (base paths)
|
||||
method.insert = cfg.basedir, private|const|string, (cat,"/home/USERNAME/rtorrent/")
|
||||
method.insert = cfg.download, private|const|string, (cat,(cfg.basedir),"download/")
|
||||
method.insert = cfg.logs, private|const|string, (cat,(cfg.basedir),"log/")
|
||||
method.insert = cfg.logfile, private|const|string, (cat,(cfg.logs),"rtorrent-",(system.time),".log")
|
||||
method.insert = cfg.session, private|const|string, (cat,(cfg.basedir),".session/")
|
||||
method.insert = cfg.watch, private|const|string, (cat,(cfg.basedir),"watch/")
|
||||
|
||||
# Create instance directories
|
||||
execute.throw = sh, -c, (cat,\
|
||||
"mkdir -p \"",(cfg.download),"\" ",\
|
||||
"\"",(cfg.logs),"\" ",\
|
||||
"\"",(cfg.session),"\" ",\
|
||||
"\"",(cfg.watch),"/load\" ",\
|
||||
"\"",(cfg.watch),"/start\" ")
|
||||
|
||||
# Listening port for incoming peer traffic (fixed; you can also randomize it)
|
||||
network.port_range.set = 50000-50000
|
||||
network.port_random.set = no
|
||||
|
||||
# Tracker-less torrent and UDP tracker support
|
||||
# (conservative settings for 'private' trackers, change for 'public')
|
||||
dht.mode.set = disable
|
||||
protocol.pex.set = no
|
||||
trackers.use_udp.set = no
|
||||
|
||||
# Peer settings
|
||||
throttle.max_uploads.set = 100
|
||||
throttle.max_uploads.global.set = 250
|
||||
|
||||
throttle.min_peers.normal.set = 20
|
||||
throttle.max_peers.normal.set = 60
|
||||
throttle.min_peers.seed.set = 30
|
||||
throttle.max_peers.seed.set = 80
|
||||
trackers.numwant.set = 80
|
||||
|
||||
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
|
||||
|
||||
# Limits for file handle resources, this is optimized for
|
||||
# an `ulimit` of 1024 (a common default). You MUST leave
|
||||
# a ceiling of handles reserved for rTorrent's internal needs!
|
||||
network.http.max_open.set = 50
|
||||
network.max_open_files.set = 600
|
||||
network.max_open_sockets.set = 300
|
||||
|
||||
# Memory resource usage (increase if you have a large number of items loaded,
|
||||
# and/or the available resources to spend)
|
||||
pieces.memory.max.set = 1800M
|
||||
network.xmlrpc.size_limit.set = 4M
|
||||
|
||||
# Basic operational settings (no need to change these)
|
||||
session.path.set = (cat, (cfg.session))
|
||||
directory.default.set = (cat, (cfg.download))
|
||||
log.execute = (cat, (cfg.logs), "execute.log")
|
||||
##log.xmlrpc = (cat, (cfg.logs), "xmlrpc.log")
|
||||
execute.nothrow = sh, -c, (cat, "echo >",\
|
||||
(session.path), "rtorrent.pid", " ", (system.pid))
|
||||
|
||||
# Other operational settings (check & adapt)
|
||||
encoding.add = utf8
|
||||
system.umask.set = 0027
|
||||
system.cwd.set = (directory.default)
|
||||
network.http.dns_cache_timeout.set = 25
|
||||
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
|
||||
##pieces.hash.on_completion.set = no
|
||||
##view.sort_current = seeding, greater=d.ratio=
|
||||
##keys.layout.set = qwerty
|
||||
##network.http.capath.set = "/etc/ssl/certs"
|
||||
##network.http.ssl_verify_peer.set = 0
|
||||
##network.http.ssl_verify_host.set = 0
|
||||
|
||||
# Some additional values and commands
|
||||
method.insert = system.startup_time, value|const, (system.time)
|
||||
method.insert = d.data_path, simple,\
|
||||
"if=(d.is_multi_file),\
|
||||
(cat, (d.directory), /),\
|
||||
(cat, (d.directory), /, (d.name))"
|
||||
method.insert = d.session_file, simple, "cat=(session.path), (d.hash), .torrent"
|
||||
|
||||
# Watch directories (add more as you like, but use unique schedule names)
|
||||
schedule2 = watch_start, 10, 10, ((load.start_verbose, (cat, (cfg.watch), "start/*.torrent")))
|
||||
schedule2 = watch_load, 11, 10, ((load.verbose, (cat, (cfg.watch), "load/*.torrent")))
|
||||
|
||||
# Run the rTorrent process as a daemon in the background
|
||||
# (and control via XMLRPC sockets)
|
||||
#system.daemon.set = true
|
||||
#network.scgi.open_local = (cat,(session.path),rtorrent.sock)
|
||||
#execute.nothrow = chmod,770,(cat,(session.path),rtorrent.sock)
|
||||
|
||||
# Logging:
|
||||
# Levels = critical error warn notice info debug
|
||||
# Groups = connection_* dht_* peer_* rpc_* storage_* thread_* tracker_* torrent_*
|
||||
print = (cat, "Logging to ", (cfg.logfile))
|
||||
log.open_file = "log", (cfg.logfile)
|
||||
log.add_output = "info", "log"
|
||||
##log.add_output = "tracker_debug", "log"
|
||||
|
||||
### END of rtorrent.rc ###
|
||||
@@ -130,7 +130,7 @@ d_stop() {
|
||||
}
|
||||
|
||||
getsession() {
|
||||
session=$(cat "$1" | grep "^[[:space:]]*session[[:space:]]*=" | sed "s/^[[:space:]]*session[[:space:]]*=[[:space:]]*//" )
|
||||
session=$(cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" )
|
||||
#session=${session/#~/`getent passwd ${user}|cut -d: -f6`}
|
||||
echo $session
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ d_stop() {
|
||||
}
|
||||
|
||||
getsession() {
|
||||
session=`cat "$1" | grep "^[[:space:]]*session[[:space:]]*=" | sed "s/^[[:space:]]*session[[:space:]]*=[[:space:]]*//" `
|
||||
session=`cat "$1" | grep "^[[:space:]]*session.path.set[[:space:]]*=" | sed "s/^[[:space:]]*session.path.set[[:space:]]*=[[:space:]]*//" `
|
||||
echo $session
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,26 @@ inline int popcount_wrapper(T t) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get the median of an unordered set of numbers of arbitrary
|
||||
// type by modifing the underlying dataset
|
||||
template <typename T = double, typename _InputIter>
|
||||
T median(_InputIter __first, _InputIter __last) {
|
||||
T __med;
|
||||
|
||||
unsigned int __size = __last - __first;
|
||||
unsigned int __middle = __size / 2;
|
||||
_InputIter __target1 = __first + __middle;
|
||||
std::nth_element(__first, __target1, __last);
|
||||
__med = *__target1;
|
||||
|
||||
if (__size % 2 == 0) {
|
||||
_InputIter __target2 = std::max_element(__first, __target1);
|
||||
__med = (__med + *__target2) / 2.0;
|
||||
}
|
||||
|
||||
return __med;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+54
-3
@@ -39,9 +39,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <locale>
|
||||
#include <random>
|
||||
|
||||
|
||||
namespace rak {
|
||||
|
||||
@@ -312,11 +316,13 @@ transform_hex_str(const Sequence& seq) {
|
||||
template <typename Sequence>
|
||||
Sequence
|
||||
generate_random(size_t length) {
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
|
||||
bytes_randomizer bytes(mt);
|
||||
Sequence s;
|
||||
s.reserve(length);
|
||||
|
||||
std::generate_n(std::back_inserter(s), length, &::random);
|
||||
|
||||
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -371,6 +377,51 @@ is_all_name(const Sequence& src) {
|
||||
return is_all_name(src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
std::string
|
||||
sanitize(Iterator first, Iterator last) {
|
||||
std::string dest;
|
||||
for (; first != last; ++first) {
|
||||
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
|
||||
dest += *first;
|
||||
else
|
||||
dest += " ";
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
std::string
|
||||
sanitize(const Sequence& src) {
|
||||
return trim(sanitize(src.begin(), src.end()));
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
std::string striptags(Iterator first, Iterator last) {
|
||||
bool copychar = true;
|
||||
std::string dest;
|
||||
|
||||
for (; first != last; ++first) {
|
||||
if (std::isprint(*first) && *first == '<') {
|
||||
copychar = false;
|
||||
} else if (std::isprint(*first) && *first == '>') {
|
||||
copychar = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (copychar)
|
||||
dest += *first;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <typename Sequence>
|
||||
std::string striptags(const Sequence& src) {
|
||||
return striptags(src.begin(), src.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+87
-55
@@ -12,7 +12,9 @@
|
||||
# present, along with the associated header file. The NcursesW
|
||||
# (wide-character) library is searched for first, followed by Ncurses,
|
||||
# then the system-default plain Curses. The first library found is the
|
||||
# one returned.
|
||||
# one returned. Finding libraries will first be attempted by using
|
||||
# pkg-config, and should the pkg-config files not be available, will
|
||||
# fallback to combinations of known flags itself.
|
||||
#
|
||||
# The following options are understood: --with-ncursesw, --with-ncurses,
|
||||
# --without-ncursesw, --without-ncurses. The "--with" options force the
|
||||
@@ -52,23 +54,25 @@
|
||||
#
|
||||
# (These preprocessor symbols are discussed later in this document.)
|
||||
#
|
||||
# The following output variable is defined by this macro; it is precious
|
||||
# and may be overridden on the ./configure command line:
|
||||
# The following output variables are defined by this macro; they are
|
||||
# precious and may be overridden on the ./configure command line:
|
||||
#
|
||||
# CURSES_LIB - library to add to xxx_LDADD
|
||||
# CURSES_CPPFLAGS - include paths to add to xxx_CPPFLAGS
|
||||
#
|
||||
# The library listed in CURSES_LIB is NOT added to LIBS by default. You
|
||||
# need to add CURSES_LIB to the appropriate xxx_LDADD line in your
|
||||
# Makefile.am. For example:
|
||||
# Neither the library listed in CURSES_LIB, nor the flags in
|
||||
# CURSES_CPPFLAGS are added to LIBS, respectively CPPFLAGS, by default.
|
||||
# You need to add both to the appropriate xxx_LDADD/xxx_CPPFLAGS line in
|
||||
# your Makefile.am. For example:
|
||||
#
|
||||
# prog_LDADD = @CURSES_LIB@
|
||||
# prog_CPPFLAGS = @CURSES_CPPFLAGS@
|
||||
#
|
||||
# If CURSES_LIB is set on the configure command line (such as by running
|
||||
# "./configure CURSES_LIB=-lmycurses"), then the only header searched for
|
||||
# is <curses.h>. The user may use the CPPFLAGS precious variable to
|
||||
# override the standard #include search path. If the user needs to
|
||||
# specify an alternative path for a library (such as for a non-standard
|
||||
# NcurseW), the user should use the LDFLAGS variable.
|
||||
# is <curses.h>. If the user needs to specify an alternative path for a
|
||||
# library (such as for a non-standard NcurseW), the user should use the
|
||||
# LDFLAGS variable.
|
||||
#
|
||||
# The following shell variables may be defined by this macro:
|
||||
#
|
||||
@@ -88,7 +92,7 @@
|
||||
#
|
||||
# AX_WITH_CURSES
|
||||
# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
# AX_MSG_ERROR([requires either NcursesW or Ncurses library])
|
||||
# AC_MSG_ERROR([requires either NcursesW or Ncurses library])
|
||||
# fi
|
||||
#
|
||||
# If any Curses library will do (but one must be present and must support
|
||||
@@ -182,33 +186,85 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 13
|
||||
#serial 16
|
||||
|
||||
# internal function to factorize common code that is used by both ncurses
|
||||
# and ncursesw
|
||||
AC_DEFUN([_FIND_CURSES_FLAGS], [
|
||||
AC_MSG_CHECKING([for $1 via pkg-config])
|
||||
|
||||
_PKG_CONFIG([_ax_cv_$1_lib], [libs], [$1])
|
||||
_PKG_CONFIG([_ax_cv_$1_cppflags], [cflags], [$1])
|
||||
|
||||
AS_IF([test "x$pkg_failed" = "xyes" || test "x$pkg_failed" = "xuntried"],[
|
||||
AC_MSG_RESULT([no])
|
||||
# No suitable .pc file found, have to find flags via fallback
|
||||
AC_CACHE_CHECK([for $1 via fallback], [ax_cv_$1], [
|
||||
AS_ECHO()
|
||||
pkg_cv__ax_cv_$1_lib="-l$1"
|
||||
pkg_cv__ax_cv_$1_cppflags="$CURSES_CPPFLAGS"
|
||||
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
|
||||
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
|
||||
|
||||
AC_MSG_CHECKING([for initscr() with $pkg_cv__ax_cv_$1_lib])
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_lib])
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
|
||||
ax_cv_$1=yes
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
m4_if(
|
||||
[$1],[ncursesw],[pkg_cv__ax_cv_$1_lib="$pkg_cv__ax_cv_$1_lib -ltinfow"],
|
||||
[$1],[ncurses],[pkg_cv__ax_cv_$1_lib="$pkg_cv__ax_cv_$1_lib -ltinfo"]
|
||||
)
|
||||
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
|
||||
|
||||
AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_lib])
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[
|
||||
ax_cv_$1=yes
|
||||
],[
|
||||
ax_cv_$1=no
|
||||
])
|
||||
])
|
||||
],[
|
||||
ax_cv_$1=no
|
||||
])
|
||||
])
|
||||
],[
|
||||
AC_MSG_RESULT([yes])
|
||||
# Found .pc file, using its information
|
||||
LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_lib"
|
||||
CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags"
|
||||
ax_cv_$1=yes
|
||||
])
|
||||
])
|
||||
|
||||
AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES])
|
||||
AC_DEFUN([AX_WITH_CURSES], [
|
||||
AC_ARG_VAR([CURSES_LIB], [linker library for Curses, e.g. -lcurses])
|
||||
AC_ARG_VAR([CURSES_CPPFLAGS], [preprocessor flags for Curses, e.g. -I/usr/include/ncursesw])
|
||||
AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses],
|
||||
[force the use of Ncurses or NcursesW])],
|
||||
[], [with_ncurses=check])
|
||||
AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw],
|
||||
[do not use NcursesW (wide character support)])],
|
||||
[], [with_ncursesw=check])
|
||||
AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
|
||||
ax_saved_LIBS=$LIBS
|
||||
ax_saved_CPPFLAGS=$CPPFLAGS
|
||||
|
||||
AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes],
|
||||
[ax_with_plaincurses=no], [ax_with_plaincurses=check])
|
||||
|
||||
ax_cv_curses_which=no
|
||||
|
||||
# Test for NcursesW
|
||||
|
||||
AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncursesw" != xno], [
|
||||
LIBS="$ax_saved_LIBS -lncursesw"
|
||||
_FIND_CURSES_FLAGS([ncursesw])
|
||||
|
||||
AC_CACHE_CHECK([for NcursesW wide-character library], [ax_cv_ncursesw], [
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
|
||||
[ax_cv_ncursesw=yes], [ax_cv_ncursesw=no])
|
||||
])
|
||||
AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [
|
||||
AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library])
|
||||
])
|
||||
@@ -216,7 +272,8 @@ AC_DEFUN([AX_WITH_CURSES], [
|
||||
AS_IF([test "x$ax_cv_ncursesw" = xyes], [
|
||||
ax_cv_curses=yes
|
||||
ax_cv_curses_which=ncursesw
|
||||
CURSES_LIB="-lncursesw"
|
||||
CURSES_LIB="$pkg_cv__ax_cv_ncursesw_lib"
|
||||
CURSES_CPPFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags"
|
||||
AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present])
|
||||
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
|
||||
|
||||
@@ -316,46 +373,15 @@ AC_DEFUN([AX_WITH_CURSES], [
|
||||
AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [
|
||||
AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h])
|
||||
])
|
||||
|
||||
dnl Test if we need to explicitly link against -ltinfow.
|
||||
AC_CACHE_CHECK([if curses tinfo library is linked properly], [ax_cv_ncurses_compiled], [
|
||||
LIBS="$ax_saved_LIBS $CURSES_LIB"
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_ncurses_compiled=yes], [ax_cv_ncurses_compiled=no])
|
||||
|
||||
LIBS="$ax_saved_LIBS $CURSES_LIB -ltinfo"
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_tinfo_compiled=yes], [ax_cv_tinfo_compiled=no])
|
||||
|
||||
LIBS="$ax_saved_LIBS $CURSES_LIB -ltinfow"
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [keypad])], [ax_cv_tinfow_compiled=yes], [ax_cv_tinfow_compiled=no])
|
||||
|
||||
LIBS=$ax_saved_LIBS
|
||||
])
|
||||
|
||||
AS_IF([test "x$ax_cv_ncurses_compiled" = xno], [
|
||||
AS_IF(
|
||||
[test "x$ax_cv_tinfo_compiled" = xyes], [
|
||||
AC_MSG_RESULT([adding libtinfo])
|
||||
CURSES_LIB="$CURSES_LIB -ltinfo"
|
||||
],
|
||||
[test "x$ax_cv_tinfow_compiled" = xyes], [
|
||||
AC_MSG_RESULT([adding libtinfow])
|
||||
CURSES_LIB="$CURSES_LIB -ltinfow"
|
||||
], [
|
||||
AC_MSG_ERROR([no])
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
unset pkg_cv__ax_cv_ncursesw_lib
|
||||
unset pkg_cv__ax_cv_ncursesw_cppflags
|
||||
|
||||
# Test for Ncurses
|
||||
|
||||
AS_IF([test "x$CURSES_LIB" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [
|
||||
LIBS="$ax_saved_LIBS -lncurses"
|
||||
_FIND_CURSES_FLAGS([ncurses])
|
||||
|
||||
AC_CACHE_CHECK([for Ncurses library], [ax_cv_ncurses], [
|
||||
AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])],
|
||||
[ax_cv_ncurses=yes], [ax_cv_ncurses=no])
|
||||
])
|
||||
AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [
|
||||
AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library])
|
||||
])
|
||||
@@ -363,7 +389,8 @@ AC_DEFUN([AX_WITH_CURSES], [
|
||||
AS_IF([test "x$ax_cv_ncurses" = xyes], [
|
||||
ax_cv_curses=yes
|
||||
ax_cv_curses_which=ncurses
|
||||
CURSES_LIB="-lncurses"
|
||||
CURSES_LIB="$pkg_cv__ax_cv_ncurses_lib"
|
||||
CURSES_CPPFLAGS="$pkg_cv__ax_cv_ncurses_cppflags"
|
||||
AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present])
|
||||
AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present])
|
||||
|
||||
@@ -418,9 +445,10 @@ AC_DEFUN([AX_WITH_CURSES], [
|
||||
])
|
||||
])
|
||||
])
|
||||
unset pkg_cv__ax_cv_ncurses_lib
|
||||
unset pkg_cv__ax_cv_ncurses_cppflags
|
||||
|
||||
# Test for plain Curses (or if CURSES_LIB was set by user)
|
||||
|
||||
AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [
|
||||
AS_IF([test "x$CURSES_LIB" != x], [
|
||||
LIBS="$ax_saved_LIBS $CURSES_LIB"
|
||||
@@ -543,4 +571,8 @@ AC_DEFUN([AX_WITH_CURSES], [
|
||||
AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no])
|
||||
|
||||
LIBS=$ax_saved_LIBS
|
||||
CPPFLAGS=$ax_saved_CPPFLAGS
|
||||
|
||||
unset ax_saved_LIBS
|
||||
unset ax_saved_CPPFLAGS
|
||||
])dnl
|
||||
|
||||
+3
-3
@@ -96,7 +96,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
|
||||
AC_DEFUN([TORRENT_CHECK_KQUEUE_SOCKET_ONLY], [
|
||||
AC_MSG_CHECKING(whether kqueue supports pipes and ptys)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@@ -174,8 +174,8 @@ AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
|
||||
AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
|
||||
AC_MSG_CHECKING(for fallocate)
|
||||
|
||||
AC_TRY_LINK([#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
AC_TRY_LINK([#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
],[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
|
||||
],
|
||||
[
|
||||
|
||||
+2
-2
@@ -153,7 +153,7 @@ dnl Need to fix this so that it uses the stuff defined by the system.
|
||||
AC_DEFUN([TORRENT_CHECK_EXECINFO], [
|
||||
AC_MSG_CHECKING(for execinfo.h)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <execinfo.h>
|
||||
int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;}
|
||||
])],
|
||||
@@ -168,7 +168,7 @@ AC_DEFUN([TORRENT_CHECK_EXECINFO], [
|
||||
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
|
||||
AC_MSG_CHECKING(the byte alignment)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
#include <inttypes.h>
|
||||
int main() {
|
||||
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
|
||||
|
||||
@@ -274,6 +274,41 @@ retrieve_d_custom_throw(core::Download* download, const std::string& key) {
|
||||
}
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_d_custom_if_z(core::Download* download, const torrent::Object::list_type& args) {
|
||||
torrent::Object::list_const_iterator itr = args.begin();
|
||||
if (itr == args.end())
|
||||
throw torrent::bencode_error("d.custom.if_z: Missing key argument.");
|
||||
const std::string& key = (itr++)->as_string();
|
||||
if (key.empty())
|
||||
throw torrent::bencode_error("d.custom.if_z: Empty key argument.");
|
||||
if (itr == args.end())
|
||||
throw torrent::bencode_error("d.custom.if_z: Missing default argument.");
|
||||
|
||||
try {
|
||||
const std::string& val = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key);
|
||||
return val.empty() ? itr->as_string() : val;
|
||||
} catch (torrent::bencode_error& e) {
|
||||
return itr->as_string();
|
||||
}
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::Object::list_type& args) {
|
||||
if (args.begin() != args.end())
|
||||
throw torrent::bencode_error("d.custom.keys/items takes no arguments.");
|
||||
|
||||
torrent::Object result = keys_only ? torrent::Object::create_list() : torrent::Object::create_map();
|
||||
torrent::Object::map_type& entries = download->bencode()->get_key("rtorrent").get_key("custom").as_map();
|
||||
|
||||
for (torrent::Object::map_type::const_iterator itr = entries.begin(), last = entries.end(); itr != last; itr++) {
|
||||
if (keys_only) result.as_list().push_back(itr->first);
|
||||
else result.as_map()[itr->first] = itr->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
retrieve_d_bitfield(core::Download* download) {
|
||||
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
|
||||
@@ -710,6 +745,9 @@ initialize_command_download() {
|
||||
CMD2_DL_STRING("d.custom", std::bind(&retrieve_d_custom, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_STRING("d.custom_throw", std::bind(&retrieve_d_custom_throw, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_LIST ("d.custom.set", std::bind(&apply_d_custom, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_LIST ("d.custom.if_z", std::bind(&retrieve_d_custom_if_z, std::placeholders::_1, std::placeholders::_2));
|
||||
CMD2_DL_LIST ("d.custom.keys", std::bind(&retrieve_d_custom_map, std::placeholders::_1, true, std::placeholders::_2));
|
||||
CMD2_DL_LIST ("d.custom.items", std::bind(&retrieve_d_custom_map, std::placeholders::_1, false, std::placeholders::_2));
|
||||
|
||||
CMD2_DL_VAR_STRING_PUBLIC("d.custom1", "rtorrent", "custom1");
|
||||
CMD2_DL_VAR_STRING_PUBLIC("d.custom2", "rtorrent", "custom2");
|
||||
@@ -835,6 +873,7 @@ initialize_command_download() {
|
||||
CMD2_DL ("d.wanted_chunks", CMD2_ON_DATA(wanted_chunks));
|
||||
|
||||
CMD2_DL_V ("d.tracker_announce", std::bind(&torrent::Download::manual_request, CMD2_BIND_DL, false));
|
||||
CMD2_DL_V ("d.tracker_announce.force", std::bind(&torrent::Download::manual_request, CMD2_BIND_DL, true));
|
||||
CMD2_DL ("d.tracker_numwant", std::bind(&torrent::TrackerList::numwant, CMD2_BIND_TL));
|
||||
CMD2_DL_VALUE_V ("d.tracker_numwant.set", std::bind(&torrent::TrackerList::set_numwant, CMD2_BIND_TL, std::placeholders::_2));
|
||||
// TODO: Deprecate 'd.tracker_focus'.
|
||||
|
||||
+23
-5
@@ -139,6 +139,8 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
case rpc::object_storage::flag_function_type:
|
||||
system_method_generate_command2(&value, itrArgs, args.end());
|
||||
break;
|
||||
case rpc::object_storage::flag_list_type:
|
||||
break;
|
||||
case rpc::object_storage::flag_multi_type:
|
||||
break;
|
||||
default:
|
||||
@@ -152,7 +154,17 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
if (!(flags & rpc::object_storage::flag_private))
|
||||
cmd_flags |= rpc::CommandMap::flag_public_xmlrpc;
|
||||
|
||||
control->object_storage()->insert_str(rawKey, value, flags);
|
||||
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {
|
||||
torrent::Object valueList = torrent::Object::create_list();
|
||||
torrent::Object::list_type& valueListType = valueList.as_list();
|
||||
|
||||
if ((itrArgs)->is_list())
|
||||
valueListType = (itrArgs)->as_list();
|
||||
|
||||
control->object_storage()->insert_str(rawKey, valueList, flags);
|
||||
} else {
|
||||
control->object_storage()->insert_str(rawKey, value, flags);
|
||||
}
|
||||
|
||||
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_function_type ||
|
||||
(flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_multi_type) {
|
||||
@@ -208,6 +220,13 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
&rpc::command_base_call_string<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_list_type:
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type> >::type>
|
||||
(create_new_key<5>(rawKey, ".set"),
|
||||
std::bind(&rpc::object_storage::set_str_list, control->object_storage(), rawKey, std::placeholders::_2),
|
||||
&rpc::command_base_call_list<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_function_type:
|
||||
case rpc::object_storage::flag_multi_type:
|
||||
default: break;
|
||||
@@ -310,10 +329,6 @@ system_method_insert(const torrent::Object::list_type& args) {
|
||||
new_flags = rpc::object_storage::flag_string_type;
|
||||
else if (options.find("list") != std::string::npos)
|
||||
new_flags = rpc::object_storage::flag_list_type;
|
||||
else if (options.find("simple") != std::string::npos)
|
||||
new_flags = rpc::object_storage::flag_function_type;
|
||||
else
|
||||
throw torrent::input_error("No support for 'list' variable type.");
|
||||
|
||||
if (options.find("static") != std::string::npos)
|
||||
new_flags |= rpc::object_storage::flag_static;
|
||||
@@ -447,6 +462,9 @@ initialize_command_dynamic() {
|
||||
|
||||
CMD2_ANY_LIST ("method.insert", std::bind(&system_method_insert, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("method.insert.value", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_value_type));
|
||||
CMD2_ANY_LIST ("method.insert.bool", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_bool_type));
|
||||
CMD2_ANY_LIST ("method.insert.string", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_string_type));
|
||||
CMD2_ANY_LIST ("method.insert.list", std::bind(&system_method_insert_object, std::placeholders::_2, rpc::object_storage::flag_list_type));
|
||||
|
||||
CMD2_METHOD_INSERT("method.insert.simple", rpc::object_storage::flag_function_type);
|
||||
CMD2_METHOD_INSERT("method.insert.c_simple", rpc::object_storage::flag_constant | rpc::object_storage::flag_function_type);
|
||||
|
||||
@@ -307,6 +307,42 @@ d_multicall(const torrent::Object::list_type& args) {
|
||||
return resultRaw;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
d_multicall_filtered(const torrent::Object::list_type& args) {
|
||||
if (args.size() < 2)
|
||||
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
|
||||
torrent::Object::list_const_iterator arg = args.begin();
|
||||
|
||||
// Find the given view
|
||||
core::ViewManager* viewManager = control->view_manager();
|
||||
core::ViewManager::iterator viewItr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
|
||||
|
||||
if (viewItr == viewManager->end())
|
||||
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
|
||||
|
||||
// Make a filtered copy of the current item list
|
||||
core::View::base_type dlist;
|
||||
(*viewItr)->filter_by(*++arg, dlist);
|
||||
|
||||
// Generate result by iterating over all items
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
++arg; // skip to first command
|
||||
|
||||
for (core::View::iterator item = dlist.begin(); item != dlist.end(); ++item) {
|
||||
// Add empty row to result
|
||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||
|
||||
// Call the provided commands and assemble their results
|
||||
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
|
||||
const std::string& cmdstr = command->as_string();
|
||||
row.push_back(rpc::parse_command(rpc::make_target(*item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
|
||||
}
|
||||
}
|
||||
|
||||
return resultRaw;
|
||||
}
|
||||
|
||||
static void
|
||||
call_watch_command(const std::string& command, const std::string& path) {
|
||||
rpc::commands.call_catch(command.c_str(), rpc::make_target(), path);
|
||||
@@ -359,6 +395,7 @@ initialize_command_events() {
|
||||
|
||||
CMD2_ANY_LIST ("download_list", std::bind(&apply_download_list, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("d.multicall2", std::bind(&d_multicall, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2));
|
||||
}
|
||||
|
||||
+3
-37
@@ -1,39 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// 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)
|
||||
// with this exception, 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.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
@@ -167,7 +131,9 @@ initialize_command_logging() {
|
||||
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
|
||||
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
|
||||
|
||||
@@ -167,8 +167,7 @@ apply_scgi(const std::string& arg, int type) {
|
||||
sa.sa_inet()->clear();
|
||||
saPtr = &sa;
|
||||
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS,
|
||||
"The SCGI socket has not been bound to any address and likely poses a security risk.");
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is open to any address and is a security risk");
|
||||
|
||||
} else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
|
||||
std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
|
||||
@@ -177,8 +176,7 @@ apply_scgi(const std::string& arg, int type) {
|
||||
|
||||
saPtr = ai->address();
|
||||
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS,
|
||||
"The SCGI socket is bound to a specific network device yet may still pose a security risk, consider using 'scgi_local'.");
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
|
||||
|
||||
} else {
|
||||
throw torrent::input_error("Could not parse address.");
|
||||
|
||||
+286
-2
@@ -39,8 +39,12 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <regex>
|
||||
|
||||
#include <rak/algorithm.h>
|
||||
#include <rak/functional.h>
|
||||
#include <rak/functional_fun.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "core/manager.h"
|
||||
#include "core/view_manager.h"
|
||||
@@ -147,6 +151,41 @@ apply_cat(rpc::target_type target, const torrent::Object& rawArgs) {
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_value(rpc::target_type target, const torrent::Object::list_type& args) {
|
||||
if (args.size() < 1)
|
||||
throw torrent::input_error("'value' takes at least a number argument!");
|
||||
if (args.size() > 2)
|
||||
throw torrent::input_error("'value' takes at most two arguments!");
|
||||
|
||||
torrent::Object::value_type val = 0;
|
||||
if (args.front().is_value()) {
|
||||
val = args.front().as_value();
|
||||
} else {
|
||||
int base = args.size() > 1 ? args.back().is_value() ?
|
||||
args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10;
|
||||
char* endptr = 0;
|
||||
|
||||
val = strtoll(args.front().as_string().c_str(), &endptr, base);
|
||||
while (*endptr == ' ' || *endptr == '\n') ++endptr;
|
||||
if (*endptr)
|
||||
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_try(rpc::target_type target, const torrent::Object& args) {
|
||||
try {
|
||||
return rpc::call_object(args, target);
|
||||
} catch (torrent::input_error& e) {
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS, "try command caught input_error: %s", e.what());
|
||||
}
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
// Move these boolean operators to a new file.
|
||||
|
||||
inline bool
|
||||
@@ -282,6 +321,103 @@ torrent::Object apply_equal(rpc::target_type target, const torrent::Object::list
|
||||
return result.is_value() ? result.as_value() == 0 : (int64_t)false;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_compare(rpc::target_type target, const torrent::Object::list_type& args) {
|
||||
if (!rpc::is_target_pair(target))
|
||||
throw torrent::input_error("Can only compare a target pair.");
|
||||
|
||||
if (args.size() < 2)
|
||||
throw torrent::input_error("Need at least order and one field.");
|
||||
|
||||
torrent::Object::list_const_iterator itr = args.begin();
|
||||
std::string order = (itr++)->as_string();
|
||||
const char* current = order.c_str();
|
||||
|
||||
torrent::Object result1;
|
||||
torrent::Object result2;
|
||||
|
||||
for (torrent::Object::list_const_iterator last = args.end(); itr != last; itr++) {
|
||||
std::string field = itr->as_string();
|
||||
result1 = rpc::parse_command_single(rpc::get_target_left(target), field);
|
||||
result2 = rpc::parse_command_single(rpc::get_target_right(target), field);
|
||||
|
||||
if (result1.type() != result2.type())
|
||||
throw torrent::input_error(std::string("Type mismatch in compare of ") + field);
|
||||
|
||||
bool descending = *current == 'd' || *current == 'D' || *current == '-';
|
||||
if (*current) {
|
||||
if (!descending && !(*current == 'a' || *current == 'A' || *current == '+'))
|
||||
throw torrent::input_error(std::string("Bad order '") + *current + "' in " + order);
|
||||
++current;
|
||||
}
|
||||
|
||||
switch (result1.type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
if (result1.as_value() != result2.as_value())
|
||||
return (int64_t) (descending ^ (result1.as_value() < result2.as_value()));
|
||||
break;
|
||||
|
||||
case torrent::Object::TYPE_STRING:
|
||||
if (result1.as_string() != result2.as_string())
|
||||
return (int64_t) (descending ^ (result1.as_string() < result2.as_string()));
|
||||
break;
|
||||
|
||||
default:
|
||||
break; // treat unknown types as equal
|
||||
}
|
||||
}
|
||||
|
||||
// if all else is equal, ensure stable sort order based on memory location
|
||||
return (int64_t) (target.second < target.third);
|
||||
}
|
||||
|
||||
// Regexp based 'match' function.
|
||||
// arg1: the text to match.
|
||||
// arg2: the regexp pattern.
|
||||
// eg: match{d.name=,.*linux.*iso}
|
||||
torrent::Object apply_match(rpc::target_type target, const torrent::Object::list_type& args) {
|
||||
if (args.size() != 2)
|
||||
throw torrent::input_error("Wrong argument count for 'match': 2 arguments needed.");
|
||||
|
||||
// This really should be converted to using args flagged as
|
||||
// commands, so that we can compare commands and statics values.
|
||||
|
||||
torrent::Object result1;
|
||||
torrent::Object result2;
|
||||
|
||||
rpc::target_type target1 = rpc::is_target_pair(target) ? rpc::get_target_left(target) : target;
|
||||
rpc::target_type target2 = rpc::is_target_pair(target) ? rpc::get_target_right(target) : target;
|
||||
|
||||
if (args.front().is_dict_key())
|
||||
result1 = rpc::commands.call_command(args.front().as_dict_key().c_str(), args.front().as_dict_obj(), target1);
|
||||
else
|
||||
result1 = rpc::parse_command_single(target1, args.front().as_string());
|
||||
|
||||
if (args.back().is_dict_key())
|
||||
result2 = rpc::commands.call_command(args.back().as_dict_key().c_str(), args.back().as_dict_obj(), target2);
|
||||
else
|
||||
result2 = args.back().as_string();
|
||||
|
||||
if (result1.type() != result2.type())
|
||||
throw torrent::input_error("Type mismatch for 'match' arguments.");
|
||||
|
||||
std::string text = result1.as_string();
|
||||
std::string pattern = result2.as_string();
|
||||
|
||||
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
|
||||
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
|
||||
|
||||
bool isAMatch = false;
|
||||
try {
|
||||
std::regex re(pattern);
|
||||
isAMatch = std::regex_match(text, re);
|
||||
} catch (const std::regex_error& exc) {
|
||||
control->core()->push_log_std("regex_error: " + std::string(exc.what()));
|
||||
}
|
||||
|
||||
return isAMatch ? (int64_t)true : (int64_t)false;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_to_time(const torrent::Object& rawArgs, int flags) {
|
||||
std::tm *u;
|
||||
@@ -519,6 +655,128 @@ apply_elapsed_greater(const torrent::Object::list_type& args) {
|
||||
return (int64_t)(start_time != 0 && rak::timer::current_seconds() - start_time > rpc::convert_to_value(args.back()));
|
||||
}
|
||||
|
||||
inline std::vector<int64_t>
|
||||
as_vector(const torrent::Object::list_type& args) {
|
||||
if (args.size() == 0)
|
||||
throw torrent::input_error("Wrong argument count in as_vector.");
|
||||
|
||||
std::vector<int64_t> result;
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
|
||||
|
||||
if (itr->is_value()) {
|
||||
result.push_back(itr->as_value());
|
||||
} else if (itr->is_string()) {
|
||||
result.push_back(rpc::convert_to_value(itr->as_string()));
|
||||
} else if (itr->is_list()) {
|
||||
std::vector<int64_t> subResult = as_vector(itr->as_list());
|
||||
result.insert(result.end(), subResult.begin(), subResult.end());
|
||||
} else {
|
||||
throw torrent::input_error("Wrong type supplied to as_vector.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t
|
||||
apply_math_basic(const char* name, const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
|
||||
int64_t val = 0, rhs = 0;
|
||||
bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod");
|
||||
|
||||
if (args.size() == 0)
|
||||
throw torrent::input_error(std::string(name) + ": No arguments provided!");
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
|
||||
|
||||
if (itr->is_value()) {
|
||||
rhs = itr->as_value();
|
||||
} else if (itr->is_string()) {
|
||||
rhs = rpc::convert_to_value(itr->as_string());
|
||||
} else if (itr->is_list()) {
|
||||
rhs = apply_math_basic(name, op, itr->as_list());
|
||||
} else {
|
||||
throw torrent::input_error(std::string(name) + ": Wrong argument type");
|
||||
}
|
||||
|
||||
if (divides && !rhs && itr != args.begin())
|
||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||
|
||||
val = itr == args.begin() ? rhs : op(val, rhs);
|
||||
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int64_t
|
||||
apply_arith_basic(const std::function<int64_t(int64_t,int64_t)> op, const torrent::Object::list_type& args) {
|
||||
if (args.size() == 0)
|
||||
throw torrent::input_error("Wrong argument count in apply_arith_basic.");
|
||||
|
||||
int64_t val = 0;
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
|
||||
|
||||
if (itr->is_value()) {
|
||||
val = itr == args.begin() ? itr->as_value() : (op(val, itr->as_value()) ? val : itr->as_value());
|
||||
} else if (itr->is_string()) {
|
||||
int64_t cval = rpc::convert_to_value(itr->as_string());
|
||||
val = itr == args.begin() ? cval : (op(val, cval) ? val : cval);
|
||||
} else if (itr->is_list()) {
|
||||
int64_t fval = apply_arith_basic(op, itr->as_list());
|
||||
val = itr == args.begin() ? fval : (op(val, fval) ? val : fval);
|
||||
} else {
|
||||
throw torrent::input_error("Wrong type supplied to apply_arith_basic.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int64_t
|
||||
apply_arith_count(const torrent::Object::list_type& args) {
|
||||
if (args.size() == 0)
|
||||
throw torrent::input_error("Wrong argument count in apply_arith_count.");
|
||||
|
||||
int64_t val = 0;
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) {
|
||||
|
||||
switch (itr->type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
case torrent::Object::TYPE_STRING:
|
||||
val++;
|
||||
break;
|
||||
case torrent::Object::TYPE_LIST:
|
||||
val += apply_arith_count(itr->as_list());
|
||||
break;
|
||||
default:
|
||||
throw torrent::input_error("Wrong type supplied to apply_arith_count.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int64_t
|
||||
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
|
||||
if (args.size() == 0)
|
||||
throw torrent::input_error("Wrong argument count in apply_arith_other.");
|
||||
|
||||
if (strcmp(op, "average") == 0) {
|
||||
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
|
||||
} else if (strcmp(op, "median") == 0) {
|
||||
std::vector<int64_t> result = as_vector(args);
|
||||
return (int64_t)rak::median(result.begin(), result.end());
|
||||
} else {
|
||||
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
initialize_command_ui() {
|
||||
CMD2_VAR_STRING("keys.layout", "qwerty");
|
||||
@@ -528,8 +786,11 @@ initialize_command_ui() {
|
||||
CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
|
||||
CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
|
||||
CMD2_VAR_STRING("view.filter.temp.excluded", "default,started,stopped");
|
||||
CMD2_VAR_BOOL ("view.filter.temp.log", 0);
|
||||
|
||||
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
|
||||
@@ -555,12 +816,22 @@ initialize_command_ui() {
|
||||
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
|
||||
CMD2_ANY_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
|
||||
CMD2_ANY_VALUE_V("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
|
||||
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
|
||||
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
|
||||
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
|
||||
|
||||
// TODO: Add 'option_string' for rtorrent-specific options.
|
||||
CMD2_VAR_STRING("ui.torrent_list.layout", "full");
|
||||
|
||||
// Move.
|
||||
CMD2_ANY("print", &apply_print);
|
||||
CMD2_ANY("cat", &apply_cat);
|
||||
CMD2_ANY_LIST("value", &apply_value);
|
||||
CMD2_ANY("try", &apply_try);
|
||||
CMD2_ANY("if", std::bind(&apply_if, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD2_ANY("not", &apply_not);
|
||||
CMD2_ANY("false", &apply_false);
|
||||
@@ -574,6 +845,8 @@ initialize_command_ui() {
|
||||
CMD2_ANY_LIST("less", &apply_less);
|
||||
CMD2_ANY_LIST("greater", &apply_greater);
|
||||
CMD2_ANY_LIST("equal", &apply_equal);
|
||||
CMD2_ANY_LIST("compare", &apply_compare);
|
||||
CMD2_ANY_LIST("match", &apply_match);
|
||||
|
||||
CMD2_ANY_VALUE("convert.gm_time", std::bind(&apply_to_time, std::placeholders::_2, 0));
|
||||
CMD2_ANY_VALUE("convert.gm_date", std::bind(&apply_to_time, std::placeholders::_2, 0x2));
|
||||
@@ -585,6 +858,17 @@ initialize_command_ui() {
|
||||
CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2));
|
||||
CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater<int64_t>(), std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2));
|
||||
CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2));
|
||||
}
|
||||
|
||||
@@ -168,6 +168,8 @@ Control::is_shutdown_completed() {
|
||||
|
||||
void
|
||||
Control::handle_shutdown() {
|
||||
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
|
||||
|
||||
if (!m_shutdownQuick) {
|
||||
// Temporary hack:
|
||||
if (worker_thread->is_active())
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "download.h"
|
||||
#include "download_list.h"
|
||||
#include "download_store.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
#define DL_TRIGGER_EVENT(download, event_name) \
|
||||
rpc::commands.call_catch(event_name, rpc::make_target(download), torrent::Object(), "Event '" event_name "' failed: ");
|
||||
@@ -93,6 +94,7 @@ DownloadList::session_save() {
|
||||
lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
|
||||
|
||||
control->dht_manager()->save_dht_cache();
|
||||
control->ui()->save_input_history();
|
||||
}
|
||||
|
||||
DownloadList::iterator
|
||||
|
||||
+24
-10
@@ -89,17 +89,21 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
|
||||
};
|
||||
|
||||
struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
view_downloads_filter(const torrent::Object& cmd) : m_command(cmd) {}
|
||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) : m_command(cmd), m_command2(cmd2) {}
|
||||
|
||||
bool operator () (Download* d1) const {
|
||||
if (m_command.is_empty())
|
||||
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
||||
}
|
||||
|
||||
bool evalCmd(const torrent::Object& cmd, Download* d1) const {
|
||||
if (cmd.is_empty())
|
||||
return true;
|
||||
|
||||
try {
|
||||
torrent::Object result;
|
||||
|
||||
if (m_command.is_dict_key()) {
|
||||
// torrent::Object tmp_command = m_command;
|
||||
if (cmd.is_dict_key()) {
|
||||
// torrent::Object tmp_command = cmd;
|
||||
|
||||
// uint32_t flags = tmp_command.flags() & torrent::Object::mask_function;
|
||||
// tmp_command.unset_flags(torrent::Object::mask_function);
|
||||
@@ -109,10 +113,10 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
// result = rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
|
||||
// rpc::make_target(d1));
|
||||
|
||||
result = rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(), rpc::make_target(d1));
|
||||
result = rpc::commands.call_command(cmd.as_dict_key().c_str(), cmd.as_dict_obj(), rpc::make_target(d1));
|
||||
|
||||
} else {
|
||||
result = rpc::parse_command_single(rpc::make_target(d1), m_command.as_string());
|
||||
result = rpc::parse_command_single(rpc::make_target(d1), cmd.as_string());
|
||||
}
|
||||
|
||||
switch (result.type()) {
|
||||
@@ -136,6 +140,7 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
}
|
||||
|
||||
const torrent::Object& m_command;
|
||||
const torrent::Object& m_command2;
|
||||
};
|
||||
|
||||
void
|
||||
@@ -262,8 +267,8 @@ View::filter() {
|
||||
return;
|
||||
|
||||
// Parition the list in two steps so we know which elements changed.
|
||||
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter));
|
||||
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter));
|
||||
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
|
||||
base_type changed(splitVisible, splitFiltered);
|
||||
iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
|
||||
@@ -295,6 +300,16 @@ View::filter() {
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void
|
||||
View::filter_by(const torrent::Object& condition, View::base_type& result) {
|
||||
// std::copy_if(begin_visible(), end_visible(), result.begin(), view_downloads_filter(condition));
|
||||
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
|
||||
|
||||
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
|
||||
if (matches(*itr))
|
||||
result.push_back(*itr);
|
||||
}
|
||||
|
||||
void
|
||||
View::filter_download(core::Download* download) {
|
||||
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||
@@ -302,8 +317,7 @@ View::filter_download(core::Download* download) {
|
||||
if (itr == base_type::end())
|
||||
throw torrent::internal_error("View::filter_download(...) could not find download.");
|
||||
|
||||
if (view_downloads_filter(m_filter)(download)) {
|
||||
|
||||
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
|
||||
if (itr >= end_visible()) {
|
||||
erase_internal(itr);
|
||||
insert_visible(download);
|
||||
|
||||
+5
-1
@@ -120,10 +120,13 @@ public:
|
||||
|
||||
// Need to explicity trigger filtering.
|
||||
void filter();
|
||||
void filter_by(const torrent::Object& condition, base_type& result);
|
||||
void filter_download(core::Download* download);
|
||||
|
||||
const torrent::Object& get_filter() const { return m_filter; }
|
||||
void set_filter(const torrent::Object& s) { m_filter = s; }
|
||||
void set_filter(const torrent::Object& s) { m_filter = s; }
|
||||
const torrent::Object& get_filter_temp() const { return m_temp_filter; }
|
||||
void set_filter_temp(const torrent::Object& s) { m_temp_filter = s; }
|
||||
void set_filter_on_event(const std::string& event);
|
||||
|
||||
void clear_filter_on();
|
||||
@@ -172,6 +175,7 @@ private:
|
||||
torrent::Object m_sortCurrent;
|
||||
|
||||
torrent::Object m_filter;
|
||||
torrent::Object m_temp_filter; // Temporary view filter (eg: name based filter)
|
||||
|
||||
torrent::Object m_event_added;
|
||||
torrent::Object m_event_removed;
|
||||
|
||||
@@ -109,6 +109,14 @@ ViewManager::set_filter(const std::string& name, const torrent::Object& cmd) {
|
||||
(*viewItr)->filter();
|
||||
}
|
||||
|
||||
void
|
||||
ViewManager::set_filter_temp(const std::string& name, const torrent::Object& cmd) {
|
||||
iterator viewItr = find_throw(name);
|
||||
|
||||
(*viewItr)->set_filter_temp(cmd);
|
||||
(*viewItr)->filter();
|
||||
}
|
||||
|
||||
void
|
||||
ViewManager::set_filter_on(const std::string& name, const filter_args& args) {
|
||||
iterator viewItr = find_throw(name);
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
void set_sort_current(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_sort_current(cmd); }
|
||||
|
||||
void set_filter(const std::string& name, const torrent::Object& cmd);
|
||||
void set_filter_temp(const std::string& name, const torrent::Object& cmd);
|
||||
void set_filter_on(const std::string& name, const filter_args& args);
|
||||
|
||||
void set_event_added(const std::string& name, const torrent::Object& cmd) { (*find_throw(name))->set_event_added(cmd); }
|
||||
|
||||
@@ -1,39 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2011, Jari Sundell
|
||||
//
|
||||
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// 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)
|
||||
// with this exception, 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.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_DISPLAY_ATTRIBUTES_H
|
||||
#define RTORRENT_DISPLAY_ATTRIBUTES_H
|
||||
|
||||
|
||||
@@ -81,11 +81,20 @@ WindowDownloadList::redraw() {
|
||||
if (m_view == NULL)
|
||||
return;
|
||||
|
||||
m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + "]").c_str());
|
||||
m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + (m_view->get_filter_temp().is_empty() ? "" : " (filtered)") + "]").c_str());
|
||||
|
||||
if (m_view->empty_visible() || m_canvas->width() < 5 || m_canvas->height() < 2)
|
||||
return;
|
||||
|
||||
// show "X of Y"
|
||||
if (m_canvas->width() > 16 + 8 + m_view->name().length()) {
|
||||
int item_idx = m_view->focus() - m_view->begin_visible();
|
||||
if (item_idx == int(m_view->size()))
|
||||
m_canvas->print(m_canvas->width() - 16, 0, "[ none of %-5d]", m_view->size());
|
||||
else
|
||||
m_canvas->print(m_canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, m_view->size());
|
||||
}
|
||||
|
||||
int layout_height;
|
||||
const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout");
|
||||
|
||||
|
||||
@@ -47,22 +47,6 @@ TextInput::pressed(int key) {
|
||||
if (m_bindings.pressed(key)) {
|
||||
return true;
|
||||
|
||||
} else if (m_alt) {
|
||||
m_alt = false;
|
||||
|
||||
switch (key) {
|
||||
// case 'b':
|
||||
// Base::insert(m_pos, "M^b");
|
||||
// break;
|
||||
|
||||
// case 'f':
|
||||
// Base::insert(m_pos, "M^f");
|
||||
// break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (key >= 0x20 && key < 0x7F) {
|
||||
Base::insert(m_pos++, 1, key);
|
||||
|
||||
@@ -113,11 +97,6 @@ TextInput::pressed(int key) {
|
||||
Base::erase(m_pos, size()-m_pos);
|
||||
break;
|
||||
|
||||
case 0x1B:
|
||||
m_alt = true;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
using Base::size_type;
|
||||
using Base::npos;
|
||||
|
||||
TextInput() : m_pos(0), m_alt(false) {}
|
||||
TextInput() : m_pos(0) {}
|
||||
virtual ~TextInput() {}
|
||||
|
||||
size_type get_pos() { return m_pos; }
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
|
||||
virtual bool pressed(int key);
|
||||
|
||||
void clear() { m_pos = 0; m_alt = false; Base::clear(); }
|
||||
void clear() { m_pos = 0; Base::clear(); }
|
||||
|
||||
void slot_dirty(slot_void s) { m_slot_dirty = s; }
|
||||
void mark_dirty() { if (m_slot_dirty) m_slot_dirty(); }
|
||||
@@ -74,7 +74,6 @@ public:
|
||||
private:
|
||||
size_type m_pos;
|
||||
|
||||
bool m_alt;
|
||||
slot_void m_slot_dirty;
|
||||
|
||||
Bindings m_bindings;
|
||||
|
||||
+10
@@ -66,6 +66,7 @@
|
||||
#include "display/window.h"
|
||||
#include "display/manager.h"
|
||||
#include "input/bindings.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
#include "rpc/command_scheduler.h"
|
||||
#include "rpc/command_scheduler_item.h"
|
||||
@@ -249,6 +250,12 @@ main(int argc, char** argv) {
|
||||
|
||||
rpc::parse_command_multiple
|
||||
(rpc::make_target(),
|
||||
"method.insert = event.view.hide,multi|rlookup|static\n"
|
||||
"method.insert = event.view.show,multi|rlookup|static\n"
|
||||
|
||||
"method.insert = event.system.startup_done,multi|rlookup|static\n"
|
||||
"method.insert = event.system.shutdown,multi|rlookup|static\n"
|
||||
|
||||
"method.insert = event.download.inserted,multi|rlookup|static\n"
|
||||
"method.insert = event.download.inserted_new,multi|rlookup|static\n"
|
||||
"method.insert = event.download.inserted_session,multi|rlookup|static\n"
|
||||
@@ -460,6 +467,7 @@ main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
control->initialize();
|
||||
control->ui()->load_input_history();
|
||||
|
||||
// Load session torrents and perform scheduled tasks to ensure
|
||||
// session torrents are loaded before arg torrents.
|
||||
@@ -477,6 +485,8 @@ main(int argc, char** argv) {
|
||||
|
||||
worker_thread->start_thread();
|
||||
|
||||
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
|
||||
|
||||
torrent::thread_base::event_loop(torrent::main_thread());
|
||||
|
||||
control->core()->download_list()->session_save();
|
||||
|
||||
@@ -113,14 +113,15 @@ ThreadWorker::change_xmlrpc_log() {
|
||||
if (scgi() == NULL)
|
||||
return;
|
||||
|
||||
if (scgi()->log_fd() != -1)
|
||||
if (scgi()->log_fd() != -1) {
|
||||
::close(scgi()->log_fd());
|
||||
|
||||
if (m_xmlrpcLog.empty()) {
|
||||
scgi()->set_log_fd(-1);
|
||||
control->core()->push_log("Closed XMLRPC log.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_xmlrpcLog.empty())
|
||||
return;
|
||||
|
||||
scgi()->set_log_fd(open(rak::path_expand(m_xmlrpcLog).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644));
|
||||
|
||||
if (scgi()->log_fd() == -1) {
|
||||
|
||||
+13
-12
@@ -54,6 +54,7 @@
|
||||
#include "display/window_download_statusbar.h"
|
||||
|
||||
#include "display/text_element_string.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "control.h"
|
||||
#include "download.h"
|
||||
@@ -395,20 +396,20 @@ Download::bind_keys() {
|
||||
|
||||
const char* keys = control->ui()->get_throttle_keys();
|
||||
|
||||
m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, 1);
|
||||
m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, -1);
|
||||
m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, 1);
|
||||
m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, -1);
|
||||
m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
|
||||
m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, 5);
|
||||
m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, -5);
|
||||
m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, 5);
|
||||
m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, -5);
|
||||
m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
|
||||
m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, 50);
|
||||
m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, -50);
|
||||
m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, 50);
|
||||
m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, -50);
|
||||
m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+56
-2
@@ -36,6 +36,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <rak/functional.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/exceptions.h>
|
||||
@@ -259,6 +261,25 @@ DownloadList::receive_view_input(Input type) {
|
||||
title = "command";
|
||||
break;
|
||||
|
||||
case INPUT_FILTER:
|
||||
{
|
||||
// Do not allow to subfilter the defined excluded views
|
||||
const std::string excluded_views = rpc::call_command_string("view.filter.temp.excluded");
|
||||
std::stringstream ss(excluded_views);
|
||||
std::string view_name_var;
|
||||
|
||||
while(ss.good()) {
|
||||
std::getline(ss, view_name_var, ',');
|
||||
if (current_view()->name() == rak::trim(view_name_var)) {
|
||||
control->core()->push_log_std("View '" + current_view()->name() + "' can't be filtered.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
title = "filter";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw torrent::internal_error("DownloadList::receive_view_input(...) Invalid input type.");
|
||||
}
|
||||
@@ -274,21 +295,31 @@ DownloadList::receive_view_input(Input type) {
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
|
||||
// reset ESC delay for input prompt
|
||||
set_escdelay(0);
|
||||
|
||||
input->bindings()['\n'] = std::bind(&DownloadList::receive_exit_input, this, type);
|
||||
input->bindings()[KEY_ENTER] = std::bind(&DownloadList::receive_exit_input, this, type);
|
||||
input->bindings()['\x07'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE);
|
||||
input->bindings()['\x07'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE); // ^G
|
||||
input->bindings()['\x1B'] = std::bind(&DownloadList::receive_exit_input, this, INPUT_NONE); // ESC , ^[
|
||||
|
||||
control->ui()->enable_input(title, input);
|
||||
control->ui()->enable_input(title, input, type);
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::receive_exit_input(Input type) {
|
||||
// set back ESC delay to default
|
||||
set_escdelay(1000);
|
||||
|
||||
input::TextInput* input = control->ui()->current_input();
|
||||
|
||||
// We should check that this object is the one holding the input.
|
||||
if (input == NULL)
|
||||
return;
|
||||
|
||||
if (type != INPUT_NONE && type != INPUT_EOI)
|
||||
control->ui()->add_to_input_history(type, input->str());
|
||||
|
||||
control->ui()->disable_input();
|
||||
|
||||
try {
|
||||
@@ -322,6 +353,28 @@ DownloadList::receive_exit_input(Input type) {
|
||||
input->str());
|
||||
break;
|
||||
|
||||
case INPUT_FILTER:
|
||||
if (input->str().empty()) {
|
||||
if (rpc::call_command_value("view.filter.temp.log"))
|
||||
control->core()->push_log_std("Clear temporary filter on '" + current_view()->name() + "' view.");
|
||||
current_view()->set_filter_temp(torrent::Object());
|
||||
current_view()->filter();
|
||||
current_view()->sort();
|
||||
} else {
|
||||
std::string pattern = input->str();
|
||||
if (pattern.back() != '$')
|
||||
pattern = pattern + ".*";
|
||||
if (pattern.front() != '^')
|
||||
pattern = ".*" + pattern;
|
||||
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
|
||||
std::string temp_filter = "match={d.name=," + pattern + "}";
|
||||
if (rpc::call_command_value("view.filter.temp.log"))
|
||||
control->core()->push_log_std("Temporary filter on '" + current_view()->name() + "' view: " + pattern);
|
||||
current_view()->set_filter_temp(temp_filter);
|
||||
current_view()->filter();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw torrent::internal_error("DownloadList::receive_exit_input(...) Invalid input type.");
|
||||
}
|
||||
@@ -343,6 +396,7 @@ DownloadList::setup_keys() {
|
||||
m_bindings[KEY_ENTER] = std::bind(&DownloadList::receive_view_input, this, INPUT_LOAD_MODIFIED);
|
||||
m_bindings['\x0F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_CHANGE_DIRECTORY);
|
||||
m_bindings['X' - '@'] = std::bind(&DownloadList::receive_view_input, this, INPUT_COMMAND);
|
||||
m_bindings['F'] = std::bind(&DownloadList::receive_view_input, this, INPUT_FILTER);
|
||||
|
||||
m_uiArray[DISPLAY_LOG]->bindings()[KEY_LEFT] =
|
||||
m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] =
|
||||
|
||||
@@ -86,7 +86,9 @@ public:
|
||||
INPUT_LOAD_DEFAULT,
|
||||
INPUT_LOAD_MODIFIED,
|
||||
INPUT_CHANGE_DIRECTORY,
|
||||
INPUT_COMMAND
|
||||
INPUT_COMMAND,
|
||||
INPUT_FILTER,
|
||||
INPUT_EOI
|
||||
} Input;
|
||||
|
||||
DownloadList();
|
||||
|
||||
@@ -220,7 +220,14 @@ ElementDownloadList::receive_change_view(const std::string& name) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string old_name = view() ? view()->name() : "";
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.hide", rpc::make_target(), name,
|
||||
"View hide event action failed: ");
|
||||
set_view(*itr);
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name,
|
||||
"View show event action failed: ");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+246
-14
@@ -36,11 +36,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/throttle.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/download/resource_manager.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "core/manager.h"
|
||||
#include "display/frame.h"
|
||||
@@ -54,6 +57,7 @@
|
||||
|
||||
#include "control.h"
|
||||
#include "download_list.h"
|
||||
#include "core/download_store.h"
|
||||
|
||||
#include "root.h"
|
||||
|
||||
@@ -65,7 +69,17 @@ Root::Root() :
|
||||
m_windowTitle(NULL),
|
||||
m_windowHttpQueue(NULL),
|
||||
m_windowInput(NULL),
|
||||
m_windowStatusbar(NULL) {
|
||||
m_windowStatusbar(NULL),
|
||||
m_input_history_length(99),
|
||||
m_input_history_last_input(""),
|
||||
m_input_history_pointer_get(0) {
|
||||
|
||||
// Initialise prefilled m_input_history and m_input_history_pointers objects.
|
||||
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++) {
|
||||
m_input_history.insert( std::make_pair(type, InputHistoryCategory(m_input_history_length)) );
|
||||
m_input_history_pointers.insert( std::make_pair(type, 0) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -140,20 +154,20 @@ Root::setup_keys() {
|
||||
|
||||
const char* keys = get_throttle_keys();
|
||||
|
||||
m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, 1);
|
||||
m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, -1);
|
||||
m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, 1);
|
||||
m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, -1);
|
||||
m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small"));
|
||||
|
||||
m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, 5);
|
||||
m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, -5);
|
||||
m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, 5);
|
||||
m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, -5);
|
||||
m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium"));
|
||||
|
||||
m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, 50);
|
||||
m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, -50);
|
||||
m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, 50);
|
||||
m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, -50);
|
||||
m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large"));
|
||||
|
||||
m_bindings['\x0C'] = std::bind(&display::Manager::force_redraw, m_control->display()); // ^L
|
||||
m_bindings['\x11'] = std::bind(&Control::receive_normal_shutdown, m_control); // ^Q
|
||||
@@ -230,7 +244,7 @@ Root::adjust_up_throttle(int throttle) {
|
||||
}
|
||||
|
||||
void
|
||||
Root::enable_input(const std::string& title, input::TextInput* input) {
|
||||
Root::enable_input(const std::string& title, input::TextInput* input, ui::DownloadList::Input type) {
|
||||
if (m_windowInput->input() != NULL)
|
||||
throw torrent::internal_error("Root::enable_input(...) m_windowInput->input() != NULL.");
|
||||
|
||||
@@ -243,8 +257,14 @@ Root::enable_input(const std::string& title, input::TextInput* input) {
|
||||
m_windowInput->set_title(title);
|
||||
m_windowInput->set_focus(true);
|
||||
|
||||
reset_input_history_attributes(type);
|
||||
|
||||
input->bindings()['\x0C'] = std::bind(&display::Manager::force_redraw, m_control->display()); // ^L
|
||||
input->bindings()['\x11'] = std::bind(&Control::receive_normal_shutdown, m_control); // ^Q
|
||||
input->bindings()[KEY_UP] = std::bind(&Root::prev_in_input_history, this, type); // UP arrow
|
||||
input->bindings()['\x10'] = std::bind(&Root::prev_in_input_history, this, type); // ^P
|
||||
input->bindings()[KEY_DOWN] = std::bind(&Root::next_in_input_history, this, type); // DOWN arrow
|
||||
input->bindings()['\x0E'] = std::bind(&Root::next_in_input_history, this, type); // ^N
|
||||
|
||||
control->input()->set_text_input(input);
|
||||
control->display()->adjust_layout();
|
||||
@@ -272,4 +292,216 @@ Root::current_input() {
|
||||
return m_windowInput->input();
|
||||
}
|
||||
|
||||
void
|
||||
Root::add_to_input_history(ui::DownloadList::Input type, std::string item) {
|
||||
InputHistory::iterator itr = m_input_history.find(type);
|
||||
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(type);
|
||||
int prev_item_pointer = (pitr->second - 1) < 0 ? (m_input_history_length - 1) : (pitr->second - 1);
|
||||
|
||||
// Don't store item if it's empty or the same as the last one in the category.
|
||||
if (!item.empty() && item != itr->second.at(prev_item_pointer)) {
|
||||
itr->second.at(pitr->second) = rak::trim(item);
|
||||
m_input_history_pointers[type] = (pitr->second + 1) % m_input_history_length;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Root::prev_in_input_history(ui::DownloadList::Input type) {
|
||||
if (m_windowInput->input() == NULL)
|
||||
throw torrent::internal_error("Root::prev_in_input_history() m_windowInput->input() == NULL.");
|
||||
|
||||
InputHistory::iterator itr = m_input_history.find(type);
|
||||
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(type);
|
||||
|
||||
if (m_input_history_pointer_get == pitr->second)
|
||||
m_input_history_last_input = m_windowInput->input()->str();
|
||||
else
|
||||
itr->second.at(m_input_history_pointer_get) = m_windowInput->input()->str();
|
||||
|
||||
std::string tmp_input = m_input_history_last_input;
|
||||
int prev_pointer_get = (m_input_history_pointer_get - 1) < 0 ? (m_input_history_length - 1) : (m_input_history_pointer_get - 1);
|
||||
|
||||
if (prev_pointer_get != pitr->second && itr->second.at(prev_pointer_get) != "")
|
||||
m_input_history_pointer_get = prev_pointer_get;
|
||||
|
||||
if (m_input_history_pointer_get != pitr->second)
|
||||
tmp_input = itr->second.at(m_input_history_pointer_get);
|
||||
|
||||
m_windowInput->input()->str() = tmp_input;
|
||||
m_windowInput->input()->set_pos(tmp_input.length());
|
||||
m_windowInput->input()->mark_dirty();
|
||||
}
|
||||
|
||||
void
|
||||
Root::next_in_input_history(ui::DownloadList::Input type) {
|
||||
if (m_windowInput->input() == NULL)
|
||||
throw torrent::internal_error("Root::next_in_input_history() m_windowInput->input() == NULL.");
|
||||
|
||||
InputHistory::iterator itr = m_input_history.find(type);
|
||||
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(type);
|
||||
|
||||
if (m_input_history_pointer_get == pitr->second)
|
||||
m_input_history_last_input = m_windowInput->input()->str();
|
||||
else
|
||||
itr->second.at(m_input_history_pointer_get) = m_windowInput->input()->str();
|
||||
|
||||
std::string tmp_input = m_input_history_last_input;
|
||||
|
||||
if (m_input_history_pointer_get != pitr->second) {
|
||||
m_input_history_pointer_get = (m_input_history_pointer_get + 1) % m_input_history_length;
|
||||
tmp_input = (m_input_history_pointer_get == pitr->second) ? m_input_history_last_input : itr->second.at(m_input_history_pointer_get);
|
||||
}
|
||||
|
||||
m_windowInput->input()->str() = tmp_input;
|
||||
m_windowInput->input()->set_pos(tmp_input.length());
|
||||
m_windowInput->input()->mark_dirty();
|
||||
}
|
||||
|
||||
void
|
||||
Root::reset_input_history_attributes(ui::DownloadList::Input type) {
|
||||
InputHistoryPointers::const_iterator itr = m_input_history_pointers.find(type);
|
||||
|
||||
// Clear last_input and set pointer_get to the same as pointer_insert.
|
||||
m_input_history_last_input = "";
|
||||
m_input_history_pointer_get = itr->second;
|
||||
}
|
||||
|
||||
void
|
||||
Root::set_input_history_size(int size) {
|
||||
if (size < 1)
|
||||
throw torrent::input_error("Invalid input history size.");
|
||||
|
||||
for (InputHistory::iterator itr = m_input_history.begin(), last = m_input_history.end(); itr != last; itr++) {
|
||||
// Reserve the latest input history entries if new size is smaller than original.
|
||||
if (size < m_input_history_length) {
|
||||
int pointer_offset = m_input_history_length - size;
|
||||
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(itr->first);
|
||||
InputHistoryCategory input_history_category_tmp = itr->second;
|
||||
|
||||
for (int i=0; i != size; i++)
|
||||
itr->second.at(i) = input_history_category_tmp.at((pitr->second + pointer_offset + i) % m_input_history_length);
|
||||
|
||||
m_input_history_pointers[pitr->first] = 0;
|
||||
}
|
||||
|
||||
itr->second.resize(size);
|
||||
}
|
||||
|
||||
m_input_history_length = size;
|
||||
}
|
||||
|
||||
void
|
||||
Root::load_input_history() {
|
||||
if (!m_control->core()->download_store()->is_enabled()) {
|
||||
lt_log_print(torrent::LOG_DEBUG, "ignoring input history file");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string history_filename = m_control->core()->download_store()->path() + "rtorrent.input_history";
|
||||
std::fstream history_file(history_filename.c_str(), std::ios::in);
|
||||
|
||||
if (history_file.is_open()) {
|
||||
// Create a temp object of the content since size of history categories can be smaller than this.
|
||||
InputHistory input_history_tmp;
|
||||
|
||||
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++)
|
||||
input_history_tmp.insert( std::make_pair(type, InputHistoryCategory()) );
|
||||
|
||||
std::string line;
|
||||
|
||||
while (std::getline(history_file, line)) {
|
||||
if (!line.empty()) {
|
||||
int delim_pos = line.find("|");
|
||||
|
||||
if (delim_pos != std::string::npos) {
|
||||
int type = std::atoi(line.substr(0, delim_pos).c_str());
|
||||
InputHistory::iterator itr = input_history_tmp.find(type);
|
||||
|
||||
if (itr != input_history_tmp.end()) {
|
||||
std::string input_str = rak::trim(line.substr(delim_pos + 1));
|
||||
|
||||
if (!input_str.empty())
|
||||
itr->second.push_back(input_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (history_file.bad()) {
|
||||
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted, discarding (path:%s)", history_filename.c_str());
|
||||
return;
|
||||
} else {
|
||||
lt_log_print(torrent::LOG_DEBUG, "input history file read (path:%s)", history_filename.c_str());
|
||||
}
|
||||
|
||||
for (InputHistory::const_iterator itr = input_history_tmp.begin(), last = input_history_tmp.end(); itr != last; itr++) {
|
||||
int input_history_tmp_category_length = itr->second.size();
|
||||
InputHistory::iterator hitr = m_input_history.find(itr->first);
|
||||
InputHistoryPointers::iterator pitr = m_input_history_pointers.find(itr->first);
|
||||
|
||||
if (m_input_history_length < input_history_tmp_category_length) {
|
||||
int pointer_offset = input_history_tmp_category_length - m_input_history_length;
|
||||
|
||||
for (int i=0; i != m_input_history_length; i++)
|
||||
hitr->second.at(i) = itr->second.at((pointer_offset + i) % input_history_tmp_category_length);
|
||||
|
||||
pitr->second = 0;
|
||||
} else {
|
||||
hitr->second = itr->second;
|
||||
hitr->second.resize(m_input_history_length);
|
||||
|
||||
pitr->second = input_history_tmp_category_length % m_input_history_length;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lt_log_print(torrent::LOG_DEBUG, "could not open input history file (path:%s)", history_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Root::save_input_history() {
|
||||
if (!m_control->core()->download_store()->is_enabled())
|
||||
return;
|
||||
|
||||
std::string history_filename = m_control->core()->download_store()->path() + "rtorrent.input_history";
|
||||
std::string history_filename_tmp = history_filename + ".new";
|
||||
std::fstream history_file(history_filename_tmp.c_str(), std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!history_file.is_open()) {
|
||||
lt_log_print(torrent::LOG_DEBUG, "could not open input history file for writing (path:%s)", history_filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for (InputHistory::const_iterator itr = m_input_history.begin(), last = m_input_history.end(); itr != last; itr++) {
|
||||
InputHistoryPointers::const_iterator pitr = m_input_history_pointers.find(itr->first);
|
||||
|
||||
for (int i=0; i != m_input_history_length; i++)
|
||||
if (!itr->second.at((pitr->second + i) % m_input_history_length).empty())
|
||||
history_file << itr->first << "|" + itr->second.at((pitr->second + i) % m_input_history_length) + "\n";
|
||||
}
|
||||
|
||||
if (!history_file.good()) {
|
||||
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted during writing, discarding (path:%s)", history_filename.c_str());
|
||||
return;
|
||||
} else {
|
||||
lt_log_print(torrent::LOG_DEBUG, "input history file written (path:%s)", history_filename.c_str());
|
||||
}
|
||||
|
||||
history_file.close();
|
||||
|
||||
std::rename(history_filename_tmp.c_str(), history_filename.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
Root::clear_input_history() {
|
||||
for (int type = ui::DownloadList::INPUT_LOAD_DEFAULT; type != ui::DownloadList::INPUT_EOI; type++) {
|
||||
InputHistory::iterator itr = m_input_history.find(type);
|
||||
|
||||
for (int i=0; i != m_input_history_length; i++)
|
||||
itr->second.at(i) = "";
|
||||
|
||||
m_input_history_pointers[type] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-1
@@ -39,6 +39,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "input/bindings.h"
|
||||
#include "download_list.h"
|
||||
|
||||
class Control;
|
||||
|
||||
@@ -65,6 +66,10 @@ public:
|
||||
typedef display::WindowInput WInput;
|
||||
typedef display::WindowStatusbar WStatusbar;
|
||||
|
||||
typedef std::map<int, int> InputHistoryPointers;
|
||||
typedef std::vector<std::string> InputHistoryCategory;
|
||||
typedef std::map<int, InputHistoryCategory> InputHistory;
|
||||
|
||||
Root();
|
||||
|
||||
void init(Control* c);
|
||||
@@ -88,11 +93,19 @@ public:
|
||||
|
||||
const char* get_throttle_keys();
|
||||
|
||||
void enable_input(const std::string& title, input::TextInput* input);
|
||||
void enable_input(const std::string& title, input::TextInput* input, ui::DownloadList::Input type);
|
||||
void disable_input();
|
||||
|
||||
input::TextInput* current_input();
|
||||
|
||||
int get_input_history_size() { return m_input_history_length; }
|
||||
void set_input_history_size(int size);
|
||||
void add_to_input_history(ui::DownloadList::Input type, std::string item);
|
||||
|
||||
void load_input_history();
|
||||
void save_input_history();
|
||||
void clear_input_history();
|
||||
|
||||
private:
|
||||
void setup_keys();
|
||||
|
||||
@@ -105,6 +118,17 @@ private:
|
||||
WStatusbar* m_windowStatusbar;
|
||||
|
||||
input::Bindings m_bindings;
|
||||
|
||||
int m_input_history_length;
|
||||
std::string m_input_history_last_input;
|
||||
int m_input_history_pointer_get;
|
||||
InputHistory m_input_history;
|
||||
InputHistoryPointers m_input_history_pointers;
|
||||
|
||||
void prev_in_input_history(ui::DownloadList::Input type);
|
||||
void next_in_input_history(ui::DownloadList::Input type);
|
||||
|
||||
void reset_input_history_attributes(ui::DownloadList::Input type);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user