[Console] Flake8 all files
This commit is contained in:
parent
a68c3140af
commit
45ef6ac56d
|
@ -34,3 +34,4 @@
|
||||||
#
|
#
|
||||||
UI_PATH = __path__[0]
|
UI_PATH = __path__[0]
|
||||||
from main import start
|
from main import start
|
||||||
|
assert start # silence pyflakes
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# colors.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from deluge.ui.console.modes import format_utils
|
from deluge.ui.console.modes import format_utils
|
||||||
|
@ -41,19 +15,19 @@ except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
colors = [
|
colors = [
|
||||||
'COLOR_BLACK',
|
"COLOR_BLACK",
|
||||||
'COLOR_BLUE',
|
"COLOR_BLUE",
|
||||||
'COLOR_CYAN',
|
"COLOR_CYAN",
|
||||||
'COLOR_GREEN',
|
"COLOR_GREEN",
|
||||||
'COLOR_MAGENTA',
|
"COLOR_MAGENTA",
|
||||||
'COLOR_RED',
|
"COLOR_RED",
|
||||||
'COLOR_WHITE',
|
"COLOR_WHITE",
|
||||||
'COLOR_YELLOW'
|
"COLOR_YELLOW"
|
||||||
]
|
]
|
||||||
|
|
||||||
# {(fg, bg): pair_number, ...}
|
# {(fg, bg): pair_number, ...}
|
||||||
color_pairs = {
|
color_pairs = {
|
||||||
("white", "black"): 0 # Special case, can't be changed
|
("white", "black"): 0 # Special case, can't be changed
|
||||||
}
|
}
|
||||||
|
|
||||||
# Some default color schemes
|
# Some default color schemes
|
||||||
|
@ -92,6 +66,7 @@ type_color = {
|
||||||
dict: "{!white,black,bold!}"
|
dict: "{!white,black,bold!}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def init_colors():
|
def init_colors():
|
||||||
# Create the color_pairs dict
|
# Create the color_pairs dict
|
||||||
counter = 1
|
counter = 1
|
||||||
|
@ -111,9 +86,11 @@ def init_colors():
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BadColorString(Exception):
|
class BadColorString(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def replace_tabs(line):
|
def replace_tabs(line):
|
||||||
"""
|
"""
|
||||||
Returns a string with tabs replaced with spaces.
|
Returns a string with tabs replaced with spaces.
|
||||||
|
@ -124,6 +101,7 @@ def replace_tabs(line):
|
||||||
line = line.replace("\t", " " * tab_length, 1)
|
line = line.replace("\t", " " * tab_length, 1)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
def strip_colors(line):
|
def strip_colors(line):
|
||||||
"""
|
"""
|
||||||
Returns a string with the color formatting removed.
|
Returns a string with the color formatting removed.
|
||||||
|
@ -135,6 +113,7 @@ def strip_colors(line):
|
||||||
|
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
def get_line_length(line, encoding="UTF-8"):
|
def get_line_length(line, encoding="UTF-8"):
|
||||||
"""
|
"""
|
||||||
Returns the string length without the color formatting.
|
Returns the string length without the color formatting.
|
||||||
|
@ -153,6 +132,7 @@ def get_line_length(line, encoding="UTF-8"):
|
||||||
line = replace_tabs(line)
|
line = replace_tabs(line)
|
||||||
return len(line)
|
return len(line)
|
||||||
|
|
||||||
|
|
||||||
def get_line_width(line, encoding="UTF-8"):
|
def get_line_width(line, encoding="UTF-8"):
|
||||||
"""
|
"""
|
||||||
Get width of string considering double width characters
|
Get width of string considering double width characters
|
||||||
|
@ -171,6 +151,7 @@ def get_line_width(line, encoding="UTF-8"):
|
||||||
line = replace_tabs(line)
|
line = replace_tabs(line)
|
||||||
return format_utils.strwidth(line)
|
return format_utils.strwidth(line)
|
||||||
|
|
||||||
|
|
||||||
def parse_color_string(s, encoding="UTF-8"):
|
def parse_color_string(s, encoding="UTF-8"):
|
||||||
"""
|
"""
|
||||||
Parses a string and returns a list of 2-tuples (color, string).
|
Parses a string and returns a list of 2-tuples (color, string).
|
||||||
|
@ -187,7 +168,6 @@ def parse_color_string(s, encoding="UTF-8"):
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
# Keep track of where the strings
|
# Keep track of where the strings
|
||||||
col_index = 0
|
|
||||||
while s.find("{!") != -1:
|
while s.find("{!") != -1:
|
||||||
begin = s.find("{!")
|
begin = s.find("{!")
|
||||||
if begin > 0:
|
if begin > 0:
|
||||||
|
@ -198,9 +178,9 @@ def parse_color_string(s, encoding="UTF-8"):
|
||||||
raise BadColorString("Missing closing '!}'")
|
raise BadColorString("Missing closing '!}'")
|
||||||
|
|
||||||
# Get a list of attributes in the bracketed section
|
# Get a list of attributes in the bracketed section
|
||||||
attrs = s[begin+2:end].split(",")
|
attrs = s[begin + 2:end].split(",")
|
||||||
|
|
||||||
if len(attrs) == 1 and not attrs[0].strip(' '):
|
if len(attrs) == 1 and not attrs[0].strip(" "):
|
||||||
raise BadColorString("No description in {! !}")
|
raise BadColorString("No description in {! !}")
|
||||||
|
|
||||||
def apply_attrs(cp, a):
|
def apply_attrs(cp, a):
|
||||||
|
@ -238,10 +218,10 @@ def parse_color_string(s, encoding="UTF-8"):
|
||||||
next_begin = s.find("{!", end)
|
next_begin = s.find("{!", end)
|
||||||
|
|
||||||
if next_begin == -1:
|
if next_begin == -1:
|
||||||
ret.append((color_pair, replace_tabs(s[end+2:])))
|
ret.append((color_pair, replace_tabs(s[end + 2:])))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
ret.append((color_pair, replace_tabs(s[end+2:next_begin])))
|
ret.append((color_pair, replace_tabs(s[end + 2:next_begin])))
|
||||||
s = s[next_begin:]
|
s = s[next_begin:]
|
||||||
|
|
||||||
if not ret:
|
if not ret:
|
||||||
|
|
|
@ -1,40 +1,15 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# commander.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -68,7 +43,7 @@ class Commander:
|
||||||
"""
|
"""
|
||||||
if not cmd:
|
if not cmd:
|
||||||
return
|
return
|
||||||
cmd, _, line = cmd.partition(' ')
|
cmd, _, line = cmd.partition(" ")
|
||||||
try:
|
try:
|
||||||
parser = self._commands[cmd].create_parser()
|
parser = self._commands[cmd].create_parser()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -103,7 +78,7 @@ class Commander:
|
||||||
self.write("{!error!}Error parsing options: %s" % ex)
|
self.write("{!error!}Error parsing options: %s" % ex)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not getattr(options, '_exit', False):
|
if not getattr(options, "_exit", False):
|
||||||
try:
|
try:
|
||||||
ret = self._commands[cmd].handle(*args, **options.__dict__)
|
ret = self._commands[cmd].handle(*args, **options.__dict__)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -119,7 +94,7 @@ class Commander:
|
||||||
commands = []
|
commands = []
|
||||||
if args:
|
if args:
|
||||||
# Multiple commands split by ";"
|
# Multiple commands split by ";"
|
||||||
commands = [arg.strip() for arg in args.split(';')]
|
commands = [arg.strip() for arg in args.split(";")]
|
||||||
|
|
||||||
def on_connect(result):
|
def on_connect(result):
|
||||||
def on_started(result):
|
def on_started(result):
|
||||||
|
@ -156,8 +131,8 @@ class Commander:
|
||||||
if not self.interactive:
|
if not self.interactive:
|
||||||
if commands[0].startswith("connect"):
|
if commands[0].startswith("connect"):
|
||||||
d = self.do_command(commands.pop(0))
|
d = self.do_command(commands.pop(0))
|
||||||
elif 'help' in commands:
|
elif "help" in commands:
|
||||||
self.do_command('help')
|
self.do_command("help")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
d.addCallback(on_connect)
|
d.addCallback(on_connect)
|
||||||
d.addErrback(on_connect_fail)
|
d.addErrback(on_connect_fail)
|
||||||
|
|
|
@ -1,39 +1,13 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# cache.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -41,6 +15,7 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Show information about the disk cache"""
|
"""Show information about the disk cache"""
|
||||||
usage = "Usage: cache"
|
usage = "Usage: cache"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,15 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# config.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import cStringIO
|
import cStringIO
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
import tokenize
|
import tokenize
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
|
@ -49,6 +22,7 @@ from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def atom(next, token):
|
def atom(next, token):
|
||||||
"""taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
|
"""taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
|
||||||
if token[1] == "(":
|
if token[1] == "(":
|
||||||
|
@ -71,15 +45,16 @@ def atom(next, token):
|
||||||
return float(token[-1])
|
return float(token[-1])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return str(token[-1])
|
return str(token[-1])
|
||||||
elif token[1].lower() == 'true':
|
elif token[1].lower() == "true":
|
||||||
return True
|
return True
|
||||||
elif token[1].lower() == 'false':
|
elif token[1].lower() == "false":
|
||||||
return False
|
return False
|
||||||
elif token[0] is tokenize.STRING or token[1] == "/":
|
elif token[0] is tokenize.STRING or token[1] == "/":
|
||||||
return token[-1].decode("string-escape")
|
return token[-1].decode("string-escape")
|
||||||
|
|
||||||
raise SyntaxError("malformed expression (%s)" % token[1])
|
raise SyntaxError("malformed expression (%s)" % token[1])
|
||||||
|
|
||||||
|
|
||||||
def simple_eval(source):
|
def simple_eval(source):
|
||||||
""" evaluates the 'source' string into a combination of primitive python objects
|
""" evaluates the 'source' string into a combination of primitive python objects
|
||||||
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
|
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
|
||||||
|
@ -94,21 +69,19 @@ class Command(BaseCommand):
|
||||||
"""Show and set configuration values"""
|
"""Show and set configuration values"""
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-s', '--set', action='store', nargs=2, dest='set',
|
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
|
||||||
help='set value for key'),
|
|
||||||
)
|
)
|
||||||
usage = "Usage: config [key1 [key2 ...]]\n"\
|
usage = """Usage: config [key1 [key2 ...]]"
|
||||||
" config --set key value"
|
config --set key value"""
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
if options['set']:
|
if options["set"]:
|
||||||
return self._set_config(*args, **options)
|
return self._set_config(*args, **options)
|
||||||
else:
|
else:
|
||||||
return self._get_config(*args, **options)
|
return self._get_config(*args, **options)
|
||||||
|
|
||||||
def _get_config(self, *args, **options):
|
def _get_config(self, *args, **options):
|
||||||
deferred = defer.Deferred()
|
|
||||||
config = component.get("CoreConfig")
|
config = component.get("CoreConfig")
|
||||||
keys = config.keys()
|
keys = config.keys()
|
||||||
keys.sort()
|
keys.sort()
|
||||||
|
@ -161,4 +134,4 @@ class Command(BaseCommand):
|
||||||
return deferred
|
return deferred
|
||||||
|
|
||||||
def complete(self, text):
|
def complete(self, text):
|
||||||
return [ k for k in component.get("CoreConfig").keys() if k.startswith(text) ]
|
return [k for k in component.get("CoreConfig").keys() if k.startswith(text)]
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# connect.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -55,6 +29,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def do_connect():
|
def do_connect():
|
||||||
d = client.connect(host, port, username, password)
|
d = client.connect(host, port, username, password)
|
||||||
|
|
||||||
def on_connect(result):
|
def on_connect(result):
|
||||||
if self.console.interactive:
|
if self.console.interactive:
|
||||||
self.console.write("{!success!}Connected to %s:%s!" % (host, port))
|
self.console.write("{!success!}Connected to %s:%s!" % (host, port))
|
||||||
|
|
|
@ -1,55 +1,28 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# debug.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.log
|
import deluge.log
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Enable and disable debugging"""
|
"""Enable and disable debugging"""
|
||||||
usage = 'Usage: debug [on|off]'
|
usage = "Usage: debug [on|off]"
|
||||||
def handle(self, state='', **options):
|
|
||||||
if state == 'on':
|
def handle(self, state="", **options):
|
||||||
|
if state == "on":
|
||||||
deluge.log.setLoggerLevel("debug")
|
deluge.log.setLoggerLevel("debug")
|
||||||
elif state == 'off':
|
elif state == "off":
|
||||||
deluge.log.setLoggerLevel("error")
|
deluge.log.setLoggerLevel("error")
|
||||||
else:
|
else:
|
||||||
component.get("ConsoleUI").write("{!error!}%s" % self.usage)
|
component.get("ConsoleUI").write("{!error!}%s" % self.usage)
|
||||||
|
@ -57,4 +30,4 @@ class Command(BaseCommand):
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
|
||||||
def complete(self, text):
|
def complete(self, text):
|
||||||
return [x for x in ['on', 'off'] if x.startswith(text)]
|
return [x for x in ["on", "off"] if x.startswith(text)]
|
||||||
|
|
|
@ -1,38 +1,12 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# status.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.common
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
from deluge.ui.console.modes.alltorrents import AllTorrents
|
from deluge.ui.console.modes.alltorrents import AllTorrents
|
||||||
|
@ -42,6 +16,7 @@ class Command(BaseCommand):
|
||||||
"""Exit this mode and go into the more 'gui' like mode"""
|
"""Exit this mode and go into the more 'gui' like mode"""
|
||||||
usage = "Usage: gui"
|
usage = "Usage: gui"
|
||||||
interactive_only = True
|
interactive_only = True
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
console = component.get("ConsoleUI")
|
console = component.get("ConsoleUI")
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# halt.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -42,6 +16,7 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"Shutdown the deluge server"
|
"Shutdown the deluge server"
|
||||||
usage = "Usage: halt"
|
usage = "Usage: halt"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,23 @@
|
||||||
# help.py
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""displays help on other commands"""
|
"""displays help on other commands"""
|
||||||
|
|
||||||
usage = "Usage: help [command]"
|
usage = "Usage: help [command]"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
@ -60,14 +34,14 @@ class Command(BaseCommand):
|
||||||
parser = cmd.create_parser()
|
parser = cmd.create_parser()
|
||||||
self.console.write(parser.format_help())
|
self.console.write(parser.format_help())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.console.write(cmd.__doc__ or 'No help for this command')
|
self.console.write(cmd.__doc__ or "No help for this command")
|
||||||
self.console.write(" ")
|
self.console.write(" ")
|
||||||
else:
|
else:
|
||||||
self.console.set_batch_write(True)
|
self.console.set_batch_write(True)
|
||||||
for cmd in sorted(self._commands):
|
for cmd in sorted(self._commands):
|
||||||
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
|
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
|
||||||
self.console.write(" ")
|
self.console.write(" ")
|
||||||
self.console.write('For help on a specific command, use "<command> --help"')
|
self.console.write("For help on a specific command, use '<command> --help'")
|
||||||
self.console.set_batch_write(False)
|
self.console.set_batch_write(False)
|
||||||
|
|
||||||
return deferred
|
return deferred
|
||||||
|
|
|
@ -1,37 +1,11 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# info.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
@ -47,39 +21,41 @@ from deluge.ui.console.modes import format_utils
|
||||||
strwidth = format_utils.strwidth
|
strwidth = format_utils.strwidth
|
||||||
|
|
||||||
|
|
||||||
STATUS_KEYS = ["state",
|
STATUS_KEYS = [
|
||||||
"download_location",
|
"state",
|
||||||
"tracker_host",
|
"download_location",
|
||||||
"tracker_status",
|
"tracker_host",
|
||||||
"next_announce",
|
"tracker_status",
|
||||||
"name",
|
"next_announce",
|
||||||
"total_size",
|
"name",
|
||||||
"progress",
|
"total_size",
|
||||||
"num_seeds",
|
"progress",
|
||||||
"total_seeds",
|
"num_seeds",
|
||||||
"num_peers",
|
"total_seeds",
|
||||||
"total_peers",
|
"num_peers",
|
||||||
"eta",
|
"total_peers",
|
||||||
"download_payload_rate",
|
"eta",
|
||||||
"upload_payload_rate",
|
"download_payload_rate",
|
||||||
"ratio",
|
"upload_payload_rate",
|
||||||
"distributed_copies",
|
"ratio",
|
||||||
"num_pieces",
|
"distributed_copies",
|
||||||
"piece_length",
|
"num_pieces",
|
||||||
"total_done",
|
"piece_length",
|
||||||
"files",
|
"total_done",
|
||||||
"file_priorities",
|
"files",
|
||||||
"file_progress",
|
"file_priorities",
|
||||||
"peers",
|
"file_progress",
|
||||||
"is_seed",
|
"peers",
|
||||||
"is_finished",
|
"is_seed",
|
||||||
"active_time",
|
"is_finished",
|
||||||
"seeding_time"
|
"active_time",
|
||||||
]
|
"seeding_time"
|
||||||
|
]
|
||||||
|
|
||||||
# Add filter specific state to torrent states
|
# Add filter specific state to torrent states
|
||||||
STATES = ["Active"] + common.TORRENT_STATE
|
STATES = ["Active"] + common.TORRENT_STATE
|
||||||
|
|
||||||
|
|
||||||
def format_progressbar(progress, width):
|
def format_progressbar(progress, width):
|
||||||
"""
|
"""
|
||||||
Returns a string of a progress bar.
|
Returns a string of a progress bar.
|
||||||
|
@ -90,9 +66,9 @@ def format_progressbar(progress, width):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
w = width - 2 # we use a [] for the beginning and end
|
w = width - 2 # we use a [] for the beginning and end
|
||||||
s = "["
|
s = "["
|
||||||
p = int(round((progress/100) * w))
|
p = int(round((progress / 100) * w))
|
||||||
s += "#" * p
|
s += "#" * p
|
||||||
s += "-" * (w - p)
|
s += "-" * (w - p)
|
||||||
s += "]"
|
s += "]"
|
||||||
|
@ -112,31 +88,29 @@ def format_time(seconds):
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Show information about the torrents"""
|
"""Show information about the torrents"""
|
||||||
|
|
||||||
sort_help = 'sort items. Possible keys: ' + ', '.join(STATUS_KEYS)
|
sort_help = "sort items. Possible keys: " + ", ".join(STATUS_KEYS)
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-v', '--verbose', action='store_true', default=False, dest='verbose',
|
make_option("-v", "--verbose", action="store_true", default=False, dest="verbose",
|
||||||
help='shows more information per torrent'),
|
help="Show more information per torrent."),
|
||||||
make_option('-d', '--detailed', action='store_true', default=False, dest='detailed',
|
make_option("-d", "--detailed", action="store_true", default=False, dest="detailed",
|
||||||
help='shows detailed information about files and peers. '
|
help="Show more detailed information including files and peers."),
|
||||||
"Implies -v"),
|
make_option("-s", "--state", action="store", dest="state",
|
||||||
make_option('-s', '--state', action='store', dest='state',
|
help="Show torrents with state STATE: %s." % (", ".join(STATES))),
|
||||||
help="show torrents with state STATE. "
|
make_option("--sort", action="store", type="string", default="", dest="sort", help=sort_help),
|
||||||
"Possible values are: %s"%(", ".join(STATES))),
|
make_option("--sort-reverse", action="store", type="string", default="", dest="sort_rev",
|
||||||
make_option('--sort', action='store', type='string', default='', dest='sort',
|
help="Same as --sort but items are in reverse order.")
|
||||||
help=sort_help),
|
|
||||||
make_option('--sort-reverse', action='store', type='string', default='', dest='sort_rev',
|
|
||||||
help='sort items in reverse order. Same keys allowed as for --sort.')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
usage = "Usage: info [-v | -d | -s <state>] [<torrent-id> [<torrent-id> ...]]\n"\
|
usage = """Usage: info [-v | -d | -s <state>] [<torrent-id> [<torrent-id> ...]]
|
||||||
" You can give the first few characters of a torrent-id to identify the torrent.\n"\
|
You can give the first few characters of a torrent-id to identify the torrent.
|
||||||
" info * will list all torrents.\n\n"\
|
info * will list all torrents.
|
||||||
" Tab Completion (info *pattern*<tab>):\n"\
|
|
||||||
" | First press of <tab> will output up to 15 matches;\n"\
|
Tab Completion (info *pattern*<tab>):
|
||||||
" | hitting <tab> a second time, will print 15 more matches; \n"\
|
| First press of <tab> will output up to 15 matches;
|
||||||
" | and a third press will print all remaining matches.\n"\
|
| hitting <tab> a second time, will print 15 more matches;
|
||||||
" | (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)"
|
| and a third press will print all remaining matches.
|
||||||
|
| (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)"""
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
@ -150,22 +124,20 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def on_torrents_status(status):
|
def on_torrents_status(status):
|
||||||
# Print out the information for each torrent
|
# Print out the information for each torrent
|
||||||
sort_key = options['sort']
|
sort_key = options["sort"]
|
||||||
sort_reverse = False
|
sort_reverse = False
|
||||||
if not sort_key:
|
if not sort_key:
|
||||||
sort_key = options['sort_rev']
|
sort_key = options["sort_rev"]
|
||||||
sort_reverse = True
|
sort_reverse = True
|
||||||
if not sort_key:
|
if not sort_key:
|
||||||
sort_key = 'name'
|
sort_key = "name"
|
||||||
sort_reverse = False
|
sort_reverse = False
|
||||||
if sort_key not in status_keys:
|
if sort_key not in STATUS_KEYS:
|
||||||
self.console.write('')
|
self.console.write("")
|
||||||
self.console.write("{!error!}Unknown sort key: " + sort_key + ", will sort on name")
|
self.console.write("{!error!}Unknown sort key: " + sort_key + ", will sort on name")
|
||||||
sort_key = 'name'
|
sort_key = "name"
|
||||||
sort_reverse = False
|
sort_reverse = False
|
||||||
for key, value in sorted(status.items(),
|
for key, value in sorted(status.items(), key=lambda x: x[1].get(sort_key), reverse=sort_reverse):
|
||||||
key=lambda x : x[1].get(sort_key),
|
|
||||||
reverse=sort_reverse):
|
|
||||||
self.show_info(key, status[key], options["verbose"], options["detailed"])
|
self.show_info(key, status[key], options["verbose"], options["detailed"])
|
||||||
|
|
||||||
def on_torrents_status_fail(reason):
|
def on_torrents_status_fail(reason):
|
||||||
|
@ -178,8 +150,8 @@ class Command(BaseCommand):
|
||||||
if options["state"] in STATES:
|
if options["state"] in STATES:
|
||||||
status_dict["state"] = options["state"]
|
status_dict["state"] = options["state"]
|
||||||
else:
|
else:
|
||||||
self.console.write("Invalid state: %s"%options["state"])
|
self.console.write("Invalid state: %s" % options["state"])
|
||||||
self.console.write("Possible values are: %s."%(", ".join(STATES)))
|
self.console.write("Possible values are: %s." % (", ".join(STATES)))
|
||||||
return
|
return
|
||||||
|
|
||||||
d = client.core.get_torrents_status(status_dict, STATUS_KEYS)
|
d = client.core.get_torrents_status(status_dict, STATUS_KEYS)
|
||||||
|
@ -188,29 +160,28 @@ class Command(BaseCommand):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def show_file_info(self, torrent_id, status):
|
def show_file_info(self, torrent_id, status):
|
||||||
SPACES_PER_LEVEL = 2
|
spaces_per_level = 2
|
||||||
|
|
||||||
if hasattr(self.console, "screen"):
|
if hasattr(self.console, "screen"):
|
||||||
cols = self.console.screen.cols
|
cols = self.console.screen.cols
|
||||||
else:
|
else:
|
||||||
cols = 80
|
cols = 80
|
||||||
|
|
||||||
dirs = []
|
|
||||||
prevpath = []
|
prevpath = []
|
||||||
for i, file in enumerate(status["files"]):
|
for i, file in enumerate(status["files"]):
|
||||||
filename = file["path"].split(dirsep)[-1]
|
filename = file["path"].split(dirsep)[-1]
|
||||||
filepath = file["path"].split(dirsep)[:-1]
|
filepath = file["path"].split(dirsep)[:-1]
|
||||||
|
|
||||||
for depth, subdir in enumerate(filepath):
|
for depth, subdir in enumerate(filepath):
|
||||||
indent = " "*depth*SPACES_PER_LEVEL
|
indent = " " * depth * spaces_per_level
|
||||||
if depth >= len(prevpath):
|
if depth >= len(prevpath):
|
||||||
self.console.write("%s{!cyan!}%s" % (indent, subdir))
|
self.console.write("%s{!cyan!}%s" % (indent, subdir))
|
||||||
elif subdir != prevpath[depth]:
|
elif subdir != prevpath[depth]:
|
||||||
self.console.write("%s{!cyan!}%s" % (indent, subdir))
|
self.console.write("%s{!cyan!}%s" % (indent, subdir))
|
||||||
|
|
||||||
depth = len(filepath)
|
depth = len(filepath)
|
||||||
|
|
||||||
indent = " " * depth * SPACES_PER_LEVEL
|
indent = " " * depth * spaces_per_level
|
||||||
|
|
||||||
col_filename = indent + filename
|
col_filename = indent + filename
|
||||||
col_size = " ({!cyan!}%s{!input!})" % common.fsize(file["size"])
|
col_size = " ({!cyan!}%s{!input!})" % common.fsize(file["size"])
|
||||||
|
@ -226,20 +197,20 @@ class Command(BaseCommand):
|
||||||
col_priority += "{!success!}"
|
col_priority += "{!success!}"
|
||||||
else:
|
else:
|
||||||
col_priority += "{!input!}"
|
col_priority += "{!input!}"
|
||||||
col_priority+= fp
|
col_priority += fp
|
||||||
|
|
||||||
rf = format_utils.remove_formatting
|
rf = format_utils.remove_formatting
|
||||||
tlen = lambda s: strwidth(rf(s))
|
tlen = lambda s: strwidth(rf(s))
|
||||||
|
|
||||||
if not isinstance(col_filename, unicode):
|
if not isinstance(col_filename, unicode):
|
||||||
col_filename = unicode(col_filename, 'utf-8')
|
col_filename = unicode(col_filename, "utf-8")
|
||||||
|
|
||||||
col_all_info = col_size + col_progress + col_priority
|
col_all_info = col_size + col_progress + col_priority
|
||||||
#Check how much space we've got left after writing all the info
|
#Check how much space we've got left after writing all the info
|
||||||
space_left = cols - tlen(col_all_info)
|
space_left = cols - tlen(col_all_info)
|
||||||
#And how much we will potentially have with the longest possible column
|
#And how much we will potentially have with the longest possible column
|
||||||
maxlen_space_left = cols - tlen(" (1000.0 MiB) 100.00% Priority: Do Not Download")
|
maxlen_space_left = cols - tlen(" (1000.0 MiB) 100.00% Priority: Do Not Download")
|
||||||
if maxlen_space_left > tlen(col_filename) + 1:
|
if maxlen_space_left > tlen(col_filename) + 1:
|
||||||
#If there is enough space, pad it all nicely
|
#If there is enough space, pad it all nicely
|
||||||
col_all_info = ""
|
col_all_info = ""
|
||||||
col_all_info += " ("
|
col_all_info += " ("
|
||||||
|
@ -253,16 +224,15 @@ class Command(BaseCommand):
|
||||||
col_all_info += col_priority
|
col_all_info += col_priority
|
||||||
col_all_info += " " * spaces_to_add
|
col_all_info += " " * spaces_to_add
|
||||||
#And remember to put it to the left!
|
#And remember to put it to the left!
|
||||||
col_filename = format_utils.pad_string(col_filename, maxlen_space_left - 2, side = "right")
|
col_filename = format_utils.pad_string(col_filename, maxlen_space_left - 2, side="right")
|
||||||
elif space_left > tlen(col_filename) + 1:
|
elif space_left > tlen(col_filename) + 1:
|
||||||
#If there is enough space, put the info to the right
|
#If there is enough space, put the info to the right
|
||||||
col_filename = format_utils.pad_string(col_filename, space_left - 2, side = "right")
|
col_filename = format_utils.pad_string(col_filename, space_left - 2, side="right")
|
||||||
else:
|
else:
|
||||||
#And if there is not, shorten the name
|
#And if there is not, shorten the name
|
||||||
col_filename = format_utils.trim_string( col_filename, space_left, True)
|
col_filename = format_utils.trim_string(col_filename, space_left, True)
|
||||||
self.console.write(col_filename + col_all_info)
|
self.console.write(col_filename + col_all_info)
|
||||||
|
|
||||||
|
|
||||||
prevpath = filepath
|
prevpath = filepath
|
||||||
|
|
||||||
def show_peer_info(self, torrent_id, status):
|
def show_peer_info(self, torrent_id, status):
|
||||||
|
@ -375,43 +345,43 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
s = "%s%s" % (colors.state_color[status["state"]], "[" + status["state"][0] + "]")
|
s = "%s%s" % (colors.state_color[status["state"]], "[" + status["state"][0] + "]")
|
||||||
|
|
||||||
s+= " {!info!}" + ("%.2f%%" % status["progress"]).ljust(7, " ")
|
s += " {!info!}" + ("%.2f%%" % status["progress"]).ljust(7, " ")
|
||||||
s+= " {!input!}%s" % (status["name"])
|
s += " {!input!}%s" % (status["name"])
|
||||||
|
|
||||||
#Shorten the ID if it's necessary. Pretty hacky
|
#Shorten the ID if it's necessary. Pretty hacky
|
||||||
#I _REALLY_ should make a nice function for it that can partition and shorten stuff
|
#I _REALLY_ should make a nice function for it that can partition and shorten stuff
|
||||||
space_left = cols - strwidth("[s] 100.00% " + status["name"] + " "*3) - 2
|
space_left = cols - strwidth("[s] 100.00% " + status["name"] + " " * 3) - 2
|
||||||
|
|
||||||
if space_left >= len(torrent_id) - 2:
|
if space_left >= len(torrent_id) - 2:
|
||||||
#There's enough space, print it
|
#There's enough space, print it
|
||||||
s += " {!cyan!}%s" % torrent_id
|
s += " {!cyan!}%s" % torrent_id
|
||||||
else:
|
else:
|
||||||
#Shorten the ID
|
#Shorten the ID
|
||||||
a = int(space_left * 2/3.0)
|
a = int(space_left * 2 / 3.0)
|
||||||
b = space_left - a
|
b = space_left - a
|
||||||
if a < 8:
|
if a < 8:
|
||||||
b = b - (8-a)
|
b = b - (8 - a)
|
||||||
a = 8
|
a = 8
|
||||||
if b < 0:
|
if b < 0:
|
||||||
a+= b
|
a += b
|
||||||
b = 0
|
b = 0
|
||||||
if a > 8:
|
if a > 8:
|
||||||
#Print the shortened ID
|
#Print the shortened ID
|
||||||
s += " {!cyan!}%s" % (torrent_id[0:a] + ".." + torrent_id[-b-1:-1])
|
s += " {!cyan!}%s" % (torrent_id[0:a] + ".." + torrent_id[-b - 1:-1])
|
||||||
else:
|
else:
|
||||||
#It has wrapped over to the second row anyway
|
#It has wrapped over to the second row anyway
|
||||||
s += " {!cyan!}%s" % torrent_id
|
s += " {!cyan!}%s" % torrent_id
|
||||||
self.console.write(s)
|
self.console.write(s)
|
||||||
|
|
||||||
dl_info = "{!info!}DL: {!input!}"
|
dl_info = "{!info!}DL: {!input!}"
|
||||||
dl_info+= "%s" % common.fsize(status["total_done"])
|
dl_info += "%s" % common.fsize(status["total_done"])
|
||||||
if status["total_done"] != status["total_size"]:
|
if status["total_done"] != status["total_size"]:
|
||||||
dl_info+= "/%s" % common.fsize(status["total_size"])
|
dl_info += "/%s" % common.fsize(status["total_size"])
|
||||||
if status["download_payload_rate"] > 0:
|
if status["download_payload_rate"] > 0:
|
||||||
dl_info += " @ %s%s" % (down_color, common.fspeed(status["download_payload_rate"]))
|
dl_info += " @ %s%s" % (down_color, common.fspeed(status["download_payload_rate"]))
|
||||||
|
|
||||||
ul_info = " {!info!}UL: {!input!}"
|
ul_info = " {!info!}UL: {!input!}"
|
||||||
ul_info+= "%s" % common.fsize(status["ratio"] * status["total_done"])
|
ul_info += "%s" % common.fsize(status["ratio"] * status["total_done"])
|
||||||
if status["upload_payload_rate"] > 0:
|
if status["upload_payload_rate"] > 0:
|
||||||
ul_info += " @ %s%s" % (up_color, common.fspeed(status["upload_payload_rate"]))
|
ul_info += " @ %s%s" % (up_color, common.fspeed(status["upload_payload_rate"]))
|
||||||
|
|
||||||
|
|
|
@ -1,48 +1,18 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# manage.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.log import LOG as log
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -59,15 +29,14 @@ torrent_options = {
|
||||||
"remove_at_ratio": bool,
|
"remove_at_ratio": bool,
|
||||||
"move_on_completed": bool,
|
"move_on_completed": bool,
|
||||||
"move_on_completed_path": str
|
"move_on_completed_path": str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Show and manage per-torrent options"""
|
"""Show and manage per-torrent options"""
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-s', '--set', action='store', nargs=2, dest='set',
|
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
|
||||||
help='set value for key'),
|
|
||||||
)
|
)
|
||||||
usage = "Usage: manage <torrent-id> [<key1> [<key2> ...]]\n"\
|
usage = "Usage: manage <torrent-id> [<key1> [<key2> ...]]\n"\
|
||||||
" manage <torrent-id> --set <key> <value>"
|
" manage <torrent-id> --set <key> <value>"
|
||||||
|
@ -79,7 +48,6 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
return self._get_option(*args, **options)
|
return self._get_option(*args, **options)
|
||||||
|
|
||||||
|
|
||||||
def _get_option(self, *args, **options):
|
def _get_option(self, *args, **options):
|
||||||
|
|
||||||
def on_torrents_status(status):
|
def on_torrents_status(status):
|
||||||
|
@ -105,7 +73,7 @@ class Command(BaseCommand):
|
||||||
return
|
return
|
||||||
request_options.append(opt)
|
request_options.append(opt)
|
||||||
if not request_options:
|
if not request_options:
|
||||||
request_options = [ opt for opt in torrent_options.keys() ]
|
request_options = [opt for opt in torrent_options.keys()]
|
||||||
request_options.append('name')
|
request_options.append('name')
|
||||||
|
|
||||||
d = client.core.get_torrents_status({"id": torrent_ids}, request_options)
|
d = client.core.get_torrents_status({"id": torrent_ids}, request_options)
|
||||||
|
@ -113,7 +81,6 @@ class Command(BaseCommand):
|
||||||
d.addErrback(on_torrents_status_fail)
|
d.addErrback(on_torrents_status_fail)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def _set_option(self, *args, **options):
|
def _set_option(self, *args, **options):
|
||||||
deferred = defer.Deferred()
|
deferred = defer.Deferred()
|
||||||
torrent_ids = []
|
torrent_ids = []
|
||||||
|
@ -133,7 +100,6 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
self.console.write("Setting %s to %s for torrents %s.." % (key, val, torrent_ids))
|
self.console.write("Setting %s to %s for torrents %s.." % (key, val, torrent_ids))
|
||||||
|
|
||||||
|
|
||||||
for tid in torrent_ids:
|
for tid in torrent_ids:
|
||||||
if key == "move_on_completed_path":
|
if key == "move_on_completed_path":
|
||||||
client.core.set_torrent_move_completed_path(tid, val).addCallback(on_set_config)
|
client.core.set_torrent_move_completed_path(tid, val).addCallback(on_set_config)
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# move.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# pause.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -42,6 +16,7 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Pause a torrent"""
|
"""Pause a torrent"""
|
||||||
usage = "Usage: pause [ * | <torrent-id> [<torrent-id> ...] ]"
|
usage = "Usage: pause [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,16 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# plugin.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
import re
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -45,25 +18,25 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Manage plugins with this command"""
|
"""Manage plugins with this command"""
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-l', '--list', action='store_true', default=False, dest='list',
|
make_option("-l", "--list", action="store_true", default=False, dest="list",
|
||||||
help='lists available plugins'),
|
help="Lists available plugins"),
|
||||||
make_option('-s', '--show', action='store_true', default=False, dest='show',
|
make_option("-s", "--show", action="store_true", default=False, dest="show",
|
||||||
help='shows enabled plugins'),
|
help="Shows enabled plugins"),
|
||||||
make_option('-e', '--enable', dest='enable',
|
make_option("-e", "--enable", dest="enable",
|
||||||
help='enables a plugin'),
|
help="Enables a plugin"),
|
||||||
make_option('-d', '--disable', dest='disable',
|
make_option("-d", "--disable", dest="disable",
|
||||||
help='disables a plugin'),
|
help="Disables a plugin"),
|
||||||
make_option('-r', '--reload', action='store_true', default=False, dest='reload',
|
make_option("-r", "--reload", action="store_true", default=False, dest="reload",
|
||||||
help='reload list of available plugins'),
|
help="Reload list of available plugins"),
|
||||||
make_option('-i', '--install', dest='plugin_file',
|
make_option("-i", "--install", dest="plugin_file",
|
||||||
help='install a plugin from an .egg file'),
|
help="Install a plugin from an .egg file"),
|
||||||
)
|
)
|
||||||
usage = "Usage: plugin [ -l | --list ]\n"\
|
usage = """Usage: plugin [ -l | --list ]
|
||||||
" plugin [ -s | --show ]\n"\
|
plugin [ -s | --show ]
|
||||||
" plugin [ -e | --enable ] <plugin-name>\n"\
|
plugin [ -e | --enable ] <plugin-name>
|
||||||
" plugin [ -d | --disable ] <plugin-name>\n"\
|
plugin [ -d | --disable ] <plugin-name>
|
||||||
" plugin [ -i | --install ] <plugin-file>\n"\
|
plugin [ -i | --install ] <plugin-file>
|
||||||
" plugin [ -r | --reload]"
|
plugin [ -r | --reload]"""
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
@ -131,7 +104,6 @@ class Command(BaseCommand):
|
||||||
self.console.write("{!error!}Invalid path: %s" % filepath)
|
self.console.write("{!error!}Invalid path: %s" % filepath)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
config_dir = deluge.configmanager.get_config_dir()
|
config_dir = deluge.configmanager.get_config_dir()
|
||||||
filename = os.path.split(filepath)[1]
|
filename = os.path.split(filepath)[1]
|
||||||
|
|
||||||
|
@ -153,6 +125,5 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
self.console.write("{!green!}Plugin was successfully installed: %s" % filename)
|
self.console.write("{!green!}Plugin was successfully installed: %s" % filename)
|
||||||
|
|
||||||
|
|
||||||
def complete(self, line):
|
def complete(self, line):
|
||||||
return component.get("ConsoleUI").tab_complete_path(line, ext=".egg", sort="name", dirs_first=-1)
|
return component.get("ConsoleUI").tab_complete_path(line, ext=".egg", sort="name", dirs_first=-1)
|
||||||
|
|
|
@ -1,38 +1,13 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# quit.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Coptright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
|
|
||||||
from twisted.internet import error, reactor
|
from twisted.internet import error, reactor
|
||||||
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
|
@ -41,8 +16,9 @@ from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Exit from the client."""
|
"""Exit from the client."""
|
||||||
aliases = ['exit']
|
aliases = ["exit"]
|
||||||
interactive_only = True
|
interactive_only = True
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
|
|
@ -1,39 +1,13 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# recheck.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -41,13 +15,14 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Forces a recheck of the torrent data"""
|
"""Forces a recheck of the torrent data"""
|
||||||
usage = "Usage: recheck [ * | <torrent-id> [<torrent-id> ...] ]"
|
usage = "Usage: recheck [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
self.console.write(self.usage)
|
self.console.write(self.usage)
|
||||||
return
|
return
|
||||||
if len(args) > 0 and args[0].lower() == '*':
|
if len(args) > 0 and args[0].lower() == "*":
|
||||||
client.core.force_recheck(self.console.match_torrent(""))
|
client.core.force_recheck(self.console.match_torrent(""))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,41 +1,14 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# resume.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -43,13 +16,14 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Resume a torrent"""
|
"""Resume a torrent"""
|
||||||
usage = "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
|
usage = "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.console = component.get("ConsoleUI")
|
self.console = component.get("ConsoleUI")
|
||||||
|
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
self.console.write(self.usage)
|
self.console.write(self.usage)
|
||||||
return
|
return
|
||||||
if len(args) > 0 and args[0] == '*':
|
if len(args) > 0 and args[0] == "*":
|
||||||
client.core.resume_session()
|
client.core.resume_session()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,16 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# rm.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
@ -44,11 +18,11 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Remove a torrent"""
|
"""Remove a torrent"""
|
||||||
usage = "Usage: rm <torrent-id>"
|
usage = "Usage: rm <torrent-id>"
|
||||||
aliases = ['del']
|
aliases = ["del"]
|
||||||
|
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('--remove_data', action='store_true', default=False,
|
make_option("--remove_data", action="store_true", default=False,
|
||||||
help="remove the torrent's data"),
|
help="remove the torrent's data"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
@ -61,7 +35,7 @@ class Command(BaseCommand):
|
||||||
torrent_ids.extend(self.console.match_torrent(arg))
|
torrent_ids.extend(self.console.match_torrent(arg))
|
||||||
|
|
||||||
for torrent_id in torrent_ids:
|
for torrent_id in torrent_ids:
|
||||||
client.core.remove_torrent(torrent_id, options['remove_data'])
|
client.core.remove_torrent(torrent_id, options["remove_data"])
|
||||||
|
|
||||||
def complete(self, line):
|
def complete(self, line):
|
||||||
# We use the ConsoleUI torrent tab complete method
|
# We use the ConsoleUI torrent tab complete method
|
||||||
|
|
|
@ -1,35 +1,10 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# status.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
@ -45,11 +20,11 @@ from deluge.ui.console.main import BaseCommand
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Shows a various status information from the daemon."""
|
"""Shows a various status information from the daemon."""
|
||||||
option_list = BaseCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('-r', '--raw', action='store_true', default=False, dest='raw',
|
make_option("-r", "--raw", action="store_true", default=False, dest="raw",
|
||||||
help='Don\'t format upload/download rates in KiB/s \
|
help="Don't format upload/download rates in KiB/s \
|
||||||
(useful for scripts that want to do their own parsing)'),
|
(useful for scripts that want to do their own parsing)"),
|
||||||
make_option('-n', '--no-torrents', action='store_false', default=True, dest='show_torrents',
|
make_option("-n", "--no-torrents", action="store_false", default=True, dest="show_torrents",
|
||||||
help='Don\'t show torrent status (this will make the command a bit faster)'),
|
help="Don't show torrent status (this will make the command a bit faster)"),
|
||||||
)
|
)
|
||||||
|
|
||||||
usage = "Usage: status [-r] [-n]"
|
usage = "Usage: status [-r] [-n]"
|
||||||
|
|
|
@ -1,42 +1,14 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# rm.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
from optparse import make_option
|
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console.main import BaseCommand
|
from deluge.ui.console.main import BaseCommand
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,11 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# eventlog.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
|
@ -1,37 +1,11 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# main.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
@ -59,6 +33,7 @@ from deluge.ui.ui import _UI
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Console(_UI):
|
class Console(_UI):
|
||||||
|
|
||||||
help = """Starts the Deluge console interface"""
|
help = """Starts the Deluge console interface"""
|
||||||
|
@ -74,7 +49,8 @@ class Console(_UI):
|
||||||
self.parser.add_option_group(group)
|
self.parser.add_option_group(group)
|
||||||
self.parser.disable_interspersed_args()
|
self.parser.disable_interspersed_args()
|
||||||
|
|
||||||
self.console_cmds = load_commands(os.path.join(UI_PATH, 'commands'))
|
self.console_cmds = load_commands(os.path.join(UI_PATH, "commands"))
|
||||||
|
|
||||||
class CommandOptionGroup(optparse.OptionGroup):
|
class CommandOptionGroup(optparse.OptionGroup):
|
||||||
def __init__(self, parser, title, description=None, cmds=None):
|
def __init__(self, parser, title, description=None, cmds=None):
|
||||||
optparse.OptionGroup.__init__(self, parser, title, description)
|
optparse.OptionGroup.__init__(self, parser, title, description)
|
||||||
|
@ -84,16 +60,17 @@ class Console(_UI):
|
||||||
result = formatter.format_heading(self.title)
|
result = formatter.format_heading(self.title)
|
||||||
formatter.indent()
|
formatter.indent()
|
||||||
if self.description:
|
if self.description:
|
||||||
result += "%s\n"%formatter.format_description(self.description)
|
result += "%s\n" % formatter.format_description(self.description)
|
||||||
for cname in self.cmds:
|
for cname in self.cmds:
|
||||||
cmd = self.cmds[cname]
|
cmd = self.cmds[cname]
|
||||||
if cmd.interactive_only or cname in cmd.aliases: continue
|
if cmd.interactive_only or cname in cmd.aliases:
|
||||||
|
continue
|
||||||
allnames = [cname]
|
allnames = [cname]
|
||||||
allnames.extend(cmd.aliases)
|
allnames.extend(cmd.aliases)
|
||||||
cname = "/".join(allnames)
|
cname = "/".join(allnames)
|
||||||
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
|
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
|
||||||
formatter.indent()
|
formatter.indent()
|
||||||
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage.split('\n')[0])
|
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage.split("\n")[0])
|
||||||
formatter.dedent()
|
formatter.dedent()
|
||||||
formatter.dedent()
|
formatter.dedent()
|
||||||
return result
|
return result
|
||||||
|
@ -110,9 +87,11 @@ class Console(_UI):
|
||||||
ConsoleUI(self.args, self.console_cmds, (self.options.daemon_addr, self.options.daemon_port,
|
ConsoleUI(self.args, self.console_cmds, (self.options.daemon_addr, self.options.daemon_port,
|
||||||
self.options.daemon_user, self.options.daemon_pass))
|
self.options.daemon_user, self.options.daemon_pass))
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
Console().start()
|
Console().start()
|
||||||
|
|
||||||
|
|
||||||
class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
|
class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
|
||||||
"""
|
"""
|
||||||
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
|
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
|
||||||
|
@ -165,7 +144,7 @@ class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
|
||||||
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
||||||
opts = self._format_colors(opts)
|
opts = self._format_colors(opts)
|
||||||
indent_first = self.help_position
|
indent_first = self.help_position
|
||||||
else: # start help on same line as opts
|
else: # start help on same line as opts
|
||||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
||||||
opts = self._format_colors(opts)
|
opts = self._format_colors(opts)
|
||||||
indent_first = 0
|
indent_first = 0
|
||||||
|
@ -181,6 +160,7 @@ class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
|
||||||
result.append("\n")
|
result.append("\n")
|
||||||
return "".join(result)
|
return "".join(result)
|
||||||
|
|
||||||
|
|
||||||
class OptionParser(optparse.OptionParser):
|
class OptionParser(optparse.OptionParser):
|
||||||
"""subclass from optparse.OptionParser so exit() won't exit."""
|
"""subclass from optparse.OptionParser so exit() won't exit."""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -202,13 +182,13 @@ class OptionParser(optparse.OptionParser):
|
||||||
"""
|
"""
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
def print_usage(self, file = None):
|
def print_usage(self, file=None):
|
||||||
console = component.get("ConsoleUI")
|
console = component.get("ConsoleUI")
|
||||||
if self.usage:
|
if self.usage:
|
||||||
for line in self.get_usage().splitlines():
|
for line in self.get_usage().splitlines():
|
||||||
console.write(line)
|
console.write(line)
|
||||||
|
|
||||||
def print_help(self, file = None):
|
def print_help(self, file=None):
|
||||||
console = component.get("ConsoleUI")
|
console = component.get("ConsoleUI")
|
||||||
console.set_batch_write(True)
|
console.set_batch_write(True)
|
||||||
for line in self.format_help().splitlines():
|
for line in self.format_help().splitlines():
|
||||||
|
@ -235,19 +215,20 @@ class OptionParser(optparse.OptionParser):
|
||||||
|
|
||||||
class BaseCommand(object):
|
class BaseCommand(object):
|
||||||
|
|
||||||
usage = 'usage'
|
usage = "usage"
|
||||||
interactive_only = False
|
interactive_only = False
|
||||||
option_list = tuple()
|
option_list = tuple()
|
||||||
aliases = []
|
aliases = []
|
||||||
|
|
||||||
def complete(self, text, *args):
|
def complete(self, text, *args):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return 'base'
|
return "base"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def epilog(self):
|
def epilog(self):
|
||||||
|
@ -255,33 +236,30 @@ class BaseCommand(object):
|
||||||
|
|
||||||
def split(self, text):
|
def split(self, text):
|
||||||
if deluge.common.windows_check():
|
if deluge.common.windows_check():
|
||||||
text = text.replace('\\', '\\\\')
|
text = text.replace("\\", "\\\\")
|
||||||
result = shlex.split(text)
|
result = shlex.split(text)
|
||||||
for i, s in enumerate(result):
|
for i, s in enumerate(result):
|
||||||
result[i] = s.replace(r'\ ', ' ')
|
result[i] = s.replace(r"\ ", " ")
|
||||||
result = filter(lambda s: s != '', result)
|
result = filter(lambda s: s != "", result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def create_parser(self):
|
def create_parser(self):
|
||||||
return OptionParser(prog = self.name,
|
return OptionParser(prog=self.name, usage=self.usage, epilog=self.epilog, option_list=self.option_list)
|
||||||
usage = self.usage,
|
|
||||||
epilog = self.epilog,
|
|
||||||
option_list = self.option_list)
|
|
||||||
|
|
||||||
|
|
||||||
def load_commands(command_dir, exclude=[]):
|
def load_commands(command_dir, exclude=[]):
|
||||||
def get_command(name):
|
def get_command(name):
|
||||||
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
return getattr(__import__("deluge.ui.console.commands.%s" % name, {}, {}, ["Command"]), "Command")()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
commands = []
|
commands = []
|
||||||
for filename in os.listdir(command_dir):
|
for filename in os.listdir(command_dir):
|
||||||
if filename.split('.')[0] in exclude or filename.startswith('_'):
|
if filename.split(".")[0] in exclude or filename.startswith("_"):
|
||||||
continue
|
continue
|
||||||
if not (filename.endswith('.py') or filename.endswith('.pyc')):
|
if not (filename.endswith(".py") or filename.endswith(".pyc")):
|
||||||
continue
|
continue
|
||||||
cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
|
cmd = get_command(filename.split(".")[len(filename.split(".")) - 2])
|
||||||
aliases = [ filename.split('.')[len(filename.split('.')) - 2] ]
|
aliases = [filename.split(".")[len(filename.split(".")) - 2]]
|
||||||
aliases.extend(cmd.aliases)
|
aliases.extend(cmd.aliases)
|
||||||
for a in aliases:
|
for a in aliases:
|
||||||
commands.append((a, cmd))
|
commands.append((a, cmd))
|
||||||
|
@ -298,14 +276,13 @@ class ConsoleUI(component.Component):
|
||||||
self.events = []
|
self.events = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
locale.setlocale(locale.LC_ALL, "")
|
||||||
self.encoding = locale.getpreferredencoding()
|
self.encoding = locale.getpreferredencoding()
|
||||||
except:
|
except:
|
||||||
self.encoding = sys.getdefaultencoding()
|
self.encoding = sys.getdefaultencoding()
|
||||||
|
|
||||||
log.debug("Using encoding: %s", self.encoding)
|
log.debug("Using encoding: %s", self.encoding)
|
||||||
|
|
||||||
|
|
||||||
# start up the session proxy
|
# start up the session proxy
|
||||||
self.sessionproxy = SessionProxy()
|
self.sessionproxy = SessionProxy()
|
||||||
|
|
||||||
|
@ -315,7 +292,7 @@ class ConsoleUI(component.Component):
|
||||||
self.interactive = True
|
self.interactive = True
|
||||||
self._commands = cmds
|
self._commands = cmds
|
||||||
if args:
|
if args:
|
||||||
args = ' '.join(args)
|
args = " ".join(args)
|
||||||
self.interactive = False
|
self.interactive = False
|
||||||
if not cmds:
|
if not cmds:
|
||||||
print("Sorry, couldn't find any commands")
|
print("Sorry, couldn't find any commands")
|
||||||
|
@ -373,12 +350,12 @@ Please use commands from the command line, eg:\n
|
||||||
# Start the twisted mainloop
|
# Start the twisted mainloop
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
# Maintain a list of (torrent_id, name) for use in tab completion
|
# Maintain a list of (torrent_id, name) for use in tab completion
|
||||||
self.torrents = []
|
self.torrents = []
|
||||||
if not self.interactive:
|
if not self.interactive:
|
||||||
self.started_deferred = defer.Deferred()
|
self.started_deferred = defer.Deferred()
|
||||||
|
|
||||||
def on_session_state(result):
|
def on_session_state(result):
|
||||||
def on_torrents_status(torrents):
|
def on_torrents_status(torrents):
|
||||||
for torrent_id, status in torrents.items():
|
for torrent_id, status in torrents.items():
|
||||||
|
@ -388,7 +365,6 @@ Please use commands from the command line, eg:\n
|
||||||
client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status)
|
client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status)
|
||||||
client.core.get_session_state().addCallback(on_session_state)
|
client.core.get_session_state().addCallback(on_session_state)
|
||||||
|
|
||||||
|
|
||||||
def match_torrent(self, string):
|
def match_torrent(self, string):
|
||||||
"""
|
"""
|
||||||
Returns a list of torrent_id matches for the string. It will search both
|
Returns a list of torrent_id matches for the string. It will search both
|
||||||
|
@ -402,7 +378,7 @@ Please use commands from the command line, eg:\n
|
||||||
"""
|
"""
|
||||||
if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy):
|
if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy):
|
||||||
return self.screen.match_torrent(string)
|
return self.screen.match_torrent(string)
|
||||||
matches = []
|
matches = []
|
||||||
|
|
||||||
string = string.decode(self.encoding)
|
string = string.decode(self.encoding)
|
||||||
for tid, name in self.torrents:
|
for tid, name in self.torrents:
|
||||||
|
@ -411,7 +387,6 @@ Please use commands from the command line, eg:\n
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
def get_torrent_name(self, torrent_id):
|
def get_torrent_name(self, torrent_id):
|
||||||
if self.interactive and hasattr(self.screen, "get_torrent_name"):
|
if self.interactive and hasattr(self.screen, "get_torrent_name"):
|
||||||
return self.screen.get_torrent_name(torrent_id)
|
return self.screen.get_torrent_name(torrent_id)
|
||||||
|
@ -422,7 +397,6 @@ Please use commands from the command line, eg:\n
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def set_batch_write(self, batch):
|
def set_batch_write(self, batch):
|
||||||
if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy):
|
if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy):
|
||||||
return self.screen.set_batch_write(batch)
|
return self.screen.set_batch_write(batch)
|
||||||
|
|
|
@ -1,61 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# add_util.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
|
||||||
#
|
|
||||||
# Modified function from commands/add.py:
|
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import glob
|
import glob
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.common import TorrentInfo
|
from deluge.ui.common import TorrentInfo
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def __bracket_fixup(path):
|
def __bracket_fixup(path):
|
||||||
if (path.find("[") == -1 and
|
if (path.find("[") == -1 and path.find("]") == -1):
|
||||||
path.find("]") == -1):
|
|
||||||
return path
|
return path
|
||||||
sentinal = 256
|
sentinal = 256
|
||||||
while (path.find(unichr(sentinal)) != -1):
|
while (path.find(unichr(sentinal)) != -1):
|
||||||
sentinal+=1
|
sentinal += 1
|
||||||
if sentinal > 65535:
|
if sentinal > 65535:
|
||||||
log.error("Can't fix brackets in path, path contains all possible sentinal characters")
|
log.error("Can't fix brackets in path, path contains all possible sentinal characters")
|
||||||
return path
|
return path
|
||||||
newpath = path.replace("]", unichr(sentinal))
|
newpath = path.replace("]", unichr(sentinal))
|
||||||
|
@ -63,16 +35,17 @@ def __bracket_fixup(path):
|
||||||
newpath = newpath.replace(unichr(sentinal), "[]]")
|
newpath = newpath.replace(unichr(sentinal), "[]]")
|
||||||
return newpath
|
return newpath
|
||||||
|
|
||||||
|
|
||||||
def add_torrent(t_file, options, success_cb, fail_cb, ress):
|
def add_torrent(t_file, options, success_cb, fail_cb, ress):
|
||||||
t_options = {}
|
t_options = {}
|
||||||
if options["path"]:
|
if options["path"]:
|
||||||
t_options["download_location"] = os.path.expanduser(options["path"])
|
t_options["download_location"] = os.path.expanduser(options["path"])
|
||||||
t_options["add_paused"] = options["add_paused"]
|
t_options["add_paused"] = options["add_paused"]
|
||||||
|
|
||||||
is_url = (not (options["path_type"]==1)) and (deluge.common.is_url(t_file) or options["path_type"]==2)
|
is_url = (not (options["path_type"] == 1)) and (deluge.common.is_url(t_file) or options["path_type"] == 2)
|
||||||
is_mag = not(is_url) and (not (options["path_type"]==1)) and deluge.common.is_magnet(t_file)
|
is_magnet = not(is_url) and (not (options["path_type"] == 1)) and deluge.common.is_magnet(t_file)
|
||||||
|
|
||||||
if is_url or is_mag:
|
if is_url or is_magnet:
|
||||||
files = [t_file]
|
files = [t_file]
|
||||||
else:
|
else:
|
||||||
files = glob.glob(__bracket_fixup(t_file))
|
files = glob.glob(__bracket_fixup(t_file))
|
||||||
|
@ -85,7 +58,7 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress):
|
||||||
for f in files:
|
for f in files:
|
||||||
if is_url:
|
if is_url:
|
||||||
client.core.add_torrent_url(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
client.core.add_torrent_url(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
||||||
elif is_mag:
|
elif is_magnet:
|
||||||
client.core.add_torrent_magnet(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
client.core.add_torrent_magnet(f, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
||||||
else:
|
else:
|
||||||
if not os.path.exists(f):
|
if not os.path.exists(f):
|
||||||
|
@ -104,4 +77,5 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress):
|
||||||
filename = os.path.split(f)[-1]
|
filename = os.path.split(f)[-1]
|
||||||
filedump = base64.encodestring(open(f).read())
|
filedump = base64.encodestring(open(f).read())
|
||||||
|
|
||||||
client.core.add_torrent_file(filename, filedump, t_options).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
client.core.add_torrent_file(filename, filedump, t_options
|
||||||
|
).addCallback(success_cb, f, ress).addErrback(fail_cb, f, ress)
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# addtorrents.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2012 Arek Stefański <asmageddon@gmail.com>
|
# Copyright (C) 2012 Arek Stefański <asmageddon@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
@ -40,13 +13,11 @@ import os
|
||||||
|
|
||||||
import deluge.common as common
|
import deluge.common as common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
|
||||||
import format_utils
|
import format_utils
|
||||||
from basemode import BaseMode
|
from basemode import BaseMode
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.sessionproxy import SessionProxy
|
|
||||||
from input_popup import InputPopup
|
from input_popup import InputPopup
|
||||||
from popup import MessagePopup, Popup
|
from popup import MessagePopup
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import curses
|
import curses
|
||||||
|
@ -83,6 +54,7 @@ if a file is highlighted
|
||||||
{!info!}'q'{!normal!} - Go back to torrent overview
|
{!info!}'q'{!normal!} - Go back to torrent overview
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class AddTorrents(BaseMode, component.Component):
|
class AddTorrents(BaseMode, component.Component):
|
||||||
def __init__(self, alltorrentmode, stdscr, console_config, encoding=None):
|
def __init__(self, alltorrentmode, stdscr, console_config, encoding=None):
|
||||||
|
|
||||||
|
@ -118,7 +90,6 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
self.__refresh_listing()
|
self.__refresh_listing()
|
||||||
|
|
||||||
|
|
||||||
component.Component.__init__(self, "AddTorrents", 1, depend=["SessionProxy"])
|
component.Component.__init__(self, "AddTorrents", 1, depend=["SessionProxy"])
|
||||||
|
|
||||||
component.start(["AddTorrents"])
|
component.start(["AddTorrents"])
|
||||||
|
@ -129,6 +100,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
# component start/update
|
# component start/update
|
||||||
def start(self):
|
def start(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -168,8 +140,8 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
row = [dirname, size, time, full_path, 1]
|
row = [dirname, size, time, full_path, 1]
|
||||||
|
|
||||||
self.raw_rows.append( row )
|
self.raw_rows.append(row)
|
||||||
self.raw_rows_dirs.append( row )
|
self.raw_rows_dirs.append(row)
|
||||||
|
|
||||||
#Highlight the directory we came from
|
#Highlight the directory we came from
|
||||||
if self.path_stack_pos < len(self.path_stack):
|
if self.path_stack_pos < len(self.path_stack):
|
||||||
|
@ -189,8 +161,8 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
row = [filename, size, time, full_path, 0]
|
row = [filename, size, time, full_path, 0]
|
||||||
|
|
||||||
self.raw_rows.append( row )
|
self.raw_rows.append(row)
|
||||||
self.raw_rows_files.append( row )
|
self.raw_rows_files.append(row)
|
||||||
|
|
||||||
self.__sort_rows()
|
self.__sort_rows()
|
||||||
|
|
||||||
|
@ -201,7 +173,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
self.raw_rows_dirs.sort(key=lambda r: r[0].lower())
|
self.raw_rows_dirs.sort(key=lambda r: r[0].lower())
|
||||||
|
|
||||||
if self.sort_column == "name":
|
if self.sort_column == "name":
|
||||||
self.raw_rows_files.sort(key=lambda r: r[0].lower(), reverse=self.reverse_sort)
|
self.raw_rows_files.sort(key=lambda r: r[0].lower(), reverse=self.reverse_sort)
|
||||||
elif self.sort_column == "date":
|
elif self.sort_column == "date":
|
||||||
self.raw_rows_files.sort(key=lambda r: r[2], reverse=self.reverse_sort)
|
self.raw_rows_files.sort(key=lambda r: r[2], reverse=self.reverse_sort)
|
||||||
|
@ -275,7 +247,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self,lines=None):
|
def refresh(self, lines=None):
|
||||||
|
|
||||||
# Update the status bars
|
# Update the status bars
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
|
@ -287,7 +259,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
string = self.statusbars.bottombar
|
string = self.statusbars.bottombar
|
||||||
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
||||||
|
|
||||||
string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
||||||
|
|
||||||
self.add_string(self.rows - 1, string)
|
self.add_string(self.rows - 1, string)
|
||||||
except:
|
except:
|
||||||
|
@ -321,8 +293,10 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
s = ""
|
s = ""
|
||||||
for i, (c, w) in enumerate(zip(cols, widths)):
|
for i, (c, w) in enumerate(zip(cols, widths)):
|
||||||
cn = ""
|
cn = ""
|
||||||
if i == 0: cn = "name"
|
if i == 0:
|
||||||
elif i == 2: cn = "date"
|
cn = "name"
|
||||||
|
elif i == 2:
|
||||||
|
cn = "date"
|
||||||
|
|
||||||
if cn == self.sort_column:
|
if cn == self.sort_column:
|
||||||
s += "{!black,green,bold!}" + c.ljust(w - 2)
|
s += "{!black,green,bold!}" + c.ljust(w - 2)
|
||||||
|
@ -361,7 +335,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
color_string = "{!white,blue,bold!}"
|
color_string = "{!white,blue,bold!}"
|
||||||
|
|
||||||
self.add_string(off, color_string + row)
|
self.add_string(off, color_string + row)
|
||||||
off+= 1
|
off += 1
|
||||||
|
|
||||||
if off > self.rows - 2:
|
if off > self.rows - 2:
|
||||||
break
|
break
|
||||||
|
@ -403,7 +377,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
self.path_stack = self.path_stack[:self.path_stack_pos]
|
self.path_stack = self.path_stack[:self.path_stack_pos]
|
||||||
self.path_stack.append(dirname)
|
self.path_stack.append(dirname)
|
||||||
|
|
||||||
path = os.path.join(*self.path_stack[:self.path_stack_pos+1])
|
path = os.path.join(*self.path_stack[:self.path_stack_pos + 1])
|
||||||
|
|
||||||
if not os.access(path, os.R_OK):
|
if not os.access(path, os.R_OK):
|
||||||
self.path_stack = self.path_stack[:self.path_stack_pos]
|
self.path_stack = self.path_stack[:self.path_stack_pos]
|
||||||
|
@ -423,23 +397,20 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
def _show_add_dialog(self):
|
def _show_add_dialog(self):
|
||||||
|
|
||||||
def _do_add(result):
|
def _do_add(result):
|
||||||
ress = {"succ":0,
|
ress = {"succ": 0, "fail": 0, "total": len(self.marked), "fmsg": []}
|
||||||
"fail":0,
|
|
||||||
"total": len(self.marked),
|
|
||||||
"fmsg":[]}
|
|
||||||
|
|
||||||
def fail_cb(msg, t_file, ress):
|
def fail_cb(msg, t_file, ress):
|
||||||
log.debug("failed to add torrent: %s: %s"%(t_file, msg))
|
log.debug("failed to add torrent: %s: %s" % (t_file, msg))
|
||||||
ress["fail"]+=1
|
ress["fail"] += 1
|
||||||
ress["fmsg"].append("{!input!} * %s: {!error!}%s"%(t_file, msg))
|
ress["fmsg"].append("{!input!} * %s: {!error!}%s" % (t_file, msg))
|
||||||
if (ress["succ"]+ress["fail"]) >= ress["total"]:
|
if (ress["succ"] + ress["fail"]) >= ress["total"]:
|
||||||
self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"])
|
self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"])
|
||||||
|
|
||||||
def success_cb(tid, t_file, ress):
|
def success_cb(tid, t_file, ress):
|
||||||
if tid:
|
if tid:
|
||||||
log.debug("added torrent: %s (%s)"%(t_file, tid))
|
log.debug("added torrent: %s (%s)" % (t_file, tid))
|
||||||
ress["succ"]+=1
|
ress["succ"] += 1
|
||||||
if (ress["succ"]+ress["fail"]) >= ress["total"]:
|
if (ress["succ"] + ress["fail"]) >= ress["total"]:
|
||||||
self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"])
|
self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"])
|
||||||
else:
|
else:
|
||||||
fail_cb("Already in session (probably)", t_file, ress)
|
fail_cb("Already in session (probably)", t_file, ress)
|
||||||
|
@ -485,7 +456,6 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
self.popup.add_text_input("Download Folder:", "location", dl)
|
self.popup.add_text_input("Download Folder:", "location", dl)
|
||||||
self.popup.add_select_input("Add Paused:", "add_paused", ["Yes", "No"], [True, False], ap)
|
self.popup.add_select_input("Add Paused:", "add_paused", ["Yes", "No"], [True, False], ap)
|
||||||
|
|
||||||
|
|
||||||
def _go_up(self):
|
def _go_up(self):
|
||||||
#Go up in directory hierarchy
|
#Go up in directory hierarchy
|
||||||
if self.path_stack_pos > 1:
|
if self.path_stack_pos > 1:
|
||||||
|
@ -508,7 +478,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
return
|
return
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'Q':
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -517,7 +487,7 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'q':
|
elif chr(c) == "q":
|
||||||
self.back_to_overview()
|
self.back_to_overview()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -549,23 +519,23 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
self.back_to_overview()
|
self.back_to_overview()
|
||||||
else:
|
else:
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'h':
|
if chr(c) == "h":
|
||||||
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
||||||
elif chr(c) == '>':
|
elif chr(c) == ">":
|
||||||
if self.sort_column == "date":
|
if self.sort_column == "date":
|
||||||
self.reverse_sort = not self.reverse_sort
|
self.reverse_sort = not self.reverse_sort
|
||||||
else:
|
else:
|
||||||
self.sort_column = "date"
|
self.sort_column = "date"
|
||||||
self.reverse_sort = True
|
self.reverse_sort = True
|
||||||
self.__sort_rows()
|
self.__sort_rows()
|
||||||
elif chr(c) == '<':
|
elif chr(c) == "<":
|
||||||
if self.sort_column == "name":
|
if self.sort_column == "name":
|
||||||
self.reverse_sort = not self.reverse_sort
|
self.reverse_sort = not self.reverse_sort
|
||||||
else:
|
else:
|
||||||
self.sort_column = "name"
|
self.sort_column = "name"
|
||||||
self.reverse_sort = False
|
self.reverse_sort = False
|
||||||
self.__sort_rows()
|
self.__sort_rows()
|
||||||
elif chr(c) == 'm':
|
elif chr(c) == "m":
|
||||||
s = self.raw_rows[self.cursel][0]
|
s = self.raw_rows[self.cursel][0]
|
||||||
if s in self.marked:
|
if s in self.marked:
|
||||||
self.marked.remove(s)
|
self.marked.remove(s)
|
||||||
|
@ -573,11 +543,11 @@ class AddTorrents(BaseMode, component.Component):
|
||||||
self.marked.add(s)
|
self.marked.add(s)
|
||||||
|
|
||||||
self.last_mark = self.cursel
|
self.last_mark = self.cursel
|
||||||
elif chr(c) == 'j':
|
elif chr(c) == "j":
|
||||||
self.scroll_list_up(1)
|
self.scroll_list_up(1)
|
||||||
elif chr(c) == 'k':
|
elif chr(c) == "k":
|
||||||
self.scroll_list_down(1)
|
self.scroll_list_down(1)
|
||||||
elif chr(c) == 'M':
|
elif chr(c) == "M":
|
||||||
if self.last_mark != -1:
|
if self.last_mark != -1:
|
||||||
if self.last_mark > self.cursel:
|
if self.last_mark > self.cursel:
|
||||||
m = range(self.cursel, self.last_mark)
|
m = range(self.cursel, self.last_mark)
|
||||||
|
|
|
@ -1,58 +1,27 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# alltorrents.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from twisted.internet import defer
|
import column as console_column
|
||||||
|
|
||||||
import column
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import format_utils
|
import format_utils
|
||||||
from add_util import add_torrent
|
|
||||||
from addtorrents import AddTorrents
|
from addtorrents import AddTorrents
|
||||||
from basemode import BaseMode
|
from basemode import BaseMode
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.sessionproxy import SessionProxy
|
|
||||||
from eventview import EventView
|
from eventview import EventView
|
||||||
from input_popup import ALIGN, InputPopup
|
from input_popup import InputPopup
|
||||||
from legacy import Legacy
|
from legacy import Legacy
|
||||||
from popup import ALIGN, MessagePopup, Popup, SelectablePopup
|
from popup import MessagePopup, Popup, SelectablePopup
|
||||||
from preferences import Preferences
|
from preferences import Preferences
|
||||||
from torrent_actions import ACTION, torrent_actions_popup
|
from torrent_actions import ACTION, torrent_actions_popup
|
||||||
from torrentdetail import TorrentDetail
|
from torrentdetail import TorrentDetail
|
||||||
|
@ -133,17 +102,18 @@ files in the torrent and the ability to set file priorities.
|
||||||
input something
|
input something
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class FILTER:
|
class FILTER:
|
||||||
ALL=0
|
ALL = 0
|
||||||
ACTIVE=1
|
ACTIVE = 1
|
||||||
DOWNLOADING=2
|
DOWNLOADING = 2
|
||||||
SEEDING=3
|
SEEDING = 3
|
||||||
PAUSED=4
|
PAUSED = 4
|
||||||
CHECKING=5
|
CHECKING = 5
|
||||||
ERROR=6
|
ERROR = 6
|
||||||
QUEUED=7
|
QUEUED = 7
|
||||||
ALLOCATING=8
|
ALLOCATING = 8
|
||||||
MOVING=9
|
MOVING = 9
|
||||||
|
|
||||||
DEFAULT_PREFS = {
|
DEFAULT_PREFS = {
|
||||||
"show_queue": True,
|
"show_queue": True,
|
||||||
|
@ -199,21 +169,21 @@ DEFAULT_PREFS = {
|
||||||
"down_limit_width": 7,
|
"down_limit_width": 7,
|
||||||
"up_limit_width": 7,
|
"up_limit_width": 7,
|
||||||
"shared_width": 10,
|
"shared_width": 10,
|
||||||
"ignore_duplicate_lines": False,
|
"ignore_duplicate_lines": False,
|
||||||
"move_selection": True,
|
"move_selection": True,
|
||||||
"third_tab_lists_all": False,
|
"third_tab_lists_all": False,
|
||||||
"torrents_per_tab_press": 15,
|
"torrents_per_tab_press": 15,
|
||||||
"sort_primary": "queue",
|
"sort_primary": "queue",
|
||||||
"sort_secondary": "name",
|
"sort_secondary": "name",
|
||||||
"separate_complete": True,
|
"separate_complete": True,
|
||||||
"ring_bell": False,
|
"ring_bell": False,
|
||||||
"save_legacy_history": True,
|
"save_legacy_history": True,
|
||||||
"first_run": True,
|
"first_run": True,
|
||||||
"addtorrents_show_misc_files": False, #TODO: Showing/hiding this
|
"addtorrents_show_misc_files": False, # TODO: Showing/hiding this
|
||||||
"addtorrents_show_hidden_folders": False, #TODO: Showing/hiding this
|
"addtorrents_show_hidden_folders": False, # TODO: Showing/hiding this
|
||||||
"addtorrents_sort_column": "date",
|
"addtorrents_sort_column": "date",
|
||||||
"addtorrents_reverse_sort": True,
|
"addtorrents_reverse_sort": True,
|
||||||
"addtorrents_last_path": "~"
|
"addtorrents_last_path": "~"
|
||||||
}
|
}
|
||||||
|
|
||||||
column_pref_names = ["queue", "name", "size", "state", "progress", "seeds",
|
column_pref_names = ["queue", "name", "size", "state", "progress", "seeds",
|
||||||
|
@ -300,13 +270,14 @@ SEARCH_SUCCESS = 2
|
||||||
SEARCH_START_REACHED = 3
|
SEARCH_START_REACHED = 3
|
||||||
SEARCH_END_REACHED = 4
|
SEARCH_END_REACHED = 4
|
||||||
|
|
||||||
|
|
||||||
class AllTorrents(BaseMode, component.Component):
|
class AllTorrents(BaseMode, component.Component):
|
||||||
def __init__(self, stdscr, encoding=None):
|
def __init__(self, stdscr, encoding=None):
|
||||||
self.torrent_names = None
|
self.torrent_names = None
|
||||||
self.numtorrents = -1
|
self.numtorrents = -1
|
||||||
self._cached_rows = {}
|
self._cached_rows = {}
|
||||||
self.cursel = 1
|
self.cursel = 1
|
||||||
self.curoff = 1 # TODO: this should really be 0 indexed
|
self.curoff = 1 # TODO: this should really be 0 indexed
|
||||||
self.column_string = ""
|
self.column_string = ""
|
||||||
self.popup = None
|
self.popup = None
|
||||||
self.messages = deque()
|
self.messages = deque()
|
||||||
|
@ -357,15 +328,15 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
("Availability", format_utils.format_float, ("distributed_copies",)),
|
("Availability", format_utils.format_float, ("distributed_copies",)),
|
||||||
("Pieces", format_utils.format_pieces, ("num_pieces", "piece_length")),
|
("Pieces", format_utils.format_pieces, ("num_pieces", "piece_length")),
|
||||||
("Seed Rank", str, ("seed_rank",)),
|
("Seed Rank", str, ("seed_rank",)),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.__status_keys = ["name", "state", "download_payload_rate", "upload_payload_rate",
|
self.__status_keys = ["name", "state", "download_payload_rate", "upload_payload_rate",
|
||||||
"progress", "eta", "download_location", "all_time_download", "total_uploaded",
|
"progress", "eta", "download_location", "all_time_download", "total_uploaded",
|
||||||
"ratio", "num_seeds", "total_seeds", "num_peers", "total_peers",
|
"ratio", "num_seeds", "total_seeds", "num_peers", "total_peers",
|
||||||
"active_time", "seeding_time", "last_seen_complete", "time_added",
|
"active_time", "seeding_time", "last_seen_complete", "time_added",
|
||||||
"completed_time", "distributed_copies", "num_pieces", "piece_length",
|
"completed_time", "distributed_copies", "num_pieces", "piece_length",
|
||||||
"seed_rank"
|
"seed_rank"
|
||||||
]
|
]
|
||||||
|
|
||||||
self.legacy_mode = Legacy(self.stdscr, self.encoding)
|
self.legacy_mode = Legacy(self.stdscr, self.encoding)
|
||||||
|
|
||||||
|
@ -376,25 +347,25 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
# component start/update
|
# component start/update
|
||||||
def start(self):
|
def start(self):
|
||||||
component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state, False)
|
component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields
|
||||||
|
).addCallback(self.set_state, False)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state, True)
|
component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields
|
||||||
|
).addCallback(self.set_state, True)
|
||||||
if self.__torrent_info_id:
|
if self.__torrent_info_id:
|
||||||
component.get("SessionProxy").get_torrent_status(self.__torrent_info_id, self.__status_keys).addCallback(self._on_torrent_status)
|
component.get("SessionProxy").get_torrent_status(self.__torrent_info_id, self.__status_keys
|
||||||
|
).addCallback(self._on_torrent_status)
|
||||||
|
|
||||||
def update_config(self):
|
def update_config(self):
|
||||||
self.config = ConfigManager("console.conf", DEFAULT_PREFS)
|
self.config = ConfigManager("console.conf", DEFAULT_PREFS)
|
||||||
s_primary = self.config["sort_primary"]
|
s_primary = self.config["sort_primary"]
|
||||||
s_secondary = self.config["sort_secondary"]
|
s_secondary = self.config["sort_secondary"]
|
||||||
self.__cols_to_show = [
|
self.__cols_to_show = [pref for pref in column_pref_names
|
||||||
pref for pref in column_pref_names
|
if ("show_%s" % pref) not in self.config or self.config["show_%s" % pref]]
|
||||||
if ("show_%s" % pref) not in self.config
|
|
||||||
or self.config["show_%s"%pref]
|
|
||||||
]
|
|
||||||
|
|
||||||
self.__columns = [prefs_to_names[col] for col in self.__cols_to_show]
|
self.__columns = [prefs_to_names[col] for col in self.__cols_to_show]
|
||||||
self.__status_fields = column.get_required_fields(self.__columns)
|
self.__status_fields = console_column.get_required_fields(self.__columns)
|
||||||
|
|
||||||
# we always need these, even if we're not displaying them
|
# we always need these, even if we're not displaying them
|
||||||
for rf in ["state", "name", "queue", "progress"]:
|
for rf in ["state", "name", "queue", "progress"]:
|
||||||
|
@ -414,17 +385,17 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def __update_columns(self):
|
def __update_columns(self):
|
||||||
self.column_widths = [self.config["%s_width"%c] for c in self.__cols_to_show]
|
self.column_widths = [self.config["%s_width" % c] for c in self.__cols_to_show]
|
||||||
req = sum(filter(lambda x:x >= 0, self.column_widths))
|
req = sum(filter(lambda x: x >= 0, self.column_widths))
|
||||||
if (req > self.cols): # can't satisfy requests, just spread out evenly
|
if (req > self.cols): # can't satisfy requests, just spread out evenly
|
||||||
cw = int(self.cols/len(self.__columns))
|
cw = int(self.cols / len(self.__columns))
|
||||||
for i in range(0, len(self.column_widths)):
|
for i in range(0, len(self.column_widths)):
|
||||||
self.column_widths[i] = cw
|
self.column_widths[i] = cw
|
||||||
else:
|
else:
|
||||||
rem = self.cols - req
|
rem = self.cols - req
|
||||||
var_cols = len(filter(lambda x: x < 0, self.column_widths))
|
var_cols = len(filter(lambda x: x < 0, self.column_widths))
|
||||||
if (var_cols > 0):
|
if (var_cols > 0):
|
||||||
vw = int(rem/var_cols)
|
vw = int(rem / var_cols)
|
||||||
for i in range(0, len(self.column_widths)):
|
for i in range(0, len(self.column_widths)):
|
||||||
if (self.column_widths[i] < 0):
|
if (self.column_widths[i] < 0):
|
||||||
self.column_widths[i] = vw
|
self.column_widths[i] = vw
|
||||||
|
@ -457,7 +428,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.column_string += ccol
|
self.column_string += ccol
|
||||||
|
|
||||||
def set_state(self, state, refresh):
|
def set_state(self, state, refresh):
|
||||||
self.curstate = state # cache in case we change sort order
|
self.curstate = state # cache in case we change sort order
|
||||||
newnames = []
|
newnames = []
|
||||||
self._cached_rows = {}
|
self._cached_rows = {}
|
||||||
self._sorted_ids = self._sort_torrents(self.curstate)
|
self._sorted_ids = self._sort_torrents(self.curstate)
|
||||||
|
@ -492,50 +463,48 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
def current_torrent_id(self):
|
def current_torrent_id(self):
|
||||||
if self._sorted_ids:
|
if self._sorted_ids:
|
||||||
return self._sorted_ids[self.cursel-1]
|
return self._sorted_ids[self.cursel - 1]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _selected_torrent_ids(self):
|
def _selected_torrent_ids(self):
|
||||||
ret = []
|
ret = []
|
||||||
for i in self.marked:
|
for i in self.marked:
|
||||||
ret.append(self._sorted_ids[i-1])
|
ret.append(self._sorted_ids[i - 1])
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _on_torrent_status(self, state):
|
def _on_torrent_status(self, state):
|
||||||
if (self.popup):
|
if (self.popup):
|
||||||
self.popup.clear()
|
self.popup.clear()
|
||||||
name = state["name"]
|
name = state["name"]
|
||||||
off = int((self.cols/4)-(len(name)/2))
|
|
||||||
self.popup.set_title(name)
|
self.popup.set_title(name)
|
||||||
for i, f in enumerate(self._info_fields):
|
for i, f in enumerate(self._info_fields):
|
||||||
if f[1] != None:
|
if f[1] is not None:
|
||||||
args = []
|
args = []
|
||||||
try:
|
try:
|
||||||
for key in f[2]:
|
for key in f[2]:
|
||||||
args.append(state[key])
|
args.append(state[key])
|
||||||
except:
|
except Exception as ex:
|
||||||
log.debug("Could not get info field: %s", e)
|
log.debug("Could not get info field: %s", ex)
|
||||||
continue
|
continue
|
||||||
info = f[1](*args)
|
info = f[1](*args)
|
||||||
else:
|
else:
|
||||||
info = state[f[2][0]]
|
info = state[f[2][0]]
|
||||||
|
|
||||||
nl = len(f[0])+4
|
nl = len(f[0]) + 4
|
||||||
if (nl+len(info))>self.popup.width:
|
if (nl + len(info)) > self.popup.width:
|
||||||
self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0], info[:(self.popup.width - nl)]))
|
self.popup.add_line("{!info!}%s: {!input!}%s" % (f[0], info[:(self.popup.width - nl)]))
|
||||||
info = info[(self.popup.width - nl):]
|
info = info[(self.popup.width - nl):]
|
||||||
n = self.popup.width-3
|
n = self.popup.width - 3
|
||||||
chunks = [info[i:i+n] for i in xrange(0, len(info), n)]
|
chunks = [info[i:i + n] for i in xrange(0, len(info), n)]
|
||||||
for c in chunks:
|
for c in chunks:
|
||||||
self.popup.add_line(" %s"%c)
|
self.popup.add_line(" %s" % c)
|
||||||
else:
|
else:
|
||||||
self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0], info))
|
self.popup.add_line("{!info!}%s: {!input!}%s" % (f[0], info))
|
||||||
self.refresh()
|
self.refresh()
|
||||||
else:
|
else:
|
||||||
self.__torrent_info_id = None
|
self.__torrent_info_id = None
|
||||||
|
|
||||||
|
|
||||||
def on_resize(self, *args):
|
def on_resize(self, *args):
|
||||||
BaseMode.on_resize_norefresh(self, *args)
|
BaseMode.on_resize_norefresh(self, *args)
|
||||||
if self.popup:
|
if self.popup:
|
||||||
|
@ -564,7 +533,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
if not state:
|
if not state:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
s_primary = self.config["sort_primary"]
|
s_primary = self.config["sort_primary"]
|
||||||
s_secondary = self.config["sort_secondary"]
|
s_secondary = self.config["sort_secondary"]
|
||||||
|
|
||||||
result = state
|
result = state
|
||||||
|
@ -575,6 +544,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
cmp_func = self._queue_sort
|
cmp_func = self._queue_sort
|
||||||
|
|
||||||
sg = state.get
|
sg = state.get
|
||||||
|
|
||||||
def sort_by_field(state, result, field):
|
def sort_by_field(state, result, field):
|
||||||
if field in column_names_to_state_keys:
|
if field in column_names_to_state_keys:
|
||||||
field = column_names_to_state_keys[field]
|
field = column_names_to_state_keys[field]
|
||||||
|
@ -585,10 +555,10 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
# and if it's a string
|
# and if it's a string
|
||||||
first_element = state[state.keys()[0]]
|
first_element = state[state.keys()[0]]
|
||||||
if field in first_element:
|
if field in first_element:
|
||||||
is_string = isinstance( first_element[field], basestring)
|
is_string = isinstance(first_element[field], basestring)
|
||||||
|
|
||||||
sort_key = lambda s:sg(s)[field]
|
sort_key = lambda s: sg(s)[field]
|
||||||
sort_key2 = lambda s:sg(s)[field].lower()
|
sort_key2 = lambda s: sg(s)[field].lower()
|
||||||
|
|
||||||
#If it's a string, sort case-insensitively but preserve A>a order
|
#If it's a string, sort case-insensitively but preserve A>a order
|
||||||
if is_string:
|
if is_string:
|
||||||
|
@ -617,7 +587,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
def _format_queue(self, qnum):
|
def _format_queue(self, qnum):
|
||||||
if (qnum >= 0):
|
if (qnum >= 0):
|
||||||
return "%d"%(qnum+1)
|
return "%d" % (qnum + 1)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -650,14 +620,14 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
def doprefs(arg):
|
def doprefs(arg):
|
||||||
if arg and True in arg[0]:
|
if arg and True in arg[0]:
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
component.get("ConsoleUI").set_mode(Preferences(self, config, self.config, port, status, self.stdscr, self.encoding))
|
component.get("ConsoleUI").set_mode(Preferences(self, config, self.config, port,
|
||||||
|
status, self.stdscr, self.encoding))
|
||||||
else:
|
else:
|
||||||
self.messages.append(("Error", "An error occurred trying to display preferences"))
|
self.messages.append(("Error", "An error occurred trying to display preferences"))
|
||||||
component.stop(["AllTorrents"]).addCallback(doprefs)
|
component.stop(["AllTorrents"]).addCallback(doprefs)
|
||||||
|
|
||||||
client.core.get_config().addCallback(_on_get_config)
|
client.core.get_config().addCallback(_on_get_config)
|
||||||
|
|
||||||
|
|
||||||
def __show_events(self):
|
def __show_events(self):
|
||||||
def doevents(arg):
|
def doevents(arg):
|
||||||
if arg and True in arg[0]:
|
if arg and True in arg[0]:
|
||||||
|
@ -679,35 +649,35 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
component.stop(["AllTorrents"]).addCallback(dolegacy)
|
component.stop(["AllTorrents"]).addCallback(dolegacy)
|
||||||
|
|
||||||
def _torrent_filter(self, idx, data):
|
def _torrent_filter(self, idx, data):
|
||||||
if data==FILTER.ALL:
|
if data == FILTER.ALL:
|
||||||
self.__status_dict = {}
|
self.__status_dict = {}
|
||||||
self._curr_filter = None
|
self._curr_filter = None
|
||||||
elif data==FILTER.ACTIVE:
|
elif data == FILTER.ACTIVE:
|
||||||
self.__status_dict = {"state":"Active"}
|
self.__status_dict = {"state": "Active"}
|
||||||
self._curr_filter = "Active"
|
self._curr_filter = "Active"
|
||||||
elif data==FILTER.DOWNLOADING:
|
elif data == FILTER.DOWNLOADING:
|
||||||
self.__status_dict = {"state":"Downloading"}
|
self.__status_dict = {"state": "Downloading"}
|
||||||
self._curr_filter = "Downloading"
|
self._curr_filter = "Downloading"
|
||||||
elif data==FILTER.SEEDING:
|
elif data == FILTER.SEEDING:
|
||||||
self.__status_dict = {"state":"Seeding"}
|
self.__status_dict = {"state": "Seeding"}
|
||||||
self._curr_filter = "Seeding"
|
self._curr_filter = "Seeding"
|
||||||
elif data==FILTER.PAUSED:
|
elif data == FILTER.PAUSED:
|
||||||
self.__status_dict = {"state":"Paused"}
|
self.__status_dict = {"state": "Paused"}
|
||||||
self._curr_filter = "Paused"
|
self._curr_filter = "Paused"
|
||||||
elif data==FILTER.CHECKING:
|
elif data == FILTER.CHECKING:
|
||||||
self.__status_dict = {"state":"Checking"}
|
self.__status_dict = {"state": "Checking"}
|
||||||
self._curr_filter = "Checking"
|
self._curr_filter = "Checking"
|
||||||
elif data==FILTER.ERROR:
|
elif data == FILTER.ERROR:
|
||||||
self.__status_dict = {"state":"Error"}
|
self.__status_dict = {"state": "Error"}
|
||||||
self._curr_filter = "Error"
|
self._curr_filter = "Error"
|
||||||
elif data==FILTER.QUEUED:
|
elif data == FILTER.QUEUED:
|
||||||
self.__status_dict = {"state":"Queued"}
|
self.__status_dict = {"state": "Queued"}
|
||||||
self._curr_filter = "Queued"
|
self._curr_filter = "Queued"
|
||||||
elif data==FILTER.ALLOCATING:
|
elif data == FILTER.ALLOCATING:
|
||||||
self.__status_dict = {"state":"Allocating"}
|
self.__status_dict = {"state": "Allocating"}
|
||||||
self._curr_filter = "Allocating"
|
self._curr_filter = "Allocating"
|
||||||
elif data==FILTER.MOVING:
|
elif data == FILTER.MOVING:
|
||||||
self.__status_dict = {"state":"Moving"}
|
self.__status_dict = {"state": "Moving"}
|
||||||
self._curr_filter = "Moving"
|
self._curr_filter = "Moving"
|
||||||
|
|
||||||
self._go_top = True
|
self._go_top = True
|
||||||
|
@ -728,11 +698,11 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
def _report_add_status(self, succ_cnt, fail_cnt, fail_msgs):
|
def _report_add_status(self, succ_cnt, fail_cnt, fail_msgs):
|
||||||
if fail_cnt == 0:
|
if fail_cnt == 0:
|
||||||
self.report_message("Torrents Added", "{!success!}Successfully added %d torrent(s)"%succ_cnt)
|
self.report_message("Torrents Added", "{!success!}Successfully added %d torrent(s)" % succ_cnt)
|
||||||
else:
|
else:
|
||||||
msg = ("{!error!}Failed to add the following %d torrent(s):\n {!input!}"%fail_cnt)+"\n ".join(fail_msgs)
|
msg = ("{!error!}Failed to add the following %d torrent(s):\n {!input!}" % fail_cnt) + "\n ".join(fail_msgs)
|
||||||
if succ_cnt != 0:
|
if succ_cnt != 0:
|
||||||
msg += "\n \n{!success!}Successfully added %d torrent(s)"%succ_cnt
|
msg += "\n \n{!success!}Successfully added %d torrent(s)" % succ_cnt
|
||||||
self.report_message("Torrent Add Report", msg)
|
self.report_message("Torrent Add Report", msg)
|
||||||
|
|
||||||
def _show_torrent_add_popup(self):
|
def _show_torrent_add_popup(self):
|
||||||
|
@ -741,11 +711,11 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
def fail_cb(msg, url):
|
def fail_cb(msg, url):
|
||||||
log.debug("failed to add torrent: %s: %s" % (url, msg))
|
log.debug("failed to add torrent: %s: %s" % (url, msg))
|
||||||
error_msg = "{!input!} * %s: {!error!}%s" % (url, msg)
|
error_msg = "{!input!} * %s: {!error!}%s" % (url, msg)
|
||||||
self._report_add_status(0, 1, [error_msg] )
|
self._report_add_status(0, 1, [error_msg])
|
||||||
|
|
||||||
def success_cb(tid, url):
|
def success_cb(tid, url):
|
||||||
if tid:
|
if tid:
|
||||||
log.debug("added torrent: %s (%s)"%(url, tid))
|
log.debug("added torrent: %s (%s)" % (url, tid))
|
||||||
self._report_add_status(1, 0, [])
|
self._report_add_status(1, 0, [])
|
||||||
else:
|
else:
|
||||||
fail_cb("Already in session (probably)", url)
|
fail_cb("Already in session (probably)", url)
|
||||||
|
@ -794,7 +764,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
if data == 1:
|
if data == 1:
|
||||||
self.show_addtorrents_screen()
|
self.show_addtorrents_screen()
|
||||||
elif data == 2:
|
elif data == 2:
|
||||||
show_add_url_popup()
|
show_add_url_popup()
|
||||||
|
@ -804,7 +774,6 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.popup.add_line("From _URL or Magnet", data=2)
|
self.popup.add_line("From _URL or Magnet", data=2)
|
||||||
self.popup.add_line("_Cancel", data=0)
|
self.popup.add_line("_Cancel", data=0)
|
||||||
|
|
||||||
|
|
||||||
def _do_set_column_visibility(self, data):
|
def _do_set_column_visibility(self, data):
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
self.config[key] = value
|
self.config[key] = value
|
||||||
|
@ -815,13 +784,9 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
def _show_visible_columns_popup(self):
|
def _show_visible_columns_popup(self):
|
||||||
title = "Visible columns (Enter to exit)"
|
title = "Visible columns (Enter to exit)"
|
||||||
self.popup = InputPopup(self,
|
self.popup = InputPopup(self, title, close_cb=self._do_set_column_visibility,
|
||||||
title,
|
immediate_action=True, height_req=len(column_pref_names) + 1,
|
||||||
close_cb=self._do_set_column_visibility,
|
width_req=max([len(col) for col in column_pref_names + [title]]) + 8)
|
||||||
immediate_action=True,
|
|
||||||
height_req= len(column_pref_names) + 1,
|
|
||||||
width_req= max([len(col) for col in column_pref_names + [title]]) + 8
|
|
||||||
)
|
|
||||||
|
|
||||||
for col in column_pref_names:
|
for col in column_pref_names:
|
||||||
name = prefs_to_names[col]
|
name = prefs_to_names[col]
|
||||||
|
@ -843,7 +808,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.popup = pu
|
self.popup = pu
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self,lines=None):
|
def refresh(self, lines=None):
|
||||||
#log.error("ref")
|
#log.error("ref")
|
||||||
#import traceback
|
#import traceback
|
||||||
#traceback.print_stack()
|
#traceback.print_stack()
|
||||||
|
@ -854,7 +819,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self._go_top = False
|
self._go_top = False
|
||||||
|
|
||||||
# show a message popup if there's anything queued
|
# show a message popup if there's anything queued
|
||||||
if self.popup == None and self.messages:
|
if self.popup is None and self.messages:
|
||||||
title, msg = self.messages.popleft()
|
title, msg = self.messages.popleft()
|
||||||
self.popup = MessagePopup(self, title, msg, width_req=1.0)
|
self.popup = MessagePopup(self, title, msg, width_req=1.0)
|
||||||
|
|
||||||
|
@ -864,10 +829,10 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
|
|
||||||
# Update the status bars
|
# Update the status bars
|
||||||
if self._curr_filter == None:
|
if self._curr_filter is None:
|
||||||
self.add_string(0, self.statusbars.topbar)
|
self.add_string(0, self.statusbars.topbar)
|
||||||
else:
|
else:
|
||||||
self.add_string(0, "%s {!filterstatus!}Current filter: %s"%(self.statusbars.topbar, self._curr_filter))
|
self.add_string(0, "%s {!filterstatus!}Current filter: %s" % (self.statusbars.topbar, self._curr_filter))
|
||||||
self.add_string(1, self.column_string)
|
self.add_string(1, self.column_string)
|
||||||
|
|
||||||
if self.entering_search:
|
if self.entering_search:
|
||||||
|
@ -875,7 +840,8 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
SEARCH_EMPTY: "{!black,white!}Search torrents: %s{!black,white!}",
|
SEARCH_EMPTY: "{!black,white!}Search torrents: %s{!black,white!}",
|
||||||
SEARCH_SUCCESS: "{!black,white!}Search torrents: {!black,green!}%s{!black,white!}",
|
SEARCH_SUCCESS: "{!black,white!}Search torrents: {!black,green!}%s{!black,white!}",
|
||||||
SEARCH_FAILING: "{!black,white!}Search torrents: {!black,red!}%s{!black,white!}",
|
SEARCH_FAILING: "{!black,white!}Search torrents: {!black,red!}%s{!black,white!}",
|
||||||
SEARCH_START_REACHED: "{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (start reached)",
|
SEARCH_START_REACHED:
|
||||||
|
"{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (start reached)",
|
||||||
SEARCH_END_REACHED: "{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (end reached)"
|
SEARCH_END_REACHED: "{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (end reached)"
|
||||||
}[self.search_state] % self.search_string
|
}[self.search_state] % self.search_string
|
||||||
|
|
||||||
|
@ -887,7 +853,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
string = self.statusbars.bottombar
|
string = self.statusbars.bottombar
|
||||||
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
||||||
|
|
||||||
string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
||||||
|
|
||||||
self.add_string(self.rows - 1, string)
|
self.add_string(self.rows - 1, string)
|
||||||
except:
|
except:
|
||||||
|
@ -896,7 +862,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
# add all the torrents
|
# add all the torrents
|
||||||
if self.numtorrents == 0:
|
if self.numtorrents == 0:
|
||||||
msg = "No torrents match filter".center(self.cols)
|
msg = "No torrents match filter".center(self.cols)
|
||||||
self.add_string(3, "{!info!}%s"%msg)
|
self.add_string(3, "{!info!}%s" % msg)
|
||||||
elif self.numtorrents > 0:
|
elif self.numtorrents > 0:
|
||||||
tidx = self.curoff
|
tidx = self.curoff
|
||||||
currow = 2
|
currow = 2
|
||||||
|
@ -904,11 +870,12 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
#Because dots are slow
|
#Because dots are slow
|
||||||
sorted_ids = self._sorted_ids
|
sorted_ids = self._sorted_ids
|
||||||
curstate = self.curstate
|
curstate = self.curstate
|
||||||
gcv = column.get_column_value
|
gcv = console_column.get_column_value
|
||||||
fr = format_utils.format_row
|
fr = format_utils.format_row
|
||||||
cols = self.__columns
|
cols = self.__columns
|
||||||
colw = self.column_widths
|
colw = self.column_widths
|
||||||
cr = self._cached_rows
|
cr = self._cached_rows
|
||||||
|
|
||||||
def draw_row(index):
|
def draw_row(index):
|
||||||
if index not in cr:
|
if index not in cr:
|
||||||
ts = curstate[sorted_ids[index]]
|
ts = curstate[sorted_ids[index]]
|
||||||
|
@ -918,15 +885,19 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
if lines:
|
if lines:
|
||||||
todraw = []
|
todraw = []
|
||||||
for l in lines:
|
for l in lines:
|
||||||
if l < tidx - 1: continue
|
if l < tidx - 1:
|
||||||
if l >= tidx - 1 + self.rows - 3: break
|
continue
|
||||||
if l >= self.numtorrents: break
|
if l >= tidx - 1 + self.rows - 3:
|
||||||
|
break
|
||||||
|
if l >= self.numtorrents:
|
||||||
|
break
|
||||||
todraw.append(draw_row(l))
|
todraw.append(draw_row(l))
|
||||||
lines.reverse()
|
lines.reverse()
|
||||||
else:
|
else:
|
||||||
todraw = []
|
todraw = []
|
||||||
for i in range(tidx-1, tidx-1 + self.rows - 3):
|
for i in range(tidx - 1, tidx - 1 + self.rows - 3):
|
||||||
if i >= self.numtorrents: break
|
if i >= self.numtorrents:
|
||||||
|
break
|
||||||
todraw += [draw_row(i)]
|
todraw += [draw_row(i)]
|
||||||
|
|
||||||
for row in todraw:
|
for row in todraw:
|
||||||
|
@ -935,8 +906,8 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
bg = "black"
|
bg = "black"
|
||||||
attr = None
|
attr = None
|
||||||
if lines:
|
if lines:
|
||||||
tidx = lines.pop()+1
|
tidx = lines.pop() + 1
|
||||||
currow = tidx-self.curoff+2
|
currow = tidx - self.curoff + 2
|
||||||
|
|
||||||
if tidx in self.marked:
|
if tidx in self.marked:
|
||||||
bg = "blue"
|
bg = "blue"
|
||||||
|
@ -964,7 +935,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
fg = "green"
|
fg = "green"
|
||||||
|
|
||||||
if self.entering_search and len(self.search_string) > 1:
|
if self.entering_search and len(self.search_string) > 1:
|
||||||
lcase_name = self.torrent_names[tidx-1].lower()
|
lcase_name = self.torrent_names[tidx - 1].lower()
|
||||||
sstring_lower = self.search_string.lower()
|
sstring_lower = self.search_string.lower()
|
||||||
if lcase_name.find(sstring_lower) != -1:
|
if lcase_name.find(sstring_lower) != -1:
|
||||||
if tidx == self.cursel:
|
if tidx == self.cursel:
|
||||||
|
@ -978,12 +949,12 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
attr = "bold"
|
attr = "bold"
|
||||||
|
|
||||||
if attr:
|
if attr:
|
||||||
colorstr = "{!%s,%s,%s!}"%(fg, bg, attr)
|
colorstr = "{!%s,%s,%s!}" % (fg, bg, attr)
|
||||||
else:
|
else:
|
||||||
colorstr = "{!%s,%s!}"%(fg, bg)
|
colorstr = "{!%s,%s!}" % (fg, bg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.add_string(currow, "%s%s"%(colorstr, row[0]), trim=False)
|
self.add_string(currow, "%s%s" % (colorstr, row[0]), trim=False)
|
||||||
except:
|
except:
|
||||||
#Yeah, this should be fixed in some better way
|
#Yeah, this should be fixed in some better way
|
||||||
pass
|
pass
|
||||||
|
@ -997,7 +968,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
#self.stdscr.redrawwin()
|
#self.stdscr.redrawwin()
|
||||||
if self.entering_search:
|
if self.entering_search:
|
||||||
curses.curs_set(2)
|
curses.curs_set(2)
|
||||||
self.stdscr.move(self.rows-1, len(self.search_string)+17)
|
self.stdscr.move(self.rows - 1, len(self.search_string) + 17)
|
||||||
else:
|
else:
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
|
|
||||||
|
@ -1011,7 +982,6 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
|
|
||||||
curses.doupdate()
|
curses.doupdate()
|
||||||
|
|
||||||
|
|
||||||
def _mark_unmark(self, idx):
|
def _mark_unmark(self, idx):
|
||||||
if idx in self.marked:
|
if idx in self.marked:
|
||||||
self.marked.remove(idx)
|
self.marked.remove(idx)
|
||||||
|
@ -1041,7 +1011,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
:returns: Nothing
|
:returns: Nothing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if direction == "first":
|
if direction == "first":
|
||||||
search_space = enumerate(self.torrent_names)
|
search_space = enumerate(self.torrent_names)
|
||||||
elif direction == "last":
|
elif direction == "last":
|
||||||
search_space = enumerate(self.torrent_names)
|
search_space = enumerate(self.torrent_names)
|
||||||
|
@ -1053,7 +1023,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
search_space = search_space[self.cursel:]
|
search_space = search_space[self.cursel:]
|
||||||
elif direction == "previous":
|
elif direction == "previous":
|
||||||
search_space = enumerate(self.torrent_names)
|
search_space = enumerate(self.torrent_names)
|
||||||
search_space = list(search_space)[:self.cursel-1]
|
search_space = list(search_space)[:self.cursel - 1]
|
||||||
search_space = reversed(search_space)
|
search_space = reversed(search_space)
|
||||||
|
|
||||||
search_string = self.search_string.lower()
|
search_string = self.search_string.lower()
|
||||||
|
@ -1063,10 +1033,10 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
if skip > 0:
|
if skip > 0:
|
||||||
skip -= 1
|
skip -= 1
|
||||||
continue
|
continue
|
||||||
self.cursel = (i+1)
|
self.cursel = (i + 1)
|
||||||
if ((self.curoff + self.rows - 5) < self.cursel):
|
if ((self.curoff + self.rows - 5) < self.cursel):
|
||||||
self.curoff = self.cursel - self.rows + 5
|
self.curoff = self.cursel - self.rows + 5
|
||||||
elif ((self.curoff +1) > self.cursel):
|
elif ((self.curoff + 1) > self.cursel):
|
||||||
self.curoff = max(1, self.cursel - 1)
|
self.curoff = max(1, self.cursel - 1)
|
||||||
self.search_state = SEARCH_SUCCESS
|
self.search_state = SEARCH_SUCCESS
|
||||||
return
|
return
|
||||||
|
@ -1078,7 +1048,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.search_state = SEARCH_START_REACHED
|
self.search_state = SEARCH_START_REACHED
|
||||||
|
|
||||||
def __update_search(self, c):
|
def __update_search(self, c):
|
||||||
cname = self.torrent_names[self.cursel-1]
|
cname = self.torrent_names[self.cursel - 1]
|
||||||
if c == curses.KEY_BACKSPACE or c == 127:
|
if c == curses.KEY_BACKSPACE or c == 127:
|
||||||
if self.search_string:
|
if self.search_string:
|
||||||
self.search_string = self.search_string[:-1]
|
self.search_string = self.search_string[:-1]
|
||||||
|
@ -1108,7 +1078,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.search_state = SEARCH_EMPTY
|
self.search_state = SEARCH_EMPTY
|
||||||
self.refresh([])
|
self.refresh([])
|
||||||
|
|
||||||
elif c == ord('/'):
|
elif c == ord("/"):
|
||||||
self.entering_search = False
|
self.entering_search = False
|
||||||
self.search_state = SEARCH_EMPTY
|
self.search_state = SEARCH_EMPTY
|
||||||
self.refresh([])
|
self.refresh([])
|
||||||
|
@ -1180,7 +1150,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
return
|
return
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'Q':
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -1198,21 +1168,23 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
return
|
return
|
||||||
|
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
if self.cursel == 1: return
|
if self.cursel == 1:
|
||||||
|
return
|
||||||
if not self._scroll_up(1):
|
if not self._scroll_up(1):
|
||||||
effected_lines = [self.cursel-1, self.cursel]
|
effected_lines = [self.cursel - 1, self.cursel]
|
||||||
elif c == curses.KEY_PPAGE:
|
elif c == curses.KEY_PPAGE:
|
||||||
self._scroll_up(int(self.rows/2))
|
self._scroll_up(int(self.rows / 2))
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
if self.cursel >= self.numtorrents: return
|
if self.cursel >= self.numtorrents:
|
||||||
|
return
|
||||||
if not self._scroll_down(1):
|
if not self._scroll_down(1):
|
||||||
effected_lines = [self.cursel-2, self.cursel-1]
|
effected_lines = [self.cursel - 2, self.cursel - 1]
|
||||||
elif c == curses.KEY_NPAGE:
|
elif c == curses.KEY_NPAGE:
|
||||||
self._scroll_down(int(self.rows/2))
|
self._scroll_down(int(self.rows / 2))
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
self._scroll_up(self.cursel)
|
self._scroll_up(self.cursel)
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
self._scroll_down(self.numtorrents-self.cursel)
|
self._scroll_down(self.numtorrents - self.cursel)
|
||||||
elif c == curses.KEY_DC:
|
elif c == curses.KEY_DC:
|
||||||
if self.cursel not in self.marked:
|
if self.cursel not in self.marked:
|
||||||
self.marked.append(self.cursel)
|
self.marked.append(self.cursel)
|
||||||
|
@ -1235,46 +1207,47 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == '/':
|
if chr(c) == "/":
|
||||||
self.search_string = ""
|
self.search_string = ""
|
||||||
self.entering_search = True
|
self.entering_search = True
|
||||||
elif chr(c) == 'n' and self.search_string:
|
elif chr(c) == "n" and self.search_string:
|
||||||
self.__do_search("next")
|
self.__do_search("next")
|
||||||
elif chr(c) == 'j':
|
elif chr(c) == "j":
|
||||||
if not self._scroll_up(1):
|
if not self._scroll_up(1):
|
||||||
effected_lines = [self.cursel-1, self.cursel]
|
effected_lines = [self.cursel - 1, self.cursel]
|
||||||
elif chr(c) == 'k':
|
elif chr(c) == "k":
|
||||||
if not self._scroll_down(1):
|
if not self._scroll_down(1):
|
||||||
effected_lines = [self.cursel-2, self.cursel-1]
|
effected_lines = [self.cursel - 2, self.cursel - 1]
|
||||||
elif chr(c) == 'i':
|
elif chr(c) == "i":
|
||||||
cid = self.current_torrent_id()
|
cid = self.current_torrent_id()
|
||||||
if cid:
|
if cid:
|
||||||
def cb(): self.__torrent_info_id = None
|
def cb():
|
||||||
|
self.__torrent_info_id = None
|
||||||
self.popup = Popup(self, "Info", close_cb=cb, height_req=20)
|
self.popup = Popup(self, "Info", close_cb=cb, height_req=20)
|
||||||
self.popup.add_line("Getting torrent info...")
|
self.popup.add_line("Getting torrent info...")
|
||||||
self.__torrent_info_id = cid
|
self.__torrent_info_id = cid
|
||||||
elif chr(c) == 'm':
|
elif chr(c) == "m":
|
||||||
self._mark_unmark(self.cursel)
|
self._mark_unmark(self.cursel)
|
||||||
effected_lines = [self.cursel-1]
|
effected_lines = [self.cursel - 1]
|
||||||
elif chr(c) == 'M':
|
elif chr(c) == "M":
|
||||||
if self.last_mark >= 0:
|
if self.last_mark >= 0:
|
||||||
if (self.cursel+1) > self.last_mark:
|
if (self.cursel + 1) > self.last_mark:
|
||||||
mrange = range(self.last_mark, self.cursel+1)
|
mrange = range(self.last_mark, self.cursel + 1)
|
||||||
else:
|
else:
|
||||||
mrange = range(self.cursel-1, self.last_mark)
|
mrange = range(self.cursel - 1, self.last_mark)
|
||||||
self.marked.extend(mrange[1:])
|
self.marked.extend(mrange[1:])
|
||||||
effected_lines = mrange
|
effected_lines = mrange
|
||||||
else:
|
else:
|
||||||
self._mark_unmark(self.cursel)
|
self._mark_unmark(self.cursel)
|
||||||
effected_lines = [self.cursel-1]
|
effected_lines = [self.cursel - 1]
|
||||||
elif chr(c) == 'c':
|
elif chr(c) == "c":
|
||||||
self.marked = []
|
self.marked = []
|
||||||
self.last_mark = -1
|
self.last_mark = -1
|
||||||
elif chr(c) == 'a':
|
elif chr(c) == "a":
|
||||||
self._show_torrent_add_popup()
|
self._show_torrent_add_popup()
|
||||||
elif chr(c) == 'v':
|
elif chr(c) == "v":
|
||||||
self._show_visible_columns_popup()
|
self._show_visible_columns_popup()
|
||||||
elif chr(c) == 'o':
|
elif chr(c) == "o":
|
||||||
if not self.marked:
|
if not self.marked:
|
||||||
self.marked = [self.cursel]
|
self.marked = [self.cursel]
|
||||||
self.last_mark = self.cursel
|
self.last_mark = self.cursel
|
||||||
|
@ -1282,7 +1255,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.last_mark = -1
|
self.last_mark = -1
|
||||||
torrent_actions_popup(self, self._selected_torrent_ids(), action=ACTION.TORRENT_OPTIONS)
|
torrent_actions_popup(self, self._selected_torrent_ids(), action=ACTION.TORRENT_OPTIONS)
|
||||||
|
|
||||||
elif chr(c) == '<':
|
elif chr(c) == "<":
|
||||||
i = len(self.__cols_to_show)
|
i = len(self.__cols_to_show)
|
||||||
try:
|
try:
|
||||||
i = self.__cols_to_show.index(self.config["sort_primary"]) - 1
|
i = self.__cols_to_show.index(self.config["sort_primary"]) - 1
|
||||||
|
@ -1298,7 +1271,7 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.__update_columns()
|
self.__update_columns()
|
||||||
self.refresh([])
|
self.refresh([])
|
||||||
|
|
||||||
elif chr(c) == '>':
|
elif chr(c) == ">":
|
||||||
i = 0
|
i = 0
|
||||||
try:
|
try:
|
||||||
i = self.__cols_to_show.index(self.config["sort_primary"]) + 1
|
i = self.__cols_to_show.index(self.config["sort_primary"]) + 1
|
||||||
|
@ -1314,17 +1287,17 @@ class AllTorrents(BaseMode, component.Component):
|
||||||
self.__update_columns()
|
self.__update_columns()
|
||||||
self.refresh([])
|
self.refresh([])
|
||||||
|
|
||||||
elif chr(c) == 'f':
|
elif chr(c) == "f":
|
||||||
self._show_torrent_filter_popup()
|
self._show_torrent_filter_popup()
|
||||||
elif chr(c) == 'h':
|
elif chr(c) == "h":
|
||||||
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
||||||
elif chr(c) == 'p':
|
elif chr(c) == "p":
|
||||||
self.show_preferences()
|
self.show_preferences()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'e':
|
elif chr(c) == "e":
|
||||||
self.__show_events()
|
self.__show_events()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'l':
|
elif chr(c) == "l":
|
||||||
self.__legacy_mode()
|
self.__legacy_mode()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,11 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# basemode.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Most code in this file taken from screen.py:
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
#
|
# See LICENSE for more details.
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -60,6 +32,7 @@ except:
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CursesStdIO(object):
|
class CursesStdIO(object):
|
||||||
"""fake fd to be registered as a reader with the twisted reactor.
|
"""fake fd to be registered as a reader with the twisted reactor.
|
||||||
Curses classes needing input should extend this"""
|
Curses classes needing input should extend this"""
|
||||||
|
@ -71,7 +44,9 @@ class CursesStdIO(object):
|
||||||
def doRead(self):
|
def doRead(self):
|
||||||
"""called when input is ready"""
|
"""called when input is ready"""
|
||||||
pass
|
pass
|
||||||
def logPrefix(self): return 'CursesClient'
|
|
||||||
|
def logPrefix(self):
|
||||||
|
return "CursesClient"
|
||||||
|
|
||||||
|
|
||||||
class BaseMode(CursesStdIO):
|
class BaseMode(CursesStdIO):
|
||||||
|
@ -125,7 +100,7 @@ class BaseMode(CursesStdIO):
|
||||||
def on_resize_norefresh(self, *args):
|
def on_resize_norefresh(self, *args):
|
||||||
log.debug("on_resize_from_signal")
|
log.debug("on_resize_from_signal")
|
||||||
# Get the new rows and cols value
|
# Get the new rows and cols value
|
||||||
self.rows, self.cols = struct.unpack("hhhh", ioctl(0, termios.TIOCGWINSZ, "\000"*8))[0:2]
|
self.rows, self.cols = struct.unpack("hhhh", ioctl(0, termios.TIOCGWINSZ, "\000" * 8))[0:2]
|
||||||
curses.resizeterm(self.rows, self.cols)
|
curses.resizeterm(self.rows, self.cols)
|
||||||
|
|
||||||
def on_resize(self, *args):
|
def on_resize(self, *args):
|
||||||
|
@ -135,7 +110,7 @@ class BaseMode(CursesStdIO):
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def add_string(self, row, string, scr=None, col = 0, pad=True, trim=True):
|
def add_string(self, row, string, scr=None, col=0, pad=True, trim=True):
|
||||||
"""
|
"""
|
||||||
Adds a string to the desired `:param:row`.
|
Adds a string to the desired `:param:row`.
|
||||||
|
|
||||||
|
@ -181,8 +156,8 @@ class BaseMode(CursesStdIO):
|
||||||
s += " " * (self.cols - (col + len(s)) - 1)
|
s += " " * (self.cols - (col + len(s)) - 1)
|
||||||
if trim:
|
if trim:
|
||||||
y, x = screen.getmaxyx()
|
y, x = screen.getmaxyx()
|
||||||
if (col+len(s)) > x:
|
if (col + len(s)) > x:
|
||||||
s = "%s..."%s[0:x-4-col]
|
s = "%s..." % s[0:x - 4 - col]
|
||||||
screen.addstr(row, col, s, color)
|
screen.addstr(row, col, s, color)
|
||||||
col += len(s)
|
col += len(s)
|
||||||
|
|
||||||
|
@ -230,7 +205,7 @@ class BaseMode(CursesStdIO):
|
||||||
|
|
||||||
def _doRead(self):
|
def _doRead(self):
|
||||||
# Read the character
|
# Read the character
|
||||||
c = self.stdscr.getch()
|
self.stdscr.getch()
|
||||||
self.stdscr.refresh()
|
self.stdscr.refresh()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# column.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -44,7 +17,7 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
def format_queue(qnum):
|
def format_queue(qnum):
|
||||||
if (qnum >= 0):
|
if (qnum >= 0):
|
||||||
return "%d" % (qnum+1)
|
return "%d" % (qnum + 1)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -76,7 +49,7 @@ columns = {
|
||||||
"Seeds:Peers": (("seeds_peers_ratio",), format_utils.format_float),
|
"Seeds:Peers": (("seeds_peers_ratio",), format_utils.format_float),
|
||||||
"Down Limit": (("max_download_speed",), format_utils.format_speed),
|
"Down Limit": (("max_download_speed",), format_utils.format_speed),
|
||||||
"Up Limit": (("max_upload_speed",), format_utils.format_speed),
|
"Up Limit": (("max_upload_speed",), format_utils.format_speed),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_column_value(name, state):
|
def get_column_value(name, state):
|
||||||
|
|
|
@ -1,39 +1,13 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# connectionmanager.py
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
|
||||||
# Copyright (C) 2007-2009 Nick Lanham <nick@afternight.org>
|
|
||||||
#
|
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
|
|
||||||
# a mode that show's a popup to select which host to connect to
|
""" A mode that show's a popup to select which host to connect to """
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
@ -46,7 +20,6 @@ from alltorrents import AllTorrents
|
||||||
from basemode import BaseMode
|
from basemode import BaseMode
|
||||||
from deluge.configmanager import ConfigManager
|
from deluge.configmanager import ConfigManager
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.coreconfig import CoreConfig
|
|
||||||
from input_popup import InputPopup
|
from input_popup import InputPopup
|
||||||
from popup import MessagePopup, SelectablePopup
|
from popup import MessagePopup, SelectablePopup
|
||||||
|
|
||||||
|
@ -77,12 +50,14 @@ class ConnectionManager(BaseMode):
|
||||||
|
|
||||||
def __update_popup(self):
|
def __update_popup(self):
|
||||||
self.popup = SelectablePopup(self, "Select Host", self.__host_selected)
|
self.popup = SelectablePopup(self, "Select Host", self.__host_selected)
|
||||||
self.popup.add_line("{!white,black,bold!}'Q'=quit, 'r'=refresh, 'a'=add new host, 'D'=delete host", selectable=False)
|
self.popup.add_line("{!white,black,bold!}'Q'=quit, 'r'=refresh, 'a'=add new host, 'D'=delete host",
|
||||||
|
selectable=False)
|
||||||
for host in self.config["hosts"]:
|
for host in self.config["hosts"]:
|
||||||
if host[0] in self.statuses:
|
if host[0] in self.statuses:
|
||||||
self.popup.add_line("%s:%d [Online] (%s)"%(host[1], host[2], self.statuses[host[0]]), data=host[0], foreground="green")
|
self.popup.add_line("%s:%d [Online] (%s)" % (host[1], host[2], self.statuses[host[0]]),
|
||||||
|
data=host[0], foreground="green")
|
||||||
else:
|
else:
|
||||||
self.popup.add_line("%s:%d [Offline]"%(host[1], host[2]), data=host[0], foreground="red")
|
self.popup.add_line("%s:%d [Offline]" % (host[1], host[2]), data=host[0], foreground="red")
|
||||||
self.inlist = True
|
self.inlist = True
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
@ -174,7 +149,7 @@ class ConnectionManager(BaseMode):
|
||||||
self.draw_statusbars()
|
self.draw_statusbars()
|
||||||
self.stdscr.noutrefresh()
|
self.stdscr.noutrefresh()
|
||||||
|
|
||||||
if self.popup == None and self.messages:
|
if self.popup is None and self.messages:
|
||||||
title, msg = self.messages.popleft()
|
title, msg = self.messages.popleft()
|
||||||
self.popup = MessagePopup(self, title, msg)
|
self.popup = MessagePopup(self, title, msg)
|
||||||
|
|
||||||
|
@ -198,8 +173,9 @@ class ConnectionManager(BaseMode):
|
||||||
c = self.stdscr.getch()
|
c = self.stdscr.getch()
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'q' and self.inlist: return
|
if chr(c) == "q" and self.inlist:
|
||||||
if chr(c) == 'Q':
|
return
|
||||||
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -208,13 +184,13 @@ class ConnectionManager(BaseMode):
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
return
|
return
|
||||||
if chr(c) == 'D' and self.inlist:
|
if chr(c) == "D" and self.inlist:
|
||||||
self.__delete_current_host()
|
self.__delete_current_host()
|
||||||
self.__update_popup()
|
self.__update_popup()
|
||||||
return
|
return
|
||||||
if chr(c) == 'r' and self.inlist:
|
if chr(c) == "r" and self.inlist:
|
||||||
self.__update_statuses()
|
self.__update_statuses()
|
||||||
if chr(c) == 'a' and self.inlist:
|
if chr(c) == "a" and self.inlist:
|
||||||
self.__add_popup()
|
self.__add_popup()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# eventview.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -62,14 +35,14 @@ class EventView(BaseMode):
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
|
|
||||||
self.add_string(0, self.statusbars.topbar)
|
self.add_string(0, self.statusbars.topbar)
|
||||||
hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10))
|
hstr = "%sPress [h] for help" % (" " * (self.cols - len(self.statusbars.bottombar) - 10))
|
||||||
#This will quite likely fail when switching modes
|
#This will quite likely fail when switching modes
|
||||||
try:
|
try:
|
||||||
rf = format_utils.remove_formatting
|
rf = format_utils.remove_formatting
|
||||||
string = self.statusbars.bottombar
|
string = self.statusbars.bottombar
|
||||||
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
||||||
|
|
||||||
string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
||||||
|
|
||||||
self.add_string(self.rows - 1, string)
|
self.add_string(self.rows - 1, string)
|
||||||
except:
|
except:
|
||||||
|
@ -80,15 +53,15 @@ class EventView(BaseMode):
|
||||||
if i - self.offset >= self.rows - 2:
|
if i - self.offset >= self.rows - 2:
|
||||||
more = len(events) - self.offset - self.rows + 2
|
more = len(events) - self.offset - self.rows + 2
|
||||||
if more > 0:
|
if more > 0:
|
||||||
self.add_string(i-self.offset, " (And %i more)" % more)
|
self.add_string(i - self.offset, " (And %i more)" % more)
|
||||||
break
|
break
|
||||||
|
|
||||||
elif i - self.offset < 0:
|
elif i - self.offset < 0:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
self.add_string(i+1-self.offset, event)
|
self.add_string(i + 1 - self.offset, event)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass #This'll just cut the line. Note: This seriously should be fixed in a better way
|
pass # This'll just cut the line. Note: This seriously should be fixed in a better way
|
||||||
else:
|
else:
|
||||||
self.add_string(1, "{!white,black,bold!}No events to show yet")
|
self.add_string(1, "{!white,black,bold!}No events to show yet")
|
||||||
|
|
||||||
|
@ -116,7 +89,7 @@ class EventView(BaseMode):
|
||||||
c = self.stdscr.getch()
|
c = self.stdscr.getch()
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'Q':
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -125,7 +98,7 @@ class EventView(BaseMode):
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'q':
|
elif chr(c) == "q":
|
||||||
self.back_to_overview()
|
self.back_to_overview()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -135,7 +108,7 @@ class EventView(BaseMode):
|
||||||
|
|
||||||
# TODO: Scroll event list
|
# TODO: Scroll event list
|
||||||
jumplen = self.rows - 3
|
jumplen = self.rows - 3
|
||||||
num_events = len( component.get("ConsoleUI").events )
|
num_events = len(component.get("ConsoleUI").events)
|
||||||
|
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
self.offset -= 1
|
self.offset -= 1
|
||||||
|
@ -149,9 +122,9 @@ class EventView(BaseMode):
|
||||||
self.offset += jumplen
|
self.offset += jumplen
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
self.offset += num_events
|
self.offset += num_events
|
||||||
elif c == ord('j'):
|
elif c == ord("j"):
|
||||||
self.offset -= 1
|
self.offset -= 1
|
||||||
elif c == ord('k'):
|
elif c == ord("k"):
|
||||||
self.offset += 1
|
self.offset += 1
|
||||||
|
|
||||||
if self.offset <= 0:
|
if self.offset <= 0:
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# format_utils.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -46,57 +19,69 @@ try:
|
||||||
except:
|
except:
|
||||||
haveud = False
|
haveud = False
|
||||||
|
|
||||||
|
|
||||||
def format_speed(speed):
|
def format_speed(speed):
|
||||||
if (speed > 0):
|
if (speed > 0):
|
||||||
return deluge.common.fspeed(speed)
|
return deluge.common.fspeed(speed)
|
||||||
else:
|
else:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
|
|
||||||
def format_time(time):
|
def format_time(time):
|
||||||
if (time > 0):
|
if (time > 0):
|
||||||
return deluge.common.ftime(time)
|
return deluge.common.ftime(time)
|
||||||
else:
|
else:
|
||||||
return "-"
|
return "-"
|
||||||
|
|
||||||
|
|
||||||
def format_date(time):
|
def format_date(time):
|
||||||
if (time > 0):
|
if (time > 0):
|
||||||
return deluge.common.fdate(time)
|
return deluge.common.fdate(time)
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def format_date_never(time):
|
def format_date_never(time):
|
||||||
if (time > 0):
|
if (time > 0):
|
||||||
return deluge.common.fdate(time)
|
return deluge.common.fdate(time)
|
||||||
else:
|
else:
|
||||||
return "Never"
|
return "Never"
|
||||||
|
|
||||||
|
|
||||||
def format_float(x):
|
def format_float(x):
|
||||||
if x < 0:
|
if x < 0:
|
||||||
return "-"
|
return "-"
|
||||||
else:
|
else:
|
||||||
return "%.3f"%x
|
return "%.3f" % x
|
||||||
|
|
||||||
|
|
||||||
def format_seeds_peers(num, total):
|
def format_seeds_peers(num, total):
|
||||||
return "%d (%d)"%(num, total)
|
return "%d (%d)" % (num, total)
|
||||||
|
|
||||||
|
|
||||||
def format_progress(perc):
|
def format_progress(perc):
|
||||||
if perc < 100:
|
if perc < 100:
|
||||||
return "%.2f%%"%perc
|
return "%.2f%%" % perc
|
||||||
else:
|
else:
|
||||||
return "100%"
|
return "100%"
|
||||||
|
|
||||||
|
|
||||||
def format_pieces(num, size):
|
def format_pieces(num, size):
|
||||||
return "%d (%s)"%(num, deluge.common.fsize(size))
|
return "%d (%s)" % (num, deluge.common.fsize(size))
|
||||||
|
|
||||||
|
|
||||||
def format_priority(prio):
|
def format_priority(prio):
|
||||||
if prio == -2: return "[Mixed]"
|
if prio == - 2:
|
||||||
if prio < 0: return "-"
|
return "[Mixed]"
|
||||||
|
if prio < 0:
|
||||||
|
return "-"
|
||||||
pstring = deluge.common.FILE_PRIORITY[prio]
|
pstring = deluge.common.FILE_PRIORITY[prio]
|
||||||
if prio > 0:
|
if prio > 0:
|
||||||
return pstring[:pstring.index("Priority")-1]
|
return pstring[:pstring.index("Priority") - 1]
|
||||||
else:
|
else:
|
||||||
return pstring
|
return pstring
|
||||||
|
|
||||||
|
|
||||||
def trim_string(string, w, have_dbls):
|
def trim_string(string, w, have_dbls):
|
||||||
if w <= 0:
|
if w <= 0:
|
||||||
return ""
|
return ""
|
||||||
|
@ -109,21 +94,23 @@ def trim_string(string, w, have_dbls):
|
||||||
idx = 0
|
idx = 0
|
||||||
while width < w:
|
while width < w:
|
||||||
chrs.append(string[idx])
|
chrs.append(string[idx])
|
||||||
if unicodedata.east_asian_width(string[idx]) in ['W', 'F']:
|
if unicodedata.east_asian_width(string[idx]) in ["W", "F"]:
|
||||||
width += 2
|
width += 2
|
||||||
else:
|
else:
|
||||||
width += 1
|
width += 1
|
||||||
idx += 1
|
idx += 1
|
||||||
if width != w:
|
if width != w:
|
||||||
chrs.pop()
|
chrs.pop()
|
||||||
chrs.append('.')
|
chrs.append(".")
|
||||||
return u"%s "%("".join(chrs))
|
return u"%s " % ("".join(chrs))
|
||||||
else:
|
else:
|
||||||
return u"%s "%(string[0:w-1])
|
return u"%s " % (string[0:w - 1])
|
||||||
|
|
||||||
#Dots are slow
|
#Dots are slow
|
||||||
eaw = unicodedata.east_asian_width
|
eaw = unicodedata.east_asian_width
|
||||||
ud_normalize = unicodedata.normalize
|
ud_normalize = unicodedata.normalize
|
||||||
|
|
||||||
|
|
||||||
def format_column(col, lim):
|
def format_column(col, lim):
|
||||||
dbls = 0
|
dbls = 0
|
||||||
#Chosen over isinstance(col, unicode) and col.__class__ == unicode
|
#Chosen over isinstance(col, unicode) and col.__class__ == unicode
|
||||||
|
@ -132,22 +119,25 @@ def format_column(col, lim):
|
||||||
if haveud and col.__class__ is unicode:
|
if haveud and col.__class__ is unicode:
|
||||||
# might have some double width chars
|
# might have some double width chars
|
||||||
col = ud_normalize("NFC", col)
|
col = ud_normalize("NFC", col)
|
||||||
dbls = sum(eaw(c) in 'WF' for c in col)
|
dbls = sum(eaw(c) in "WF" for c in col)
|
||||||
size = len(col)+dbls
|
size = len(col) + dbls
|
||||||
if (size >= lim - 1):
|
if (size >= lim - 1):
|
||||||
return trim_string(col, lim, dbls>0)
|
return trim_string(col, lim, dbls > 0)
|
||||||
else:
|
else:
|
||||||
return "%s%s"%(col, " "*(lim-size))
|
return "%s%s" % (col, " " * (lim - size))
|
||||||
|
|
||||||
|
|
||||||
def format_row(row, column_widths):
|
def format_row(row, column_widths):
|
||||||
return "".join([format_column(row[i], column_widths[i]) for i in range(0, len(row))])
|
return "".join([format_column(row[i], column_widths[i]) for i in range(0, len(row))])
|
||||||
|
|
||||||
_strip_re = re.compile("\{!.*?!\}")
|
_strip_re = re.compile("\{!.*?!\}")
|
||||||
|
|
||||||
|
|
||||||
def remove_formatting(string):
|
def remove_formatting(string):
|
||||||
return re.sub(_strip_re, "", string)
|
return re.sub(_strip_re, "", string)
|
||||||
|
|
||||||
def wrap_string(string,width,min_lines=0,strip_colors=True):
|
|
||||||
|
def wrap_string(string, width, min_lines=0, strip_colors=True):
|
||||||
"""
|
"""
|
||||||
Wrap a string to fit in a particular width. Returns a list of output lines.
|
Wrap a string to fit in a particular width. Returns a list of output lines.
|
||||||
|
|
||||||
|
@ -161,27 +151,27 @@ def wrap_string(string,width,min_lines=0,strip_colors=True):
|
||||||
s1 = string.split("\n")
|
s1 = string.split("\n")
|
||||||
|
|
||||||
def insert_clr(s, offset, mtchs, clrs):
|
def insert_clr(s, offset, mtchs, clrs):
|
||||||
end_pos = offset+len(s)
|
end_pos = offset + len(s)
|
||||||
while mtchs and (mtchs[0] <= end_pos) and (mtchs[0] >= offset):
|
while mtchs and (mtchs[0] <= end_pos) and (mtchs[0] >= offset):
|
||||||
mtc = mtchs.popleft()-offset
|
mtc = mtchs.popleft() - offset
|
||||||
clr = clrs.popleft()
|
clr = clrs.popleft()
|
||||||
end_pos += len(clr)
|
end_pos += len(clr)
|
||||||
s = "%s%s%s"%(s[:mtc], clr, s[mtc:])
|
s = "%s%s%s" % (s[:mtc], clr, s[mtc:])
|
||||||
return s
|
return s
|
||||||
|
|
||||||
for s in s1:
|
for s in s1:
|
||||||
cur_pos = offset = 0
|
offset = 0
|
||||||
if strip_colors:
|
if strip_colors:
|
||||||
mtchs = deque()
|
mtchs = deque()
|
||||||
clrs = deque()
|
clrs = deque()
|
||||||
for m in _strip_re.finditer(s):
|
for m in _strip_re.finditer(s):
|
||||||
mtchs.append(m.start())
|
mtchs.append(m.start())
|
||||||
clrs.append(m.group())
|
clrs.append(m.group())
|
||||||
cstr = _strip_re.sub('', s)
|
cstr = _strip_re.sub("", s)
|
||||||
else:
|
else:
|
||||||
cstr = s
|
cstr = s
|
||||||
while len(cstr) > width:
|
while len(cstr) > width:
|
||||||
sidx = cstr.rfind(" ", 0, width-1)
|
sidx = cstr.rfind(" ", 0, width - 1)
|
||||||
sidx += 1
|
sidx += 1
|
||||||
if sidx > 0:
|
if sidx > 0:
|
||||||
if strip_colors:
|
if strip_colors:
|
||||||
|
@ -208,13 +198,13 @@ def wrap_string(string,width,min_lines=0,strip_colors=True):
|
||||||
if not cstr:
|
if not cstr:
|
||||||
cstr = None
|
cstr = None
|
||||||
break
|
break
|
||||||
if cstr != None:
|
if cstr is not None:
|
||||||
if strip_colors:
|
if strip_colors:
|
||||||
ret.append(insert_clr(cstr, offset, mtchs, clrs))
|
ret.append(insert_clr(cstr, offset, mtchs, clrs))
|
||||||
else:
|
else:
|
||||||
ret.append(cstr)
|
ret.append(cstr)
|
||||||
|
|
||||||
if min_lines>0:
|
if min_lines > 0:
|
||||||
for i in range(len(ret), min_lines):
|
for i in range(len(ret), min_lines):
|
||||||
ret.append(" ")
|
ret.append(" ")
|
||||||
|
|
||||||
|
@ -236,17 +226,9 @@ def strwidth(string):
|
||||||
Measure width of a string considering asian double width characters
|
Measure width of a string considering asian double width characters
|
||||||
"""
|
"""
|
||||||
if not isinstance(string, unicode):
|
if not isinstance(string, unicode):
|
||||||
string = unicode(string, 'utf-8')
|
string = unicode(string, "utf-8")
|
||||||
eaw = east_asian_width
|
return sum([1 + (east_asian_width(char) in ["W", "F"]) for char in string])
|
||||||
length = sum( [1 + (eaw(c) in ['W', 'F']) for c in string] )
|
|
||||||
#Using list comprehenstion for improved performance
|
|
||||||
#The code above is equal to:
|
|
||||||
#length = 0
|
|
||||||
#for char in string:
|
|
||||||
#length += 1
|
|
||||||
#if east_asian_width(char) in ['W','F']:
|
|
||||||
#length += 1
|
|
||||||
return length
|
|
||||||
|
|
||||||
def pad_string(string, length, character=" ", side="right"):
|
def pad_string(string, length, character=" ", side="right"):
|
||||||
"""
|
"""
|
||||||
|
@ -254,7 +236,7 @@ def pad_string(string, length, character=" ", side="right"):
|
||||||
"""
|
"""
|
||||||
w = strwidth(string)
|
w = strwidth(string)
|
||||||
diff = length - w
|
diff = length - w
|
||||||
if side == "left":
|
if side == "left":
|
||||||
return "%s%s" % (character * diff, string)
|
return "%s%s" % (character * diff, string)
|
||||||
elif side == "right":
|
elif side == "right":
|
||||||
return "%s%s" % (string, character * diff)
|
return "%s%s" % (string, character * diff)
|
||||||
|
|
|
@ -1,40 +1,12 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# input_popup.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
|
||||||
# Complete function from commands/add.py:
|
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -51,6 +23,7 @@ from popup import ALIGN, Popup
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class InputField:
|
class InputField:
|
||||||
depend = None
|
depend = None
|
||||||
# render the input. return number of rows taken up
|
# render the input. return number of rows taken up
|
||||||
|
@ -58,18 +31,21 @@ class InputField:
|
||||||
def get_height(self):
|
def get_height(self):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def render(self,screen,row,width,selected,col=1):
|
def render(self, screen, row, width, selected, col=1):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if c in [curses.KEY_ENTER, 10, 127, 113]:
|
if c in [curses.KEY_ENTER, 10, 127, 113]:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_depend(self,i,inverse=False):
|
def set_depend(self, i, inverse=False):
|
||||||
if not isinstance(i, CheckedInput):
|
if not isinstance(i, CheckedInput):
|
||||||
raise Exception("Can only depend on CheckedInputs")
|
raise Exception("Can only depend on CheckedInputs")
|
||||||
self.depend = i
|
self.depend = i
|
||||||
|
@ -83,14 +59,15 @@ class InputField:
|
||||||
else:
|
else:
|
||||||
return not self.depend.checked
|
return not self.depend.checked
|
||||||
|
|
||||||
|
|
||||||
class CheckedInput(InputField):
|
class CheckedInput(InputField):
|
||||||
def __init__(self, parent, message, name, checked=False, additional_formatting=False):
|
def __init__(self, parent, message, name, checked=False, additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
self.chkd_inact = "[X] %s"%message
|
self.chkd_inact = "[X] %s" % message
|
||||||
self.unchkd_inact = "[ ] %s"%message
|
self.unchkd_inact = "[ ] %s" % message
|
||||||
self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s"%message
|
self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s" % message
|
||||||
self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s"%message
|
self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s" % message
|
||||||
self.name = name
|
self.name = name
|
||||||
self.checked = checked
|
self.checked = checked
|
||||||
|
|
||||||
|
@ -118,21 +95,22 @@ class CheckedInput(InputField):
|
||||||
def set_value(self, c):
|
def set_value(self, c):
|
||||||
self.checked = c
|
self.checked = c
|
||||||
|
|
||||||
|
|
||||||
class CheckedPlusInput(InputField):
|
class CheckedPlusInput(InputField):
|
||||||
def __init__(self, parent, message, name, child, checked=False, additional_formatting=False):
|
def __init__(self, parent, message, name, child, checked=False, additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
self.chkd_inact = "[X] %s"%message
|
self.chkd_inact = "[X] %s" % message
|
||||||
self.unchkd_inact = "[ ] %s"%message
|
self.unchkd_inact = "[ ] %s" % message
|
||||||
self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s"%message
|
self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s" % message
|
||||||
self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s"%message
|
self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s" % message
|
||||||
self.name = name
|
self.name = name
|
||||||
self.checked = checked
|
self.checked = checked
|
||||||
self.msglen = len(self.chkd_inact)+1
|
self.msglen = len(self.chkd_inact) + 1
|
||||||
self.child = child
|
self.child = child
|
||||||
self.child_active = False
|
self.child_active = False
|
||||||
|
|
||||||
def get_height():
|
def get_height(self):
|
||||||
return max(2, self.child.height)
|
return max(2, self.child.height)
|
||||||
|
|
||||||
def render(self, screen, row, width, active, col=1):
|
def render(self, screen, row, width, active, col=1):
|
||||||
|
@ -147,24 +125,26 @@ class CheckedPlusInput(InputField):
|
||||||
self.parent.add_string(row, self.unchkd_inact, screen, col, False, True)
|
self.parent.add_string(row, self.unchkd_inact, screen, col, False, True)
|
||||||
|
|
||||||
if active and self.checked and self.child_active:
|
if active and self.checked and self.child_active:
|
||||||
self.parent.add_string(row+1, "(esc to leave)", screen, col, False, True)
|
self.parent.add_string(row + 1, "(esc to leave)", screen, col, False, True)
|
||||||
elif active and self.checked:
|
elif active and self.checked:
|
||||||
self.parent.add_string(row+1, "(right arrow to edit)", screen, col, False, True)
|
self.parent.add_string(row + 1, "(right arrow to edit)", screen, col, False, True)
|
||||||
rows = 2
|
rows = 2
|
||||||
# show child
|
# show child
|
||||||
if self.checked:
|
if self.checked:
|
||||||
if isinstance(self.child, (TextInput, IntSpinInput, FloatSpinInput)):
|
if isinstance(self.child, (TextInput, IntSpinInput, FloatSpinInput)):
|
||||||
crows = self.child.render(screen, row, width-self.msglen, self.child_active and active, col+self.msglen, self.msglen)
|
crows = self.child.render(screen, row, width - self.msglen,
|
||||||
|
self.child_active and active, col + self.msglen, self.msglen)
|
||||||
else:
|
else:
|
||||||
crows = self.child.render(screen, row, width-self.msglen, self.child_active and active, col+self.msglen)
|
crows = self.child.render(screen, row, width - self.msglen,
|
||||||
|
self.child_active and active, col + self.msglen)
|
||||||
rows = max(rows, crows)
|
rows = max(rows, crows)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "(enable to view/edit value)", screen, col+self.msglen, False, True)
|
self.parent.add_string(row, "(enable to view/edit value)", screen, col + self.msglen, False, True)
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if self.child_active:
|
if self.child_active:
|
||||||
if c == 27: # leave child on esc
|
if c == 27: # leave child on esc
|
||||||
self.child_active = False
|
self.child_active = False
|
||||||
return
|
return
|
||||||
# pass keys through to child
|
# pass keys through to child
|
||||||
|
@ -186,7 +166,8 @@ class CheckedPlusInput(InputField):
|
||||||
|
|
||||||
|
|
||||||
class IntSpinInput(InputField):
|
class IntSpinInput(InputField):
|
||||||
def __init__(self, parent, message, name, move_func, value, min_val=None, max_val=None, additional_formatting=False):
|
def __init__(self, parent, message, name, move_func, value, min_val=None, max_val=None,
|
||||||
|
additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.message = message
|
self.message = message
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -194,11 +175,11 @@ class IntSpinInput(InputField):
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
|
|
||||||
self.default_str = str(value)
|
self.default_str = str(value)
|
||||||
self.set_value( value)
|
self.set_value(value)
|
||||||
self.default_value = self.value
|
self.default_value = self.value
|
||||||
|
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
self.cursoff = colors.get_line_width(self.message)+4 # + 4 for the " [ " in the rendered string
|
self.cursoff = colors.get_line_width(self.message) + 4 # + 4 for the " [ " in the rendered string
|
||||||
self.move_func = move_func
|
self.move_func = move_func
|
||||||
self.min_val = min_val
|
self.min_val = min_val
|
||||||
self.max_val = max_val
|
self.max_val = max_val
|
||||||
|
@ -208,14 +189,14 @@ class IntSpinInput(InputField):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def __limit_value(self):
|
def __limit_value(self):
|
||||||
if (self.min_val != None) and self.value < self.min_val:
|
if (self.min_val is not None) and self.value < self.min_val:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
if (self.max_val != None) and self.value > self.max_val:
|
if (self.max_val is not None) and self.value > self.max_val:
|
||||||
self.value = self.max_val
|
self.value = self.max_val
|
||||||
|
|
||||||
def render(self, screen, row, width, active, col=1, cursor_offset=0):
|
def render(self, screen, row, width, active, col=1, cursor_offset=0):
|
||||||
if not active and self.need_update:
|
if not active and self.need_update:
|
||||||
if not self.valstr or self.valstr == '-':
|
if not self.valstr or self.valstr == "-":
|
||||||
self.value = self.default_value
|
self.value = self.default_value
|
||||||
self.valstr = self.default_str
|
self.valstr = self.default_str
|
||||||
try:
|
try:
|
||||||
|
@ -225,11 +206,11 @@ class IntSpinInput(InputField):
|
||||||
else:
|
else:
|
||||||
self.value = int(self.valstr)
|
self.value = int(self.valstr)
|
||||||
self.__limit_value()
|
self.__limit_value()
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
self.cursor = colors.get_line_width(self.valstr)
|
self.cursor = colors.get_line_width(self.valstr)
|
||||||
self.need_update = False
|
self.need_update = False
|
||||||
elif self.need_update and self.valstr != '-':
|
elif self.need_update and self.valstr != "-":
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
try:
|
try:
|
||||||
self.value = int(self.valstr)
|
self.value = int(self.valstr)
|
||||||
|
@ -240,49 +221,55 @@ class IntSpinInput(InputField):
|
||||||
except:
|
except:
|
||||||
self.real_value = False
|
self.real_value = False
|
||||||
if not self.valstr:
|
if not self.valstr:
|
||||||
self.parent.add_string(row, "%s {!input!}[ ]"%self.message, screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True)
|
||||||
elif active:
|
elif active:
|
||||||
self.parent.add_string(row, "%s {!input!}[ {!black,white,bold!}%s{!input!} ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ {!black,white,bold!}%s{!input!} ]" % (
|
||||||
|
self.message, self.valstr), screen, col, False, True)
|
||||||
elif self.additional_formatting and self.valstr == self.default_str:
|
elif self.additional_formatting and self.valstr == self.default_str:
|
||||||
self.parent.add_string(row, "%s {!input!}[ {!magenta,black!}%s{!input!} ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ {!magenta,black!}%s{!input!} ]" % (
|
||||||
|
self.message, self.valstr), screen, col, False, True)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "%s {!input!}[ %s ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True)
|
||||||
|
|
||||||
if active:
|
if active:
|
||||||
self.move_func(row, self.cursor+self.cursoff+cursor_offset)
|
self.move_func(row, self.cursor + self.cursoff + cursor_offset)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if c == curses.KEY_PPAGE and self.value < self.max_val:
|
if c == curses.KEY_PPAGE and self.value < self.max_val:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
else:
|
else:
|
||||||
self.value+=1
|
self.value += 1
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_NPAGE and self.value > self.min_val:
|
elif c == curses.KEY_NPAGE and self.value > self.min_val:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
else:
|
else:
|
||||||
self.value-=1
|
self.value -= 1
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_LEFT:
|
elif c == curses.KEY_LEFT:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
self.cursor = max(0, self.cursor-1)
|
return None
|
||||||
|
self.cursor = max(0, self.cursor - 1)
|
||||||
elif c == curses.KEY_RIGHT:
|
elif c == curses.KEY_RIGHT:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
self.cursor = min(len(self.valstr), self.cursor+1)
|
return None
|
||||||
|
self.cursor = min(len(self.valstr), self.cursor + 1)
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_BACKSPACE or c == 127:
|
elif c == curses.KEY_BACKSPACE or c == 127:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
|
@ -290,14 +277,15 @@ class IntSpinInput(InputField):
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif self.valstr and self.cursor > 0:
|
elif self.valstr and self.cursor > 0:
|
||||||
self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:]
|
self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:]
|
||||||
self.cursor-=1
|
self.cursor -= 1
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c == curses.KEY_DC:
|
elif c == curses.KEY_DC:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
if self.valstr and self.cursor < len(self.valstr):
|
if self.valstr and self.cursor < len(self.valstr):
|
||||||
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor+1:]
|
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:]
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c == 45 and self.min_val < 0:
|
elif c == 45 and self.min_val < 0:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
|
@ -305,10 +293,12 @@ class IntSpinInput(InputField):
|
||||||
self.cursor = 1
|
self.cursor = 1
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
if self.cursor != 0: return
|
if self.cursor != 0:
|
||||||
minus_place = self.valstr.find('-')
|
return
|
||||||
if minus_place >= 0: return
|
minus_place = self.valstr.find("-")
|
||||||
self.valstr = chr(c)+self.valstr
|
if minus_place >= 0:
|
||||||
|
return
|
||||||
|
self.valstr = chr(c) + self.valstr
|
||||||
self.cursor += 1
|
self.cursor += 1
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c > 47 and c < 58:
|
elif c > 47 and c < 58:
|
||||||
|
@ -317,9 +307,11 @@ class IntSpinInput(InputField):
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
if c == 48 and self.cursor == 0: return
|
if c == 48 and self.cursor == 0:
|
||||||
minus_place = self.valstr.find('-')
|
return
|
||||||
if self.cursor <= minus_place: return
|
minus_place = self.valstr.find("-")
|
||||||
|
if self.cursor <= minus_place:
|
||||||
|
return
|
||||||
if self.cursor == len(self.valstr):
|
if self.cursor == len(self.valstr):
|
||||||
self.valstr += chr(c)
|
self.valstr += chr(c)
|
||||||
else:
|
else:
|
||||||
|
@ -327,8 +319,7 @@ class IntSpinInput(InputField):
|
||||||
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
# Move the cursor forward
|
# Move the cursor forward
|
||||||
self.cursor+=1
|
self.cursor += 1
|
||||||
|
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
if self.real_value:
|
if self.real_value:
|
||||||
|
@ -348,9 +339,10 @@ class IntSpinInput(InputField):
|
||||||
self.valstr = val
|
self.valstr = val
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
|
|
||||||
#TODO: This vvvvv
|
|
||||||
class FloatSpinInput(InputField):
|
class FloatSpinInput(InputField):
|
||||||
def __init__(self, parent, message, name, move_func, value, inc_amt, precision, min_val=None, max_val=None, additional_formatting = False):
|
def __init__(self, parent, message, name, move_func, value, inc_amt, precision, min_val=None,
|
||||||
|
max_val=None, additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.message = message
|
self.message = message
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -359,14 +351,14 @@ class FloatSpinInput(InputField):
|
||||||
|
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
|
|
||||||
self.fmt = "%%.%df"%precision
|
self.fmt = "%%.%df" % precision
|
||||||
|
|
||||||
self.default_str = str(value)
|
self.default_str = str(value)
|
||||||
self.set_value(value)
|
self.set_value(value)
|
||||||
self.default_value = self.value
|
self.default_value = self.value
|
||||||
|
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
self.cursoff = colors.get_line_width(self.message)+4 # + 4 for the " [ " in the rendered string
|
self.cursoff = colors.get_line_width(self.message) + 4 # + 4 for the " [ " in the rendered string
|
||||||
self.move_func = move_func
|
self.move_func = move_func
|
||||||
self.min_val = min_val
|
self.min_val = min_val
|
||||||
self.max_val = max_val
|
self.max_val = max_val
|
||||||
|
@ -376,15 +368,15 @@ class FloatSpinInput(InputField):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def __limit_value(self):
|
def __limit_value(self):
|
||||||
if (self.min_val != None) and self.value < self.min_val:
|
if (self.min_val is not None) and self.value < self.min_val:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
if (self.max_val != None) and self.value > self.max_val:
|
if (self.max_val is not None) and self.value > self.max_val:
|
||||||
self.value = self.max_val
|
self.value = self.max_val
|
||||||
self.valstr = self.fmt % self.value
|
self.valstr = self.fmt % self.value
|
||||||
|
|
||||||
def render(self, screen, row, width, active, col=1, cursor_offset=0):
|
def render(self, screen, row, width, active, col=1, cursor_offset=0):
|
||||||
if not active and self.need_update:
|
if not active and self.need_update:
|
||||||
if not self.valstr or self.valstr == '-':
|
if not self.valstr or self.valstr == "-":
|
||||||
self.value = self.default_value
|
self.value = self.default_value
|
||||||
self.valstr = self.default_str
|
self.valstr = self.default_str
|
||||||
try:
|
try:
|
||||||
|
@ -398,7 +390,7 @@ class FloatSpinInput(InputField):
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
self.cursor = colors.get_line_width(self.valstr)
|
self.cursor = colors.get_line_width(self.valstr)
|
||||||
self.need_update = False
|
self.need_update = False
|
||||||
elif self.need_update and self.valstr != '-':
|
elif self.need_update and self.valstr != "-":
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
try:
|
try:
|
||||||
self.value = round(float(self.valstr), self.precision)
|
self.value = round(float(self.valstr), self.precision)
|
||||||
|
@ -410,50 +402,56 @@ class FloatSpinInput(InputField):
|
||||||
self.real_value = False
|
self.real_value = False
|
||||||
|
|
||||||
if not self.valstr:
|
if not self.valstr:
|
||||||
self.parent.add_string(row, "%s {!input!}[ ]"%self.message, screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True)
|
||||||
elif active:
|
elif active:
|
||||||
self.parent.add_string(row, "%s {!input!}[ {!black,white,bold!}%s{!white,black!} ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ {!black,white,bold!}%s{!white,black!} ]" % (
|
||||||
|
self.message, self.valstr), screen, col, False, True)
|
||||||
elif self.additional_formatting and self.valstr == self.default_str:
|
elif self.additional_formatting and self.valstr == self.default_str:
|
||||||
self.parent.add_string(row, "%s {!input!}[ {!magenta,black!}%s{!input!} ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ {!magenta,black!}%s{!input!} ]" % (
|
||||||
|
self.message, self.valstr), screen, col, False, True)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "%s {!input!}[ %s ]"%(self.message, self.valstr), screen, col, False, True)
|
self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True)
|
||||||
if active:
|
if active:
|
||||||
self.move_func(row, self.cursor+self.cursoff+cursor_offset)
|
self.move_func(row, self.cursor + self.cursoff + cursor_offset)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if c == curses.KEY_PPAGE:
|
if c == curses.KEY_PPAGE:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
else:
|
else:
|
||||||
self.value+=self.inc_amt
|
self.value += self.inc_amt
|
||||||
self.__limit_value()
|
self.__limit_value()
|
||||||
self.valstr = self.fmt%self.value
|
self.valstr = self.fmt % self.value
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_NPAGE:
|
elif c == curses.KEY_NPAGE:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
self.value = self.min_val
|
self.value = self.min_val
|
||||||
self.valstr = "%d"%self.value
|
self.valstr = "%d" % self.value
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
else:
|
else:
|
||||||
self.value-=self.inc_amt
|
self.value -= self.inc_amt
|
||||||
self.__limit_value()
|
self.__limit_value()
|
||||||
self.valstr = self.fmt%self.value
|
self.valstr = self.fmt % self.value
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_LEFT:
|
elif c == curses.KEY_LEFT:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
self.cursor = max(0, self.cursor-1)
|
return None
|
||||||
|
self.cursor = max(0, self.cursor - 1)
|
||||||
elif c == curses.KEY_RIGHT:
|
elif c == curses.KEY_RIGHT:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
self.cursor = min(len(self.valstr), self.cursor+1)
|
return None
|
||||||
|
self.cursor = min(len(self.valstr), self.cursor + 1)
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
elif c == curses.KEY_BACKSPACE or c == 127:
|
elif c == curses.KEY_BACKSPACE or c == 127:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
|
@ -461,14 +459,15 @@ class FloatSpinInput(InputField):
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif self.valstr and self.cursor > 0:
|
elif self.valstr and self.cursor > 0:
|
||||||
self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:]
|
self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:]
|
||||||
self.cursor-=1
|
self.cursor -= 1
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c == curses.KEY_DC:
|
elif c == curses.KEY_DC:
|
||||||
if not self.real_value: return None
|
if not self.real_value:
|
||||||
|
return None
|
||||||
if self.valstr and self.cursor < len(self.valstr):
|
if self.valstr and self.cursor < len(self.valstr):
|
||||||
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor+1:]
|
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:]
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c == 45 and self.min_val < 0:
|
elif c == 45 and self.min_val < 0:
|
||||||
if not self.real_value:
|
if not self.real_value:
|
||||||
|
@ -476,10 +475,12 @@ class FloatSpinInput(InputField):
|
||||||
self.cursor = 1
|
self.cursor = 1
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
if self.cursor != 0: return
|
if self.cursor != 0:
|
||||||
minus_place = self.valstr.find('-')
|
return
|
||||||
if minus_place >= 0: return
|
minus_place = self.valstr.find("-")
|
||||||
self.valstr = chr(c)+self.valstr
|
if minus_place >= 0:
|
||||||
|
return
|
||||||
|
self.valstr = chr(c) + self.valstr
|
||||||
self.cursor += 1
|
self.cursor += 1
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
elif c == 46:
|
elif c == 46:
|
||||||
|
@ -488,10 +489,12 @@ class FloatSpinInput(InputField):
|
||||||
self.cursor = 2
|
self.cursor = 2
|
||||||
self.real_value = True
|
self.real_value = True
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
minus_place = self.valstr.find('-')
|
minus_place = self.valstr.find("-")
|
||||||
if self.cursor <= minus_place: return
|
if self.cursor <= minus_place:
|
||||||
point_place = self.valstr.find('.')
|
return
|
||||||
if point_place >= 0: return
|
point_place = self.valstr.find(".")
|
||||||
|
if point_place >= 0:
|
||||||
|
return
|
||||||
if self.cursor == len(self.valstr):
|
if self.cursor == len(self.valstr):
|
||||||
self.valstr += chr(c)
|
self.valstr += chr(c)
|
||||||
else:
|
else:
|
||||||
|
@ -499,7 +502,7 @@ class FloatSpinInput(InputField):
|
||||||
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
# Move the cursor forward
|
# Move the cursor forward
|
||||||
self.cursor+=1
|
self.cursor += 1
|
||||||
elif (c > 47 and c < 58):
|
elif (c > 47 and c < 58):
|
||||||
if (not self.real_value) and self.valstr:
|
if (not self.real_value) and self.valstr:
|
||||||
self.valstr = ""
|
self.valstr = ""
|
||||||
|
@ -508,8 +511,9 @@ class FloatSpinInput(InputField):
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
if self.value == "mixed":
|
if self.value == "mixed":
|
||||||
self.value = ""
|
self.value = ""
|
||||||
minus_place = self.valstr.find('-')
|
minus_place = self.valstr.find("-")
|
||||||
if self.cursor <= minus_place: return
|
if self.cursor <= minus_place:
|
||||||
|
return
|
||||||
if self.cursor == len(self.valstr):
|
if self.cursor == len(self.valstr):
|
||||||
self.valstr += chr(c)
|
self.valstr += chr(c)
|
||||||
else:
|
else:
|
||||||
|
@ -517,7 +521,7 @@ class FloatSpinInput(InputField):
|
||||||
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:]
|
||||||
self.need_update = True
|
self.need_update = True
|
||||||
# Move the cursor forward
|
# Move the cursor forward
|
||||||
self.cursor+=1
|
self.cursor += 1
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
if self.real_value:
|
if self.real_value:
|
||||||
|
@ -537,8 +541,9 @@ class FloatSpinInput(InputField):
|
||||||
self.valstr = val
|
self.valstr = val
|
||||||
self.cursor = len(self.valstr)
|
self.cursor = len(self.valstr)
|
||||||
|
|
||||||
|
|
||||||
class SelectInput(InputField):
|
class SelectInput(InputField):
|
||||||
def __init__(self, parent, message, name, opts, vals, selidx, additional_formatting = False):
|
def __init__(self, parent, message, name, opts, vals, selidx, additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.message = message
|
self.message = message
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
|
@ -555,20 +560,21 @@ class SelectInput(InputField):
|
||||||
if self.message:
|
if self.message:
|
||||||
self.parent.add_string(row, self.message, screen, col, False, True)
|
self.parent.add_string(row, self.message, screen, col, False, True)
|
||||||
row += 1
|
row += 1
|
||||||
off = col+1
|
off = col + 1
|
||||||
for i, opt in enumerate(self.opts):
|
for i, opt in enumerate(self.opts):
|
||||||
if selected and i == self.selidx:
|
if selected and i == self.selidx:
|
||||||
self.parent.add_string(row, "{!black,white,bold!}[%s]"%opt, screen, off, False, True)
|
self.parent.add_string(row, "{!black,white,bold!}[%s]" % opt, screen, off, False, True)
|
||||||
elif i == self.selidx:
|
elif i == self.selidx:
|
||||||
if self.additional_formatting and i == self.default_option:
|
if self.additional_formatting and i == self.default_option:
|
||||||
self.parent.add_string(row, "[{!magenta,black!}%s{!white,black!}]"%opt, screen, off, False, True)
|
self.parent.add_string(row, "[{!magenta,black!}%s{!white,black!}]" % opt, screen, off, False, True)
|
||||||
elif self.additional_formatting:
|
elif self.additional_formatting:
|
||||||
self.parent.add_string(row, "[{!white,blue!}%s{!white,black!}]"%opt, screen, off, False, True)
|
self.parent.add_string(row, "[{!white,blue!}%s{!white,black!}]" % opt, screen, off, False, True)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "[{!white,black,underline!}%s{!white,black!}]"%opt, screen, off, False, True)
|
self.parent.add_string(row, "[{!white,black,underline!}%s{!white,black!}]" %
|
||||||
|
opt, screen, off, False, True)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "[%s]"%opt, screen, off, False, True)
|
self.parent.add_string(row, "[%s]" % opt, screen, off, False, True)
|
||||||
off += len(opt)+3
|
off += len(opt) + 3
|
||||||
if self.message:
|
if self.message:
|
||||||
return 2
|
return 2
|
||||||
else:
|
else:
|
||||||
|
@ -576,9 +582,9 @@ class SelectInput(InputField):
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if c == curses.KEY_LEFT:
|
if c == curses.KEY_LEFT:
|
||||||
self.selidx = max(0, self.selidx-1)
|
self.selidx = max(0, self.selidx - 1)
|
||||||
if c == curses.KEY_RIGHT:
|
if c == curses.KEY_RIGHT:
|
||||||
self.selidx = min(len(self.opts)-1, self.selidx+1)
|
self.selidx = min(len(self.opts) - 1, self.selidx + 1)
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
return self.vals[self.selidx]
|
return self.vals[self.selidx]
|
||||||
|
@ -590,11 +596,12 @@ class SelectInput(InputField):
|
||||||
return
|
return
|
||||||
raise Exception("Invalid value for SelectInput")
|
raise Exception("Invalid value for SelectInput")
|
||||||
|
|
||||||
|
|
||||||
class TextInput(InputField):
|
class TextInput(InputField):
|
||||||
def __init__(self, parent, move_func, width, message, name, value, docmp, additional_formatting=False):
|
def __init__(self, parent, move_func, width, message, name, value, docmp, additional_formatting=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.move_func = move_func
|
self.move_func = move_func
|
||||||
self.width = width
|
self.width = width
|
||||||
|
|
||||||
self.additional_formatting = additional_formatting
|
self.additional_formatting = additional_formatting
|
||||||
|
|
||||||
|
@ -612,7 +619,7 @@ class TextInput(InputField):
|
||||||
def get_height(self):
|
def get_height(self):
|
||||||
return 2 + bool(self.message)
|
return 2 + bool(self.message)
|
||||||
|
|
||||||
def render(self,screen,row,width,selected,col=1,cursor_offset=0):
|
def render(self, screen, row, width, selected, col=1, cursor_offset=0):
|
||||||
if not self.value and not selected and len(self.default_value) != 0:
|
if not self.value and not selected and len(self.default_value) != 0:
|
||||||
self.value = self.default_value
|
self.value = self.default_value
|
||||||
self.cursor = len(self.value)
|
self.cursor = len(self.value)
|
||||||
|
@ -622,21 +629,21 @@ class TextInput(InputField):
|
||||||
row += 1
|
row += 1
|
||||||
if selected:
|
if selected:
|
||||||
if self.opts:
|
if self.opts:
|
||||||
self.parent.add_string(row+1, self.opts[self.opt_off:], screen, col, False, True)
|
self.parent.add_string(row + 1, self.opts[self.opt_off:], screen, col, False, True)
|
||||||
if self.cursor > (width-3):
|
if self.cursor > (width - 3):
|
||||||
self.move_func(row, width-2)
|
self.move_func(row, width - 2)
|
||||||
else:
|
else:
|
||||||
self.move_func(row, self.cursor+1+cursor_offset)
|
self.move_func(row, self.cursor + 1 + cursor_offset)
|
||||||
slen = len(self.value)+3
|
slen = len(self.value) + 3
|
||||||
if slen > width:
|
if slen > width:
|
||||||
vstr = self.value[(slen-width):]
|
vstr = self.value[(slen - width):]
|
||||||
else:
|
else:
|
||||||
vstr = self.value.ljust(width-2)
|
vstr = self.value.ljust(width - 2)
|
||||||
|
|
||||||
if self.additional_formatting and len(self.value) != 0 and self.value == self.default_value:
|
if self.additional_formatting and len(self.value) != 0 and self.value == self.default_value:
|
||||||
self.parent.add_string(row, "{!magenta,white!}%s"%vstr, screen, col, False, False)
|
self.parent.add_string(row, "{!magenta,white!}%s" % vstr, screen, col, False, False)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(row, "{!black,white,bold!}%s"%vstr, screen, col, False, False)
|
self.parent.add_string(row, "{!black,white,bold!}%s" % vstr, screen, col, False, False)
|
||||||
|
|
||||||
if self.message:
|
if self.message:
|
||||||
return 3
|
return 3
|
||||||
|
@ -666,15 +673,15 @@ class TextInput(InputField):
|
||||||
if self.cursor == len(self.value) or self.value[self.cursor] == " ":
|
if self.cursor == len(self.value) or self.value[self.cursor] == " ":
|
||||||
if self.opts:
|
if self.opts:
|
||||||
prev = self.opt_off
|
prev = self.opt_off
|
||||||
self.opt_off += self.width-3
|
self.opt_off += self.width - 3
|
||||||
# now find previous double space, best guess at a split point
|
# now find previous double space, best guess at a split point
|
||||||
# in future could keep opts unjoined to get this really right
|
# in future could keep opts unjoined to get this really right
|
||||||
self.opt_off = self.opts.rfind(" ", 0, self.opt_off)+2
|
self.opt_off = self.opts.rfind(" ", 0, self.opt_off) + 2
|
||||||
if second_hit and self.opt_off == prev: # double tap and we're at the end
|
if second_hit and self.opt_off == prev: # double tap and we're at the end
|
||||||
self.opt_off = 0
|
self.opt_off = 0
|
||||||
else:
|
else:
|
||||||
opts = self.complete(self.value)
|
opts = self.complete(self.value)
|
||||||
if len(opts) == 1: # only one option, just complete it
|
if len(opts) == 1: # only one option, just complete it
|
||||||
self.value = opts[0]
|
self.value = opts[0]
|
||||||
self.cursor = len(opts[0])
|
self.cursor = len(opts[0])
|
||||||
self.tab_count = 0
|
self.tab_count = 0
|
||||||
|
@ -684,15 +691,15 @@ class TextInput(InputField):
|
||||||
self.value = prefix
|
self.value = prefix
|
||||||
self.cursor = len(prefix)
|
self.cursor = len(prefix)
|
||||||
|
|
||||||
if len(opts) > 1 and second_hit: # display multiple options on second tab hit
|
if len(opts) > 1 and second_hit: # display multiple options on second tab hit
|
||||||
sp = self.value.rfind(os.sep)+1
|
sp = self.value.rfind(os.sep) + 1
|
||||||
self.opts = " ".join([o[sp:] for o in opts])
|
self.opts = " ".join([o[sp:] for o in opts])
|
||||||
|
|
||||||
# Cursor movement
|
# Cursor movement
|
||||||
elif c == curses.KEY_LEFT:
|
elif c == curses.KEY_LEFT:
|
||||||
self.cursor = max(0, self.cursor-1)
|
self.cursor = max(0, self.cursor - 1)
|
||||||
elif c == curses.KEY_RIGHT:
|
elif c == curses.KEY_RIGHT:
|
||||||
self.cursor = min(len(self.value), self.cursor+1)
|
self.cursor = min(len(self.value), self.cursor + 1)
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
|
@ -705,12 +712,12 @@ class TextInput(InputField):
|
||||||
|
|
||||||
# Delete a character in the input string based on cursor position
|
# Delete a character in the input string based on cursor position
|
||||||
if c == curses.KEY_BACKSPACE or c == 127:
|
if c == curses.KEY_BACKSPACE or c == 127:
|
||||||
if self.value and self.cursor > 0:
|
if self.value and self.cursor > 0:
|
||||||
self.value = self.value[:self.cursor - 1] + self.value[self.cursor:]
|
self.value = self.value[:self.cursor - 1] + self.value[self.cursor:]
|
||||||
self.cursor-=1
|
self.cursor -= 1
|
||||||
elif c == curses.KEY_DC:
|
elif c == curses.KEY_DC:
|
||||||
if self.value and self.cursor < len(self.value):
|
if self.value and self.cursor < len(self.value):
|
||||||
self.value = self.value[:self.cursor] + self.value[self.cursor+1:]
|
self.value = self.value[:self.cursor] + self.value[self.cursor + 1:]
|
||||||
elif c > 31 and c < 256:
|
elif c > 31 and c < 256:
|
||||||
# Emulate getwch
|
# Emulate getwch
|
||||||
stroke = chr(c)
|
stroke = chr(c)
|
||||||
|
@ -728,8 +735,7 @@ class TextInput(InputField):
|
||||||
# Insert into string
|
# Insert into string
|
||||||
self.value = self.value[:self.cursor] + uchar + self.value[self.cursor:]
|
self.value = self.value[:self.cursor] + uchar + self.value[self.cursor:]
|
||||||
# Move the cursor forward
|
# Move the cursor forward
|
||||||
self.cursor+=1
|
self.cursor += 1
|
||||||
|
|
||||||
|
|
||||||
def complete(self, line):
|
def complete(self, line):
|
||||||
line = os.path.abspath(os.path.expanduser(line))
|
line = os.path.abspath(os.path.expanduser(line))
|
||||||
|
@ -752,7 +758,7 @@ class TextInput(InputField):
|
||||||
# shares a common prefix.
|
# shares a common prefix.
|
||||||
for f in os.listdir(os.path.dirname(line)):
|
for f in os.listdir(os.path.dirname(line)):
|
||||||
if f.startswith(os.path.split(line)[1]):
|
if f.startswith(os.path.split(line)[1]):
|
||||||
ret.append(os.path.join( os.path.dirname(line), f))
|
ret.append(os.path.join(os.path.dirname(line), f))
|
||||||
else:
|
else:
|
||||||
# This path does not exist, so lets do a listdir on it's parent
|
# This path does not exist, so lets do a listdir on it's parent
|
||||||
# and find any matches.
|
# and find any matches.
|
||||||
|
@ -769,12 +775,10 @@ class TextInput(InputField):
|
||||||
|
|
||||||
|
|
||||||
class InputPopup(Popup):
|
class InputPopup(Popup):
|
||||||
def __init__(self,parent_mode,title,width_req=0,height_req=0,
|
def __init__(self, parent_mode, title, width_req=0, height_req=0, align=ALIGN.DEFAULT, close_cb=None,
|
||||||
align=ALIGN.DEFAULT,
|
additional_formatting=True, immediate_action=False):
|
||||||
close_cb=None,
|
Popup.__init__(self, parent_mode, title, width_req=width_req, height_req=height_req,
|
||||||
additional_formatting=True,
|
align=align, close_cb=close_cb)
|
||||||
immediate_action=False):
|
|
||||||
Popup.__init__(self, parent_mode, title, width_req=width_req, height_req=height_req, align=align, close_cb=close_cb)
|
|
||||||
self.inputs = []
|
self.inputs = []
|
||||||
self.lines = []
|
self.lines = []
|
||||||
self.current_input = 0
|
self.current_input = 0
|
||||||
|
@ -798,17 +802,16 @@ class InputPopup(Popup):
|
||||||
:param value: initial value of the field
|
:param value: initial value of the field
|
||||||
:param complete: should completion be run when tab is hit and this field is active
|
:param complete: should completion be run when tab is hit and this field is active
|
||||||
"""
|
"""
|
||||||
self.inputs.append(TextInput(self, self.move, self.width, message,
|
self.inputs.append(TextInput(self, self.move, self.width, message, name, value, complete,
|
||||||
name, value, complete,
|
additional_formatting=self.additional_formatting))
|
||||||
additional_formatting = self.additional_formatting))
|
|
||||||
|
|
||||||
def getmaxyx(self):
|
def getmaxyx(self):
|
||||||
return self.screen.getmaxyx()
|
return self.screen.getmaxyx()
|
||||||
|
|
||||||
def add_string(self, row, string, scr=None, col = 0, pad=True, trim=True):
|
def add_string(self, row, string, scr=None, col=0, pad=True, trim=True):
|
||||||
if row <= 0:
|
if row <= 0:
|
||||||
return False
|
return False
|
||||||
elif row >= self.height -1:
|
elif row >= self.height - 1:
|
||||||
return False
|
return False
|
||||||
self.parent.add_string(row, string, scr, col, pad, trim)
|
self.parent.add_string(row, string, scr, col, pad, trim)
|
||||||
return True
|
return True
|
||||||
|
@ -820,26 +823,26 @@ class InputPopup(Popup):
|
||||||
def add_text(self, string):
|
def add_text(self, string):
|
||||||
lines = string.split("\n")
|
lines = string.split("\n")
|
||||||
for line in lines:
|
for line in lines:
|
||||||
self.lines.append( (len(self.inputs), line) )
|
self.lines.append((len(self.inputs), line))
|
||||||
|
|
||||||
def add_select_input(self, message, name, opts, vals, default_index=0):
|
def add_select_input(self, message, name, opts, vals, default_index=0):
|
||||||
self.inputs.append(SelectInput(self, message, name, opts, vals, default_index,
|
self.inputs.append(SelectInput(self, message, name, opts, vals, default_index,
|
||||||
additional_formatting = self.additional_formatting))
|
additional_formatting=self.additional_formatting))
|
||||||
|
|
||||||
def add_checked_input(self, message, name, checked=False):
|
def add_checked_input(self, message, name, checked=False):
|
||||||
self.inputs.append(CheckedInput(self, message, name, checked,
|
self.inputs.append(CheckedInput(self, message, name, checked,
|
||||||
additional_formatting = self.additional_formatting))
|
additional_formatting=self.additional_formatting))
|
||||||
|
|
||||||
#def add_checked_plus_input(self, message, name, child)
|
#def add_checked_plus_input(self, message, name, child)
|
||||||
|
|
||||||
def add_float_spin_input(self, message, name, value=0.0, inc_amt = 1.0, precision = 1, min_val = None, max_val = None):
|
def add_float_spin_input(self, message, name, value=0.0, inc_amt=1.0, precision=1, min_val=None, max_val=None):
|
||||||
i = FloatSpinInput(self, message, name, self.move, value, inc_amt, precision, min_val, max_val,
|
i = FloatSpinInput(self, message, name, self.move, value, inc_amt, precision, min_val, max_val,
|
||||||
additional_formatting = self.additional_formatting)
|
additional_formatting=self.additional_formatting)
|
||||||
self.inputs.append(i)
|
self.inputs.append(i)
|
||||||
|
|
||||||
def add_int_spin_input(self, message, name, value = 0, min_val = None, max_val = None):
|
def add_int_spin_input(self, message, name, value=0, min_val=None, max_val=None):
|
||||||
i = IntSpinInput(self, message, name, self.move, value, min_val, max_val,
|
i = IntSpinInput(self, message, name, self.move, value, min_val, max_val,
|
||||||
additional_formatting = self.additional_formatting)
|
additional_formatting=self.additional_formatting)
|
||||||
self.inputs.append(i)
|
self.inputs.append(i)
|
||||||
|
|
||||||
def _refresh_lines(self):
|
def _refresh_lines(self):
|
||||||
|
@ -849,7 +852,6 @@ class InputPopup(Popup):
|
||||||
|
|
||||||
start_row = 0
|
start_row = 0
|
||||||
end_row = 0
|
end_row = 0
|
||||||
spos = 0
|
|
||||||
for i, ipt in enumerate(self.inputs):
|
for i, ipt in enumerate(self.inputs):
|
||||||
for line in self.lines:
|
for line in self.lines:
|
||||||
if line[0] == i:
|
if line[0] == i:
|
||||||
|
@ -859,37 +861,36 @@ class InputPopup(Popup):
|
||||||
active = (i == self.current_input)
|
active = (i == self.current_input)
|
||||||
|
|
||||||
if active:
|
if active:
|
||||||
if end_row + 1 >= self.height + self.lineoff :
|
if end_row + 1 >= self.height + self.lineoff:
|
||||||
self.lineoff += ipt.get_height()
|
self.lineoff += ipt.get_height()
|
||||||
elif start_row < self.lineoff:
|
elif start_row < self.lineoff:
|
||||||
self.lineoff -= ipt.get_height()
|
self.lineoff -= ipt.get_height()
|
||||||
self.content_height = end_row
|
self.content_height = end_row
|
||||||
|
|
||||||
crow = 1 - self.lineoff
|
crow = 1 - self.lineoff
|
||||||
spos = 0
|
|
||||||
for i, ipt in enumerate(self.inputs):
|
for i, ipt in enumerate(self.inputs):
|
||||||
for line in self.lines:
|
for line in self.lines:
|
||||||
if line[0] == i:
|
if line[0] == i:
|
||||||
self.add_string(crow, line[1], self.screen, 1, pad=False)
|
self.add_string(crow, line[1], self.screen, 1, pad=False)
|
||||||
crow += 1
|
crow += 1
|
||||||
crow += ipt.render(self.screen, crow, self.width, i==self.current_input)
|
crow += ipt.render(self.screen, crow, self.width, i == self.current_input)
|
||||||
|
|
||||||
if (self.content_height > (self.height-2)):
|
if (self.content_height > (self.height - 2)):
|
||||||
lts = self.content_height-(self.height-3)
|
lts = self.content_height - (self.height - 3)
|
||||||
perc_sc = float(self.lineoff)/lts
|
perc_sc = float(self.lineoff) / lts
|
||||||
sb_pos = int((self.height-2)*perc_sc)+1
|
sb_pos = int((self.height - 2) * perc_sc) + 1
|
||||||
if (sb_pos == 1) and (self.lineoff != 0):
|
if (sb_pos == 1) and (self.lineoff != 0):
|
||||||
sb_pos += 1
|
sb_pos += 1
|
||||||
self.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width-1), pad=False, trim=False)
|
self.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width - 1), pad=False, trim=False)
|
||||||
if self._cursor_row >= 0:
|
if self._cursor_row >= 0:
|
||||||
curses.curs_set(2)
|
curses.curs_set(2)
|
||||||
self.screen.move(self._cursor_row, self._cursor_col)
|
self.screen.move(self._cursor_row, self._cursor_col)
|
||||||
|
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
self.current_input = max(0, self.current_input-1)
|
self.current_input = max(0, self.current_input - 1)
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
self.current_input = min(len(self.inputs)-1, self.current_input+1)
|
self.current_input = min(len(self.inputs) - 1, self.current_input + 1)
|
||||||
elif c == curses.KEY_ENTER or c == 10:
|
elif c == curses.KEY_ENTER or c == 10:
|
||||||
if self.close_cb:
|
if self.close_cb:
|
||||||
vals = {}
|
vals = {}
|
||||||
|
@ -897,8 +898,8 @@ class InputPopup(Popup):
|
||||||
vals[ipt.name] = ipt.get_value()
|
vals[ipt.name] = ipt.get_value()
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
self.close_cb(vals)
|
self.close_cb(vals)
|
||||||
return True # close the popup
|
return True # close the popup
|
||||||
elif c == 27: # close on esc, no action
|
elif c == 27: # close on esc, no action
|
||||||
return True
|
return True
|
||||||
elif self.inputs:
|
elif self.inputs:
|
||||||
self.inputs[self.current_input].handle_read(c)
|
self.inputs[self.current_input].handle_read(c)
|
||||||
|
|
|
@ -1,38 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# legacy.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -45,7 +18,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.configmanager
|
import deluge.configmanager
|
||||||
|
@ -64,10 +37,12 @@ INPUT_HISTORY_SIZE = 500
|
||||||
|
|
||||||
MAX_HISTFILE_SIZE = 2000
|
MAX_HISTFILE_SIZE = 2000
|
||||||
|
|
||||||
|
|
||||||
def complete_line(line, possible_matches):
|
def complete_line(line, possible_matches):
|
||||||
"Find the common prefix of possible matches, proritizing matching-case elements"
|
"Find the common prefix of possible matches, proritizing matching-case elements"
|
||||||
|
|
||||||
if not possible_matches: return line
|
if not possible_matches:
|
||||||
|
return line
|
||||||
|
|
||||||
line = line.replace(r"\ ", " ")
|
line = line.replace(r"\ ", " ")
|
||||||
|
|
||||||
|
@ -79,13 +54,16 @@ def complete_line(line, possible_matches):
|
||||||
match = match.replace(r"\ ", " ")
|
match = match.replace(r"\ ", " ")
|
||||||
m1, m2 = "", ""
|
m1, m2 = "", ""
|
||||||
for i, c in enumerate(line):
|
for i, c in enumerate(line):
|
||||||
if m1 and m2: break
|
if m1 and m2:
|
||||||
|
break
|
||||||
if not m1 and c != line[i]:
|
if not m1 and c != line[i]:
|
||||||
m1 = line[:i]
|
m1 = line[:i]
|
||||||
if not m2 and c.lower() != line[i].lower():
|
if not m2 and c.lower() != line[i].lower():
|
||||||
m2 = line[:i]
|
m2 = line[:i]
|
||||||
if not m1: matches1.append(match)
|
if not m1:
|
||||||
elif not m2: matches2.append(match)
|
matches1.append(match)
|
||||||
|
elif not m2:
|
||||||
|
matches2.append(match)
|
||||||
|
|
||||||
possible_matches = matches1 + matches2
|
possible_matches = matches1 + matches2
|
||||||
|
|
||||||
|
@ -103,15 +81,17 @@ def complete_line(line, possible_matches):
|
||||||
|
|
||||||
return possible_matches[0][:maxlen].replace(" ", r"\ ")
|
return possible_matches[0][:maxlen].replace(" ", r"\ ")
|
||||||
|
|
||||||
|
|
||||||
def commonprefix(m):
|
def commonprefix(m):
|
||||||
"Given a list of pathnames, returns the longest common leading component"
|
"Given a list of pathnames, returns the longest common leading component"
|
||||||
if not m: return ''
|
if not m:
|
||||||
|
return ""
|
||||||
s1 = min(m)
|
s1 = min(m)
|
||||||
s2 = max(m)
|
s2 = max(m)
|
||||||
for i, c in enumerate(s1):
|
for i, c in enumerate(s1):
|
||||||
if c != s2[i]:
|
if c != s2[i]:
|
||||||
return s1[:i]
|
return s1[:i]
|
||||||
return s
|
return s2
|
||||||
|
|
||||||
|
|
||||||
class Legacy(BaseMode, component.Component):
|
class Legacy(BaseMode, component.Component):
|
||||||
|
@ -131,7 +111,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
self.input_incomplete = ""
|
self.input_incomplete = ""
|
||||||
self._old_char = 0
|
self._old_char = 0
|
||||||
self._last_char = 0
|
self._last_char = 0
|
||||||
self._last_del_char = ''
|
self._last_del_char = ""
|
||||||
|
|
||||||
# Keep track of where the cursor is
|
# Keep track of where the cursor is
|
||||||
self.input_cursor = 0
|
self.input_cursor = 0
|
||||||
|
@ -160,14 +140,14 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
if self.console_config["save_legacy_history"]:
|
if self.console_config["save_legacy_history"]:
|
||||||
try:
|
try:
|
||||||
lines1 = open(self.history_file[0], 'r').read().splitlines()
|
lines1 = open(self.history_file[0], "r").read().splitlines()
|
||||||
self._hf_lines[0] = len(lines1)
|
self._hf_lines[0] = len(lines1)
|
||||||
except:
|
except:
|
||||||
lines1 = []
|
lines1 = []
|
||||||
self._hf_lines[0] = 0
|
self._hf_lines[0] = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lines2 = open(self.history_file[1], 'r').read().splitlines()
|
lines2 = open(self.history_file[1], "r").read().splitlines()
|
||||||
self._hf_lines[1] = len(lines2)
|
self._hf_lines[1] = len(lines2)
|
||||||
except:
|
except:
|
||||||
lines2 = []
|
lines2 = []
|
||||||
|
@ -202,7 +182,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
component.start("LegacyUI")
|
component.start("LegacyUI")
|
||||||
|
|
||||||
|
|
||||||
# show the cursor
|
# show the cursor
|
||||||
curses.curs_set(2)
|
curses.curs_set(2)
|
||||||
|
|
||||||
|
@ -213,6 +192,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
# Maintain a list of (torrent_id, name) for use in tab completion
|
# Maintain a list of (torrent_id, name) for use in tab completion
|
||||||
self.torrents = []
|
self.torrents = []
|
||||||
|
|
||||||
def on_session_state(result):
|
def on_session_state(result):
|
||||||
def on_torrents_status(torrents):
|
def on_torrents_status(torrents):
|
||||||
for torrent_id, status in torrents.items():
|
for torrent_id, status in torrents.items():
|
||||||
|
@ -258,7 +238,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
if self.input:
|
if self.input:
|
||||||
self.input = self.input.encode(self.encoding)
|
self.input = self.input.encode(self.encoding)
|
||||||
|
|
||||||
if self.input.endswith('\\'):
|
if self.input.endswith("\\"):
|
||||||
self.input = self.input[:-1]
|
self.input = self.input[:-1]
|
||||||
self.input_cursor -= 1
|
self.input_cursor -= 1
|
||||||
self.add_line("{!yellow,black,bold!}>>>{!input!} %s" % self.input)
|
self.add_line("{!yellow,black,bold!}>>>{!input!} %s" % self.input)
|
||||||
|
@ -456,7 +436,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
self.stdscr.redrawwin()
|
self.stdscr.redrawwin()
|
||||||
self.stdscr.refresh()
|
self.stdscr.refresh()
|
||||||
|
|
||||||
|
|
||||||
def add_line(self, text, refresh=True):
|
def add_line(self, text, refresh=True):
|
||||||
"""
|
"""
|
||||||
Add a line to the screen. This will be showed between the two bars.
|
Add a line to the screen. This will be showed between the two bars.
|
||||||
|
@ -495,13 +474,13 @@ class Legacy(BaseMode, component.Component):
|
||||||
active_file = 0
|
active_file = 0
|
||||||
|
|
||||||
#Write the line
|
#Write the line
|
||||||
f = open(self.history_file[active_file], 'a')
|
f = open(self.history_file[active_file], "a")
|
||||||
|
|
||||||
if isinstance(text, unicode):
|
if isinstance(text, unicode):
|
||||||
text = text.encode(self.encoding)
|
text = text.encode(self.encoding)
|
||||||
f.write(text)
|
f.write(text)
|
||||||
|
|
||||||
f.write( os.linesep )
|
f.write(os.linesep)
|
||||||
|
|
||||||
#And increment line counter
|
#And increment line counter
|
||||||
self._hf_lines[active_file] += 1
|
self._hf_lines[active_file] += 1
|
||||||
|
@ -510,7 +489,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
# therefore swapping the currently active file
|
# therefore swapping the currently active file
|
||||||
if self._hf_lines[active_file] == MAX_HISTFILE_SIZE:
|
if self._hf_lines[active_file] == MAX_HISTFILE_SIZE:
|
||||||
self._hf_lines[1 - active_file] = 0
|
self._hf_lines[1 - active_file] = 0
|
||||||
f = open(self.history_file[1 - active_file], 'w')
|
f = open(self.history_file[1 - active_file], "w")
|
||||||
f.truncate(0)
|
f.truncate(0)
|
||||||
|
|
||||||
def get_line_chunks(line):
|
def get_line_chunks(line):
|
||||||
|
@ -522,20 +501,20 @@ class Legacy(BaseMode, component.Component):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
chunks = []
|
chunks = []
|
||||||
if not line.startswith('{!'):
|
if not line.startswith("{!"):
|
||||||
begin = line.find('{!')
|
begin = line.find("{!")
|
||||||
if begin == -1:
|
if begin == -1:
|
||||||
begin = len(line)
|
begin = len(line)
|
||||||
chunks.append( ('', line[:begin]) )
|
chunks.append(("", line[:begin]))
|
||||||
line = line[begin:]
|
line = line[begin:]
|
||||||
|
|
||||||
while line:
|
while line:
|
||||||
# We know the line starts with '{!' here
|
# We know the line starts with "{!" here
|
||||||
end_color = line.find('!}')
|
end_color = line.find("!}")
|
||||||
next_color = line.find('{!', end_color)
|
next_color = line.find("{!", end_color)
|
||||||
if next_color == -1:
|
if next_color == -1:
|
||||||
next_color = len(line)
|
next_color = len(line)
|
||||||
chunks.append( (line[:end_color+2], line[end_color+2:next_color]) )
|
chunks.append((line[:end_color + 2], line[end_color + 2:next_color]))
|
||||||
line = line[next_color:]
|
line = line[next_color:]
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
|
@ -582,7 +561,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
if refresh:
|
if refresh:
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
def add_string(self, row, string):
|
def add_string(self, row, string):
|
||||||
"""
|
"""
|
||||||
Adds a string to the desired `:param:row`.
|
Adds a string to the desired `:param:row`.
|
||||||
|
@ -608,7 +586,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
col += strwidth(s)
|
col += strwidth(s)
|
||||||
|
|
||||||
|
|
||||||
def do_command(self, cmd):
|
def do_command(self, cmd):
|
||||||
"""
|
"""
|
||||||
Processes a command.
|
Processes a command.
|
||||||
|
@ -618,7 +595,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
"""
|
"""
|
||||||
if not cmd:
|
if not cmd:
|
||||||
return
|
return
|
||||||
cmd, _, line = cmd.partition(' ')
|
cmd, _, line = cmd.partition(" ")
|
||||||
try:
|
try:
|
||||||
parser = self.console._commands[cmd].create_parser()
|
parser = self.console._commands[cmd].create_parser()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -633,6 +610,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
# Do a little hack here to print 'command --help' properly
|
# Do a little hack here to print 'command --help' properly
|
||||||
parser._print_help = parser.print_help
|
parser._print_help = parser.print_help
|
||||||
|
|
||||||
def print_help(f=None):
|
def print_help(f=None):
|
||||||
parser._print_help(f)
|
parser._print_help(f)
|
||||||
parser.print_help = print_help
|
parser.print_help = print_help
|
||||||
|
@ -654,7 +632,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
self.write("{!error!}Error parsing options: %s" % ex)
|
self.write("{!error!}Error parsing options: %s" % ex)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not getattr(options, '_exit', False):
|
if not getattr(options, "_exit", False):
|
||||||
try:
|
try:
|
||||||
ret = self.console._commands[cmd].handle(*args, **options.__dict__)
|
ret = self.console._commands[cmd].handle(*args, **options.__dict__)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -666,8 +644,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
else:
|
else:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_batch_write(self, batch):
|
def set_batch_write(self, batch):
|
||||||
"""
|
"""
|
||||||
When this is set the screen is not refreshed after a `:meth:write` until
|
When this is set the screen is not refreshed after a `:meth:write` until
|
||||||
|
@ -691,7 +667,6 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
self.add_line(line, not self.batch_write)
|
self.add_line(line, not self.batch_write)
|
||||||
|
|
||||||
|
|
||||||
def tab_completer(self, line, cursor, hits):
|
def tab_completer(self, line, cursor, hits):
|
||||||
"""
|
"""
|
||||||
Called when the user hits 'tab' and will autocomplete or show options.
|
Called when the user hits 'tab' and will autocomplete or show options.
|
||||||
|
@ -747,7 +722,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
new_line = format_utils.remove_formatting(new_line)
|
new_line = format_utils.remove_formatting(new_line)
|
||||||
return (new_line, len(new_line))
|
return (new_line, len(new_line))
|
||||||
else:
|
else:
|
||||||
if hits == 1:
|
if hits == 1:
|
||||||
p = " ".join(split(line)[:-1])
|
p = " ".join(split(line)[:-1])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -755,7 +730,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
l_arg = ""
|
l_arg = ""
|
||||||
|
|
||||||
new_line = " ".join( [p, complete_line(l_arg, possible_matches)] ).lstrip()
|
new_line = " ".join([p, complete_line(l_arg, possible_matches)]).lstrip()
|
||||||
|
|
||||||
if len(format_utils.remove_formatting(new_line)) > len(line):
|
if len(format_utils.remove_formatting(new_line)) > len(line):
|
||||||
line = new_line
|
line = new_line
|
||||||
|
@ -763,8 +738,8 @@ class Legacy(BaseMode, component.Component):
|
||||||
elif hits >= 2:
|
elif hits >= 2:
|
||||||
max_list = self.console_config["torrents_per_tab_press"]
|
max_list = self.console_config["torrents_per_tab_press"]
|
||||||
match_count = len(possible_matches)
|
match_count = len(possible_matches)
|
||||||
listed = (hits-2) * max_list
|
listed = (hits - 2) * max_list
|
||||||
pages = (match_count-1) // max_list + 1
|
pages = (match_count - 1) // max_list + 1
|
||||||
left = match_count - listed
|
left = match_count - listed
|
||||||
if hits == 2:
|
if hits == 2:
|
||||||
self.write(" ")
|
self.write(" ")
|
||||||
|
@ -777,7 +752,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
for i in range(listed, listed + max_list):
|
for i in range(listed, listed + max_list):
|
||||||
match = possible_matches[i]
|
match = possible_matches[i]
|
||||||
self.write(match.replace(r"\ ", " "))
|
self.write(match.replace(r"\ ", " "))
|
||||||
self.write("{!error!}And %i more. Press <tab> to list them" % (left - max_list) )
|
self.write("{!error!}And %i more. Press <tab> to list them" % (left - max_list))
|
||||||
else:
|
else:
|
||||||
self.tab_count = 0
|
self.tab_count = 0
|
||||||
for match in possible_matches[listed:]:
|
for match in possible_matches[listed:]:
|
||||||
|
@ -787,13 +762,14 @@ class Legacy(BaseMode, component.Component):
|
||||||
for i in range(listed, listed + max_list):
|
for i in range(listed, listed + max_list):
|
||||||
match = possible_matches[i]
|
match = possible_matches[i]
|
||||||
self.write(match.replace(r"\ ", " "))
|
self.write(match.replace(r"\ ", " "))
|
||||||
self.write("{!error!}And %i more (%i/%i). Press <tab> to view more" % (left - max_list, hits-1, pages) )
|
self.write("{!error!}And %i more (%i/%i). Press <tab> to view more" % (
|
||||||
|
left - max_list, hits - 1, pages))
|
||||||
else:
|
else:
|
||||||
self.tab_count = 0
|
self.tab_count = 0
|
||||||
for match in possible_matches[listed:]:
|
for match in possible_matches[listed:]:
|
||||||
self.write(match.replace(r"\ ", " "))
|
self.write(match.replace(r"\ ", " "))
|
||||||
if hits > 2:
|
if hits > 2:
|
||||||
self.write("{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits-1, pages) )
|
self.write("{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits - 1, pages))
|
||||||
|
|
||||||
#We only want to print eventual colors or other control characters, not return them
|
#We only want to print eventual colors or other control characters, not return them
|
||||||
line = format_utils.remove_formatting(line)
|
line = format_utils.remove_formatting(line)
|
||||||
|
@ -818,9 +794,9 @@ class Legacy(BaseMode, component.Component):
|
||||||
continue
|
continue
|
||||||
f = os.path.join(line, f)
|
f = os.path.join(line, f)
|
||||||
if os.path.isdir(f):
|
if os.path.isdir(f):
|
||||||
if os.sep == '\\': # Windows path support :|
|
if os.sep == "\\": # Windows path support
|
||||||
f += "\\"
|
f += "\\"
|
||||||
else: # \o/ Unix
|
else: # Unix
|
||||||
f += "/"
|
f += "/"
|
||||||
elif not f.endswith(ext):
|
elif not f.endswith(ext):
|
||||||
continue
|
continue
|
||||||
|
@ -833,7 +809,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
# shares a common prefix.
|
# shares a common prefix.
|
||||||
for f in os.listdir(os.path.dirname(line)):
|
for f in os.listdir(os.path.dirname(line)):
|
||||||
if f.startswith(os.path.split(line)[1]):
|
if f.startswith(os.path.split(line)[1]):
|
||||||
ret.append(os.path.join( os.path.dirname(line), f))
|
ret.append(os.path.join(os.path.dirname(line), f))
|
||||||
except OSError:
|
except OSError:
|
||||||
self.console.write("{!error!}Permission denied: {!info!}%s" % line)
|
self.console.write("{!error!}Permission denied: {!info!}%s" % line)
|
||||||
else:
|
else:
|
||||||
|
@ -847,9 +823,9 @@ class Legacy(BaseMode, component.Component):
|
||||||
p = os.path.join(os.path.dirname(line), f)
|
p = os.path.join(os.path.dirname(line), f)
|
||||||
|
|
||||||
if os.path.isdir(p):
|
if os.path.isdir(p):
|
||||||
if os.sep == '\\': # Windows path support :|
|
if os.sep == "\\": # Windows path support
|
||||||
p += "\\"
|
p += "\\"
|
||||||
else: # \o/ Unix
|
else: # Unix
|
||||||
p += "/"
|
p += "/"
|
||||||
ret.append(p)
|
ret.append(p)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -897,7 +873,7 @@ class Legacy(BaseMode, component.Component):
|
||||||
possible_matches = []
|
possible_matches = []
|
||||||
possible_matches2 = []
|
possible_matches2 = []
|
||||||
|
|
||||||
match_count = 0
|
match_count = 0
|
||||||
match_count2 = 0
|
match_count2 = 0
|
||||||
for torrent_id, torrent_name in self.torrents:
|
for torrent_id, torrent_name in self.torrents:
|
||||||
if torrent_id.startswith(line):
|
if torrent_id.startswith(line):
|
||||||
|
@ -909,9 +885,10 @@ class Legacy(BaseMode, component.Component):
|
||||||
|
|
||||||
# Find all possible matches
|
# Find all possible matches
|
||||||
for torrent_id, torrent_name in self.torrents:
|
for torrent_id, torrent_name in self.torrents:
|
||||||
#Escape spaces to avoid, for example, expanding "Doc" into "Doctor Who" and removing everything containing one of these words
|
# Escape spaces to avoid, for example, expanding "Doc" into "Doctor Who" and removing
|
||||||
|
# everything containing one of these words
|
||||||
escaped_name = torrent_name.replace(" ", "\\ ")
|
escaped_name = torrent_name.replace(" ", "\\ ")
|
||||||
#If we only matched one torrent, don't add the full name or it'll also get autocompleted
|
# If we only matched one torrent, don't add the full name or it'll also get autocompleted
|
||||||
if match_count == 1:
|
if match_count == 1:
|
||||||
if torrent_id.startswith(line):
|
if torrent_id.startswith(line):
|
||||||
possible_matches.append(torrent_id)
|
possible_matches.append(torrent_id)
|
||||||
|
@ -932,10 +909,12 @@ class Legacy(BaseMode, component.Component):
|
||||||
text = "{!info!}%s{!input!}%s - '%s'" % (torrent_id[:l], torrent_id[l:], torrent_name)
|
text = "{!info!}%s{!input!}%s - '%s'" % (torrent_id[:l], torrent_id[l:], torrent_name)
|
||||||
possible_matches.append(text)
|
possible_matches.append(text)
|
||||||
if torrent_name.startswith(line):
|
if torrent_name.startswith(line):
|
||||||
text = "{!info!}%s{!input!}%s ({!cyan!}%s{!input!})" % (escaped_name[:l], escaped_name[l:], torrent_id)
|
text = "{!info!}%s{!input!}%s ({!cyan!}%s{!input!})" % (
|
||||||
|
escaped_name[:l], escaped_name[l:], torrent_id)
|
||||||
possible_matches.append(text)
|
possible_matches.append(text)
|
||||||
elif torrent_name.lower().startswith(line.lower()):
|
elif torrent_name.lower().startswith(line.lower()):
|
||||||
text = "{!info!}%s{!input!}%s ({!cyan!}%s{!input!})" % (escaped_name[:l], escaped_name[l:], torrent_id)
|
text = "{!info!}%s{!input!}%s ({!cyan!}%s{!input!})" % (
|
||||||
|
escaped_name[:l], escaped_name[l:], torrent_id)
|
||||||
possible_matches2.append(text)
|
possible_matches2.append(text)
|
||||||
|
|
||||||
return possible_matches + possible_matches2
|
return possible_matches + possible_matches2
|
||||||
|
|
|
@ -1,42 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# popup.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import curses
|
import curses
|
||||||
import signal
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -46,20 +18,23 @@ import format_utils
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ALIGN:
|
class ALIGN:
|
||||||
TOP_LEFT = 1
|
TOP_LEFT = 1
|
||||||
TOP_CENTER = 2
|
TOP_CENTER = 2
|
||||||
TOP_RIGHT = 3
|
TOP_RIGHT = 3
|
||||||
MIDDLE_LEFT = 4
|
MIDDLE_LEFT = 4
|
||||||
MIDDLE_CENTER= 5
|
MIDDLE_CENTER = 5
|
||||||
MIDDLE_RIGHT = 6
|
MIDDLE_RIGHT = 6
|
||||||
BOTTOM_LEFT = 7
|
BOTTOM_LEFT = 7
|
||||||
BOTTOM_CENTER= 8
|
BOTTOM_CENTER = 8
|
||||||
BOTTOM_RIGHT = 9
|
BOTTOM_RIGHT = 9
|
||||||
DEFAULT = MIDDLE_CENTER
|
DEFAULT = MIDDLE_CENTER
|
||||||
|
|
||||||
|
|
||||||
class Popup:
|
class Popup:
|
||||||
def __init__(self,parent_mode,title,width_req=0,height_req=0,align=ALIGN.DEFAULT, close_cb=None,init_lines=None):
|
def __init__(self, parent_mode, title, width_req=0, height_req=0, align=ALIGN.DEFAULT,
|
||||||
|
close_cb=None, init_lines=None):
|
||||||
"""
|
"""
|
||||||
Init a new popup. The default constructor will handle sizing and borders and the like.
|
Init a new popup. The default constructor will handle sizing and borders and the like.
|
||||||
|
|
||||||
|
@ -77,14 +52,14 @@ class Popup:
|
||||||
refresh(self) - draw the popup window to screen. this default mode simply draws a bordered window
|
refresh(self) - draw the popup window to screen. this default mode simply draws a bordered window
|
||||||
with the supplied title to the screen
|
with the supplied title to the screen
|
||||||
|
|
||||||
add_string(self, row, string) - add string at row. handles triming/ignoring if the string won't fit in the popup
|
add_string(self, row, string) - add string at row. handles triming/ignoring if the string won't fit in the popup
|
||||||
|
|
||||||
_doRead(self) - handle user input to the popup.
|
_doRead(self) - handle user input to the popup.
|
||||||
"""
|
"""
|
||||||
self.parent = parent_mode
|
self.parent = parent_mode
|
||||||
|
|
||||||
self.height_req = height_req
|
self.height_req = height_req
|
||||||
self.width_req = width_req
|
self.width_req = width_req
|
||||||
self.align = align
|
self.align = align
|
||||||
|
|
||||||
self.handle_resize()
|
self.handle_resize()
|
||||||
|
@ -102,19 +77,19 @@ class Popup:
|
||||||
def _refresh_lines(self):
|
def _refresh_lines(self):
|
||||||
crow = 1
|
crow = 1
|
||||||
for line in self._lines[self.lineoff:]:
|
for line in self._lines[self.lineoff:]:
|
||||||
if (crow >= self.height-1):
|
if (crow >= self.height - 1):
|
||||||
break
|
break
|
||||||
self.parent.add_string(crow, line, self.screen, 1, False, True)
|
self.parent.add_string(crow, line, self.screen, 1, False, True)
|
||||||
crow+=1
|
crow += 1
|
||||||
|
|
||||||
def handle_resize(self):
|
def handle_resize(self):
|
||||||
if isinstance(self.height_req, float) and 0.0 < self.height_req <= 1.0:
|
if isinstance(self.height_req, float) and 0.0 < self.height_req <= 1.0:
|
||||||
hr = int( (self.parent.rows - 2) * self.height_req )
|
hr = int((self.parent.rows - 2) * self.height_req)
|
||||||
else:
|
else:
|
||||||
hr = self.height_req
|
hr = self.height_req
|
||||||
|
|
||||||
if isinstance(self.width_req, float) and 0.0 < self.width_req <= 1.0:
|
if isinstance(self.width_req, float) and 0.0 < self.width_req <= 1.0:
|
||||||
wr = int( (self.parent.cols - 2) * self.width_req )
|
wr = int((self.parent.cols - 2) * self.width_req)
|
||||||
else:
|
else:
|
||||||
wr = self.width_req
|
wr = self.width_req
|
||||||
|
|
||||||
|
@ -122,31 +97,31 @@ class Popup:
|
||||||
|
|
||||||
#Height
|
#Height
|
||||||
if hr == 0:
|
if hr == 0:
|
||||||
hr = int(self.parent.rows/2)
|
hr = int(self.parent.rows / 2)
|
||||||
elif hr == -1:
|
elif hr == -1:
|
||||||
hr = self.parent.rows - 2
|
hr = self.parent.rows - 2
|
||||||
elif hr > self.parent.rows - 2:
|
elif hr > self.parent.rows - 2:
|
||||||
hr = self.parent.rows - 2
|
hr = self.parent.rows - 2
|
||||||
|
|
||||||
#Width
|
#Width
|
||||||
if wr == 0:
|
if wr == 0:
|
||||||
wr = int(self.parent.cols/2)
|
wr = int(self.parent.cols / 2)
|
||||||
elif wr == -1:
|
elif wr == -1:
|
||||||
wr = self.parent.cols
|
wr = self.parent.cols
|
||||||
elif wr >= self.parent.cols:
|
elif wr >= self.parent.cols:
|
||||||
wr = self.parent.cols
|
wr = self.parent.cols
|
||||||
|
|
||||||
if self.align in [ALIGN.TOP_CENTER, ALIGN.TOP_LEFT, ALIGN.TOP_RIGHT]:
|
if self.align in [ALIGN.TOP_CENTER, ALIGN.TOP_LEFT, ALIGN.TOP_RIGHT]:
|
||||||
by = 1
|
by = 1
|
||||||
elif self.align in [ALIGN.MIDDLE_CENTER, ALIGN.MIDDLE_LEFT, ALIGN.MIDDLE_RIGHT]:
|
elif self.align in [ALIGN.MIDDLE_CENTER, ALIGN.MIDDLE_LEFT, ALIGN.MIDDLE_RIGHT]:
|
||||||
by = (self.parent.rows/2)-(hr/2)
|
by = (self.parent.rows / 2) - (hr / 2)
|
||||||
elif self.align in [ALIGN.BOTTOM_CENTER, ALIGN.BOTTOM_LEFT, ALIGN.BOTTOM_RIGHT]:
|
elif self.align in [ALIGN.BOTTOM_CENTER, ALIGN.BOTTOM_LEFT, ALIGN.BOTTOM_RIGHT]:
|
||||||
by = self.parent.rows - hr - 1
|
by = self.parent.rows - hr - 1
|
||||||
|
|
||||||
if self.align in [ALIGN.TOP_LEFT, ALIGN.MIDDLE_LEFT, ALIGN.BOTTOM_LEFT]:
|
if self.align in [ALIGN.TOP_LEFT, ALIGN.MIDDLE_LEFT, ALIGN.BOTTOM_LEFT]:
|
||||||
bx = 0
|
bx = 0
|
||||||
elif self.align in [ALIGN.TOP_CENTER, ALIGN.MIDDLE_CENTER, ALIGN.BOTTOM_CENTER]:
|
elif self.align in [ALIGN.TOP_CENTER, ALIGN.MIDDLE_CENTER, ALIGN.BOTTOM_CENTER]:
|
||||||
bx = (self.parent.cols/2)-(wr/2)
|
bx = (self.parent.cols / 2) - (wr / 2)
|
||||||
elif self.align in [ALIGN.TOP_RIGHT, ALIGN.MIDDLE_RIGHT, ALIGN.BOTTOM_RIGHT]:
|
elif self.align in [ALIGN.TOP_RIGHT, ALIGN.MIDDLE_RIGHT, ALIGN.BOTTOM_RIGHT]:
|
||||||
bx = self.parent.cols - wr - 1
|
bx = self.parent.cols - wr - 1
|
||||||
|
|
||||||
|
@ -155,21 +130,21 @@ class Popup:
|
||||||
self.x, self.y = bx, by
|
self.x, self.y = bx, by
|
||||||
self.height, self.width = self.screen.getmaxyx()
|
self.height, self.width = self.screen.getmaxyx()
|
||||||
|
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.screen.erase()
|
self.screen.erase()
|
||||||
self.screen.border(0, 0, 0, 0)
|
self.screen.border(0, 0, 0, 0)
|
||||||
toff = max(1, (self.width//2) - (len(self.title)//2))
|
toff = max(1, (self.width // 2) - (len(self.title) // 2))
|
||||||
self.parent.add_string(0, "{!white,black,bold!}%s"%self.title, self.screen, toff, False, True)
|
self.parent.add_string(0, "{!white,black,bold!}%s" % self.title, self.screen, toff, False, True)
|
||||||
|
|
||||||
self._refresh_lines()
|
self._refresh_lines()
|
||||||
if (len(self._lines) > (self.height-2)):
|
if (len(self._lines) > (self.height - 2)):
|
||||||
lts = len(self._lines)-(self.height-3)
|
lts = len(self._lines) - (self.height - 3)
|
||||||
perc_sc = float(self.lineoff)/lts
|
perc_sc = float(self.lineoff) / lts
|
||||||
sb_pos = int((self.height-2)*perc_sc)+1
|
sb_pos = int((self.height - 2) * perc_sc) + 1
|
||||||
if (sb_pos == 1) and (self.lineoff != 0):
|
if (sb_pos == 1) and (self.lineoff != 0):
|
||||||
sb_pos += 1
|
sb_pos += 1
|
||||||
self.parent.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width-1), pad=False, trim=False)
|
self.parent.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width - 1),
|
||||||
|
pad=False, trim=False)
|
||||||
|
|
||||||
self.screen.redrawwin()
|
self.screen.redrawwin()
|
||||||
self.screen.noutrefresh()
|
self.screen.noutrefresh()
|
||||||
|
@ -180,28 +155,28 @@ class Popup:
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
p_off = self.height - 3
|
p_off = self.height - 3
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
self.lineoff = max(0, self.lineoff -1)
|
self.lineoff = max(0, self.lineoff - 1)
|
||||||
elif c == curses.KEY_PPAGE:
|
elif c == curses.KEY_PPAGE:
|
||||||
self.lineoff = max(0, self.lineoff - p_off)
|
self.lineoff = max(0, self.lineoff - p_off)
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
self.lineoff = 0
|
self.lineoff = 0
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
if len(self._lines)-self.lineoff > (self.height-2):
|
if len(self._lines) - self.lineoff > (self.height - 2):
|
||||||
self.lineoff += 1
|
self.lineoff += 1
|
||||||
elif c == curses.KEY_NPAGE:
|
elif c == curses.KEY_NPAGE:
|
||||||
self.lineoff = min(len(self._lines) - self.height+2, self.lineoff + p_off)
|
self.lineoff = min(len(self._lines) - self.height + 2, self.lineoff + p_off)
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
self.lineoff = len(self._lines) - self.height+2
|
self.lineoff = len(self._lines) - self.height + 2
|
||||||
|
|
||||||
elif c == curses.KEY_ENTER or c == 10 or c == 27: # close on enter/esc
|
elif c == curses.KEY_ENTER or c == 10 or c == 27: # close on enter/esc
|
||||||
if self.close_cb:
|
if self.close_cb:
|
||||||
self.close_cb()
|
self.close_cb()
|
||||||
return True # close the popup
|
return True # close the popup
|
||||||
|
|
||||||
if c > 31 and c < 256 and chr(c) == 'q':
|
if c > 31 and c < 256 and chr(c) == "q":
|
||||||
if self.close_cb:
|
if self.close_cb:
|
||||||
self.close_cb()
|
self.close_cb()
|
||||||
return True # close the popup
|
return True # close the popup
|
||||||
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
@ -215,7 +190,7 @@ class Popup:
|
||||||
|
|
||||||
def add_divider(self):
|
def add_divider(self):
|
||||||
if not self.divider:
|
if not self.divider:
|
||||||
self.divider = "-"*(self.width-2)
|
self.divider = "-" * (self.width - 2)
|
||||||
self._lines.append(self.divider)
|
self._lines.append(self.divider)
|
||||||
|
|
||||||
|
|
||||||
|
@ -224,7 +199,7 @@ class SelectablePopup(Popup):
|
||||||
A popup which will let the user select from some of the lines that
|
A popup which will let the user select from some of the lines that
|
||||||
are added.
|
are added.
|
||||||
"""
|
"""
|
||||||
def __init__(self,parent_mode,title, selection_callback, args=(), align=ALIGN.DEFAULT, immediate_action=False):
|
def __init__(self, parent_mode, title, selection_callback, args=(), align=ALIGN.DEFAULT, immediate_action=False):
|
||||||
Popup.__init__(self, parent_mode, title, align=align)
|
Popup.__init__(self, parent_mode, title, align=align)
|
||||||
self._selection_callback = selection_callback
|
self._selection_callback = selection_callback
|
||||||
self._selection_args = args
|
self._selection_args = args
|
||||||
|
@ -240,63 +215,67 @@ class SelectablePopup(Popup):
|
||||||
|
|
||||||
def add_line(self, string, selectable=True, use_underline=True, data=None, foreground=None):
|
def add_line(self, string, selectable=True, use_underline=True, data=None, foreground=None):
|
||||||
if use_underline:
|
if use_underline:
|
||||||
udx = string.find('_')
|
udx = string.find("_")
|
||||||
if udx >= 0:
|
if udx >= 0:
|
||||||
string = string[:udx]+string[udx+1:]
|
string = string[:udx] + string[udx + 1:]
|
||||||
self._udxs[len(self._lines)+1] = udx
|
self._udxs[len(self._lines) + 1] = udx
|
||||||
c = string[udx].lower()
|
c = string[udx].lower()
|
||||||
self._hotkeys[c] = len(self._lines)
|
self._hotkeys[c] = len(self._lines)
|
||||||
Popup.add_line(self, string)
|
Popup.add_line(self, string)
|
||||||
self._line_foregrounds.append(foreground)
|
self._line_foregrounds.append(foreground)
|
||||||
if selectable:
|
if selectable:
|
||||||
self._selectable_lines.append(len(self._lines)-1)
|
self._selectable_lines.append(len(self._lines) - 1)
|
||||||
self._select_data.append(data)
|
self._select_data.append(data)
|
||||||
if self._selected < 0:
|
if self._selected < 0:
|
||||||
self._selected = (len(self._lines)-1)
|
self._selected = (len(self._lines) - 1)
|
||||||
|
|
||||||
def _refresh_lines(self):
|
def _refresh_lines(self):
|
||||||
crow = 1
|
crow = 1
|
||||||
for row, line in enumerate(self._lines):
|
for row, line in enumerate(self._lines):
|
||||||
if (crow >= self.height-1):
|
if (crow >= self.height - 1):
|
||||||
break
|
break
|
||||||
if (row < self.lineoff):
|
if (row < self.lineoff):
|
||||||
continue
|
continue
|
||||||
fg = self._line_foregrounds[row]
|
fg = self._line_foregrounds[row]
|
||||||
udx = self._udxs.get(crow)
|
udx = self._udxs.get(crow)
|
||||||
if row == self._selected:
|
if row == self._selected:
|
||||||
if fg == None: fg = "black"
|
if fg is None:
|
||||||
colorstr = "{!%s,white,bold!}"%fg
|
fg = "black"
|
||||||
|
colorstr = "{!%s,white,bold!}" % fg
|
||||||
if udx >= 0:
|
if udx >= 0:
|
||||||
ustr = "{!%s,white,bold,underline!}"%fg
|
ustr = "{!%s,white,bold,underline!}" % fg
|
||||||
else:
|
else:
|
||||||
if fg == None: fg = "white"
|
if fg is None:
|
||||||
colorstr = "{!%s,black!}"%fg
|
fg = "white"
|
||||||
|
colorstr = "{!%s,black!}" % fg
|
||||||
if udx >= 0:
|
if udx >= 0:
|
||||||
ustr = "{!%s,black,underline!}"%fg
|
ustr = "{!%s,black,underline!}" % fg
|
||||||
if udx == 0:
|
if udx == 0:
|
||||||
self.parent.add_string(crow, "- %s%c%s%s"%(ustr, line[0], colorstr, line[1:]), self.screen, 1, False, True)
|
self.parent.add_string(crow, "- %s%c%s%s" % (
|
||||||
|
ustr, line[0], colorstr, line[1:]), self.screen, 1, False, True)
|
||||||
elif udx > 0:
|
elif udx > 0:
|
||||||
# well, this is a litte gross
|
# well, this is a litte gross
|
||||||
self.parent.add_string(crow, "- %s%s%s%c%s%s"%(colorstr, line[:udx], ustr, line[udx], colorstr, line[udx+1:]), self.screen, 1, False, True)
|
self.parent.add_string(crow, "- %s%s%s%c%s%s" % (
|
||||||
|
colorstr, line[:udx], ustr, line[udx], colorstr, line[udx + 1:]), self.screen, 1, False, True)
|
||||||
else:
|
else:
|
||||||
self.parent.add_string(crow, "- %s%s"%(colorstr, line), self.screen, 1, False, True)
|
self.parent.add_string(crow, "- %s%s" % (colorstr, line), self.screen, 1, False, True)
|
||||||
crow+=1
|
crow += 1
|
||||||
|
|
||||||
def current_selection(self):
|
def current_selection(self):
|
||||||
"Returns a tuple of (selected index, selected data)"
|
"Returns a tuple of (selected index, selected data)"
|
||||||
idx = self._selectable_lines.index(self._selected)
|
idx = self._selectable_lines.index(self._selected)
|
||||||
return (idx, self._select_data[idx])
|
return (idx, self._select_data[idx])
|
||||||
|
|
||||||
def add_divider(self,color="white"):
|
def add_divider(self, color="white"):
|
||||||
if not self.divider:
|
if not self.divider:
|
||||||
self.divider = "-"*(self.width-6)+" -"
|
self.divider = "-" * (self.width - 6) + " -"
|
||||||
self._lines.append(self.divider)
|
self._lines.append(self.divider)
|
||||||
self._line_foregrounds.append(color)
|
self._line_foregrounds.append(color)
|
||||||
|
|
||||||
def _move_cursor_up(self, amount):
|
def _move_cursor_up(self, amount):
|
||||||
if self._selectable_lines.index(self._selected) > amount:
|
if self._selectable_lines.index(self._selected) > amount:
|
||||||
idx = self._selectable_lines.index(self._selected)
|
idx = self._selectable_lines.index(self._selected)
|
||||||
self._selected = self._selectable_lines[idx-amount]
|
self._selected = self._selectable_lines[idx - amount]
|
||||||
else:
|
else:
|
||||||
self._selected = self._selectable_lines[0]
|
self._selected = self._selectable_lines[0]
|
||||||
|
|
||||||
|
@ -306,7 +285,7 @@ class SelectablePopup(Popup):
|
||||||
def _move_cursor_down(self, amount):
|
def _move_cursor_down(self, amount):
|
||||||
idx = self._selectable_lines.index(self._selected)
|
idx = self._selectable_lines.index(self._selected)
|
||||||
if (idx < len(self._selectable_lines) - amount):
|
if (idx < len(self._selectable_lines) - amount):
|
||||||
self._selected = self._selectable_lines[idx+amount]
|
self._selected = self._selectable_lines[idx + amount]
|
||||||
else:
|
else:
|
||||||
self._selected = self._selectable_lines[-1]
|
self._selected = self._selectable_lines[-1]
|
||||||
|
|
||||||
|
@ -329,7 +308,7 @@ class SelectablePopup(Popup):
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
self._move_cursor_down(len(self._selectable_lines))
|
self._move_cursor_down(len(self._selectable_lines))
|
||||||
|
|
||||||
elif c == 27: # close on esc, no action
|
elif c == 27: # close on esc, no action
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif c == curses.KEY_ENTER or c == 10:
|
elif c == curses.KEY_ENTER or c == 10:
|
||||||
|
@ -337,13 +316,13 @@ class SelectablePopup(Popup):
|
||||||
return self._selection_callback(idx, self._select_data[idx], *self._selection_args)
|
return self._selection_callback(idx, self._select_data[idx], *self._selection_args)
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'q':
|
if chr(c) == "q":
|
||||||
return True # close the popup
|
return True # close the popup
|
||||||
uc = chr(c).lower()
|
uc = chr(c).lower()
|
||||||
if uc in self._hotkeys:
|
if uc in self._hotkeys:
|
||||||
# exec hotkey action
|
# exec hotkey action
|
||||||
idx = self._selectable_lines.index(self._hotkeys[uc])
|
idx = self._selectable_lines.index(self._hotkeys[uc])
|
||||||
return self._selection_callback(idx,self._select_data[idx],*self._selection_args)
|
return self._selection_callback(idx, self._select_data[idx], *self._selection_args)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -357,12 +336,12 @@ class MessagePopup(Popup):
|
||||||
self.message = message
|
self.message = message
|
||||||
#self.width= int(parent_mode.cols/2)
|
#self.width= int(parent_mode.cols/2)
|
||||||
Popup.__init__(self, parent_mode, title, align=align, width_req=width_req)
|
Popup.__init__(self, parent_mode, title, align=align, width_req=width_req)
|
||||||
lns = format_utils.wrap_string(self.message, self.width-2, 3, True)
|
lns = format_utils.wrap_string(self.message, self.width - 2, 3, True)
|
||||||
self.height_req = min(len(lns)+2, int(parent_mode.rows*2/3))
|
self.height_req = min(len(lns) + 2, int(parent_mode.rows * 2 / 3))
|
||||||
self.handle_resize()
|
self.handle_resize()
|
||||||
self._lines = lns
|
self._lines = lns
|
||||||
|
|
||||||
def handle_resize(self):
|
def handle_resize(self):
|
||||||
Popup.handle_resize(self)
|
Popup.handle_resize(self)
|
||||||
self.clear()
|
self.clear()
|
||||||
self._lines = format_utils.wrap_string(self.message, self.width-2, 3, True)
|
self._lines = format_utils.wrap_string(self.message, self.width - 2, 3, True)
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# preference_panes.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -52,10 +26,11 @@ class NoInput:
|
||||||
def depend_skip(self):
|
def depend_skip(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Header(NoInput):
|
class Header(NoInput):
|
||||||
def __init__(self, parent, header, space_above, space_below):
|
def __init__(self, parent, header, space_above, space_below):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.header = "{!white,black,bold!}%s"%header
|
self.header = "{!white,black,bold!}%s" % header
|
||||||
self.space_above = space_above
|
self.space_above = space_above
|
||||||
self.space_below = space_below
|
self.space_below = space_below
|
||||||
self.name = header
|
self.name = header
|
||||||
|
@ -65,32 +40,35 @@ class Header(NoInput):
|
||||||
if self.space_above:
|
if self.space_above:
|
||||||
row += 1
|
row += 1
|
||||||
rows += 1
|
rows += 1
|
||||||
self.parent.add_string(row, self.header, screen, offset-1, False, True)
|
self.parent.add_string(row, self.header, screen, offset - 1, False, True)
|
||||||
if self.space_below: rows += 1
|
if self.space_below:
|
||||||
|
rows += 1
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
class InfoField(NoInput):
|
class InfoField(NoInput):
|
||||||
def __init__(self, parent, label, value, name):
|
def __init__(self, parent, label, value, name):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.label = label
|
self.label = label
|
||||||
self.value = value
|
self.value = value
|
||||||
self.txt = "%s %s"%(label, value)
|
self.txt = "%s %s" % (label, value)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def render(self, screen, row, width, active, offset):
|
def render(self, screen, row, width, active, offset):
|
||||||
self.parent.add_string(row, self.txt, screen, offset-1, False, True)
|
self.parent.add_string(row, self.txt, screen, offset - 1, False, True)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def set_value(self, v):
|
def set_value(self, v):
|
||||||
self.value = v
|
self.value = v
|
||||||
if type(v) == float:
|
if type(v) == float:
|
||||||
self.txt = "%s %.2f"%(self.label, self.value)
|
self.txt = "%s %.2f" % (self.label, self.value)
|
||||||
else:
|
else:
|
||||||
self.txt = "%s %s"%(self.label, self.value)
|
self.txt = "%s %s" % (self.label, self.value)
|
||||||
|
|
||||||
|
|
||||||
class BasePane:
|
class BasePane:
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
self.offset = offset+1
|
self.offset = offset + 1
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.width = width
|
self.width = width
|
||||||
self.inputs = []
|
self.inputs = []
|
||||||
|
@ -137,8 +115,6 @@ class BasePane:
|
||||||
conf_dict.setdefault("proxy", {})["proxy_hostnames"] = ipt.get_value()
|
conf_dict.setdefault("proxy", {})["proxy_hostnames"] = ipt.get_value()
|
||||||
elif ipt.name == "proxy_peer_connections":
|
elif ipt.name == "proxy_peer_connections":
|
||||||
conf_dict.setdefault("proxy", {})["proxy_peer_connections"] = ipt.get_value()
|
conf_dict.setdefault("proxy", {})["proxy_peer_connections"] = ipt.get_value()
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
conf_dict[ipt.name] = ipt.get_value()
|
conf_dict[ipt.name] = ipt.get_value()
|
||||||
if hasattr(ipt, "get_child"):
|
if hasattr(ipt, "get_child"):
|
||||||
|
@ -150,13 +126,13 @@ class BasePane:
|
||||||
if not isinstance(ipt, NoInput):
|
if not isinstance(ipt, NoInput):
|
||||||
try:
|
try:
|
||||||
ipt.set_value(conf_dict[ipt.name])
|
ipt.set_value(conf_dict[ipt.name])
|
||||||
except KeyError: # just ignore if it's not in dict
|
except KeyError: # just ignore if it's not in dict
|
||||||
pass
|
pass
|
||||||
if hasattr(ipt, "get_child"):
|
if hasattr(ipt, "get_child"):
|
||||||
try:
|
try:
|
||||||
c = ipt.get_child()
|
c = ipt.get_child()
|
||||||
c.set_value(conf_dict[c.name])
|
c.set_value(conf_dict[c.name])
|
||||||
except KeyError: # just ignore if it's not in dict
|
except KeyError: # just ignore if it's not in dict
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def render(self, mode, screen, width, active):
|
def render(self, mode, screen, width, active):
|
||||||
|
@ -169,26 +145,27 @@ class BasePane:
|
||||||
drew_act = not active
|
drew_act = not active
|
||||||
crow = 1
|
crow = 1
|
||||||
for i, ipt in enumerate(self.inputs):
|
for i, ipt in enumerate(self.inputs):
|
||||||
if ipt.depend_skip() or i<self.input_offset:
|
if ipt.depend_skip() or i < self.input_offset:
|
||||||
if active and i==self.active_input:
|
if active and i == self.active_input:
|
||||||
self.input_offset-=1
|
self.input_offset -= 1
|
||||||
mode.refresh()
|
mode.refresh()
|
||||||
return 0
|
return 0
|
||||||
continue
|
continue
|
||||||
act = active and i==self.active_input
|
act = active and i == self.active_input
|
||||||
if act: drew_act = True
|
if act:
|
||||||
|
drew_act = True
|
||||||
crow += ipt.render(screen, crow, width, act, self.offset)
|
crow += ipt.render(screen, crow, width, act, self.offset)
|
||||||
if crow >= (mode.prefs_height):
|
if crow >= (mode.prefs_height):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not drew_act:
|
if not drew_act:
|
||||||
self.input_offset+=1
|
self.input_offset += 1
|
||||||
mode.refresh()
|
mode.refresh()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if active and self._cursor_row >= 0:
|
if active and self._cursor_row >= 0:
|
||||||
curses.curs_set(2)
|
curses.curs_set(2)
|
||||||
screen.move(self._cursor_row, self._cursor_col+self.offset-1)
|
screen.move(self._cursor_row, self._cursor_col + self.offset - 1)
|
||||||
else:
|
else:
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
|
|
||||||
|
@ -196,30 +173,30 @@ class BasePane:
|
||||||
|
|
||||||
# just handles setting the active input
|
# just handles setting the active input
|
||||||
def handle_read(self, c):
|
def handle_read(self, c):
|
||||||
if not self.inputs: # no inputs added yet
|
if not self.inputs: # no inputs added yet
|
||||||
return
|
return
|
||||||
|
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
nc = max(0, self.active_input-1)
|
nc = max(0, self.active_input - 1)
|
||||||
while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
||||||
nc-=1
|
nc -= 1
|
||||||
if nc <= 0: break
|
if nc <= 0:
|
||||||
|
break
|
||||||
if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
||||||
self.active_input = nc
|
self.active_input = nc
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
ilen = len(self.inputs)
|
ilen = len(self.inputs)
|
||||||
nc = min(self.active_input+1, ilen-1)
|
nc = min(self.active_input + 1, ilen - 1)
|
||||||
while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
||||||
nc+=1
|
nc += 1
|
||||||
if nc >= ilen:
|
if nc >= ilen:
|
||||||
nc-=1
|
nc -= 1
|
||||||
break
|
break
|
||||||
if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
|
||||||
self.active_input = nc
|
self.active_input = nc
|
||||||
else:
|
else:
|
||||||
self.inputs[self.active_input].handle_read(c)
|
self.inputs[self.active_input].handle_read(c)
|
||||||
|
|
||||||
|
|
||||||
def add_header(self, header, space_above=False, space_below=False):
|
def add_header(self, header, space_above=False, space_below=False):
|
||||||
self.inputs.append(Header(self.parent, header, space_above, space_below))
|
self.inputs.append(Header(self.parent, header, space_above, space_below))
|
||||||
|
|
||||||
|
@ -242,7 +219,8 @@ class BasePane:
|
||||||
self.inputs.append(IntSpinInput(self.parent, message, name, self.move, value, min_val, max_val))
|
self.inputs.append(IntSpinInput(self.parent, message, name, self.move, value, min_val, max_val))
|
||||||
|
|
||||||
def add_float_spin_input(self, name, message, value, inc_amt, precision, min_val, max_val):
|
def add_float_spin_input(self, name, message, value, inc_amt, precision, min_val, max_val):
|
||||||
self.inputs.append(FloatSpinInput(self.parent, message, name, self.move, value, inc_amt, precision, min_val, max_val))
|
self.inputs.append(FloatSpinInput(self.parent, message, name, self.move, value,
|
||||||
|
inc_amt, precision, min_val, max_val))
|
||||||
|
|
||||||
|
|
||||||
class InterfacePane(BasePane):
|
class InterfacePane(BasePane):
|
||||||
|
@ -250,22 +228,30 @@ class InterfacePane(BasePane):
|
||||||
BasePane.__init__(self, offset, parent, width)
|
BasePane.__init__(self, offset, parent, width)
|
||||||
self.add_header("General options", False)
|
self.add_header("General options", False)
|
||||||
|
|
||||||
self.add_checked_input("ring_bell", "Ring system bell when a download finishes", parent.console_config["ring_bell"])
|
self.add_checked_input("ring_bell", "Ring system bell when a download finishes",
|
||||||
|
parent.console_config["ring_bell"])
|
||||||
|
|
||||||
self.add_header("New Console UI", True)
|
self.add_header("New Console UI", True)
|
||||||
|
|
||||||
self.add_checked_input("separate_complete", "List complete torrents after incomplete regardless of sorting order", parent.console_config["separate_complete"])
|
self.add_checked_input("separate_complete",
|
||||||
self.add_checked_input("move_selection", "Move selection when moving torrents in the queue", parent.console_config["move_selection"])
|
"List complete torrents after incomplete regardless of sorting order",
|
||||||
|
parent.console_config["separate_complete"])
|
||||||
|
self.add_checked_input("move_selection", "Move selection when moving torrents in the queue",
|
||||||
|
parent.console_config["move_selection"])
|
||||||
|
|
||||||
self.add_header("Legacy Mode", True)
|
self.add_header("Legacy Mode", True)
|
||||||
|
|
||||||
self.add_checked_input("ignore_duplicate_lines", "Do not store duplicate input in history", parent.console_config["ignore_duplicate_lines"])
|
self.add_checked_input("ignore_duplicate_lines", "Do not store duplicate input in history",
|
||||||
self.add_checked_input("save_legacy_history", "Store and load command line history in Legacy mode", parent.console_config["save_legacy_history"])
|
parent.console_config["ignore_duplicate_lines"])
|
||||||
|
self.add_checked_input("save_legacy_history", "Store and load command line history in Legacy mode",
|
||||||
|
parent.console_config["save_legacy_history"])
|
||||||
|
|
||||||
self.add_header("", False)
|
self.add_header("", False)
|
||||||
|
|
||||||
self.add_checked_input("third_tab_lists_all", "Third tab lists all remaining torrents in legacy mode", parent.console_config["third_tab_lists_all"])
|
self.add_checked_input("third_tab_lists_all", "Third tab lists all remaining torrents in legacy mode",
|
||||||
self.add_int_spin_input("torrents_per_tab_press", "Torrents per tab press", parent.console_config["torrents_per_tab_press"], 5, 100)
|
parent.console_config["third_tab_lists_all"])
|
||||||
|
self.add_int_spin_input("torrents_per_tab_press", "Torrents per tab press",
|
||||||
|
parent.console_config["torrents_per_tab_press"], 5, 100)
|
||||||
|
|
||||||
|
|
||||||
class ColumnsPane(BasePane):
|
class ColumnsPane(BasePane):
|
||||||
|
@ -276,7 +262,7 @@ class ColumnsPane(BasePane):
|
||||||
default_prefs = deluge.ui.console.modes.alltorrents.DEFAULT_PREFS
|
default_prefs = deluge.ui.console.modes.alltorrents.DEFAULT_PREFS
|
||||||
|
|
||||||
for cpn in deluge.ui.console.modes.alltorrents.column_pref_names:
|
for cpn in deluge.ui.console.modes.alltorrents.column_pref_names:
|
||||||
pn = "show_%s"%cpn
|
pn = "show_%s" % cpn
|
||||||
#If there is no option for it, it's not togglable
|
#If there is no option for it, it's not togglable
|
||||||
# We check in default_prefs because it might still exist in config files
|
# We check in default_prefs because it might still exist in config files
|
||||||
if pn not in default_prefs:
|
if pn not in default_prefs:
|
||||||
|
@ -286,7 +272,7 @@ class ColumnsPane(BasePane):
|
||||||
parent.console_config[pn])
|
parent.console_config[pn])
|
||||||
self.add_header("Column Widths (-1 = expand)", True)
|
self.add_header("Column Widths (-1 = expand)", True)
|
||||||
for cpn in deluge.ui.console.modes.alltorrents.column_pref_names:
|
for cpn in deluge.ui.console.modes.alltorrents.column_pref_names:
|
||||||
pn = "%s_width"%cpn
|
pn = "%s_width" % cpn
|
||||||
if pn not in default_prefs:
|
if pn not in default_prefs:
|
||||||
continue
|
continue
|
||||||
self.add_int_spin_input(pn,
|
self.add_int_spin_input(pn,
|
||||||
|
@ -300,11 +286,15 @@ class DownloadsPane(BasePane):
|
||||||
|
|
||||||
self.add_header("Folders")
|
self.add_header("Folders")
|
||||||
self.add_text_input("download_location", "Download To:", parent.core_config["download_location"])
|
self.add_text_input("download_location", "Download To:", parent.core_config["download_location"])
|
||||||
cmptxt = TextInput(self.parent, self.move, self.width, None, "move_completed_path", parent.core_config["move_completed_path"], False)
|
cmptxt = TextInput(self.parent, self.move, self.width, None, "move_completed_path",
|
||||||
|
parent.core_config["move_completed_path"], False)
|
||||||
self.add_checkedplus_input("move_completed", "Move completed to:", cmptxt, parent.core_config["move_completed"])
|
self.add_checkedplus_input("move_completed", "Move completed to:", cmptxt, parent.core_config["move_completed"])
|
||||||
copytxt = TextInput(self.parent, self.move, self.width, None, "torrentfiles_location", parent.core_config["torrentfiles_location"], False)
|
copytxt = TextInput(self.parent, self.move, self.width, None, "torrentfiles_location",
|
||||||
self.add_checkedplus_input("copy_torrent_file", "Copy of .torrent files to:", copytxt, parent.core_config["copy_torrent_file"])
|
parent.core_config["torrentfiles_location"], False)
|
||||||
self.add_checked_input("del_copy_torrent_file", "Delete copy of torrent file on remove", parent.core_config["del_copy_torrent_file"])
|
self.add_checkedplus_input("copy_torrent_file", "Copy of .torrent files to:", copytxt,
|
||||||
|
parent.core_config["copy_torrent_file"])
|
||||||
|
self.add_checked_input("del_copy_torrent_file", "Delete copy of torrent file on remove",
|
||||||
|
parent.core_config["del_copy_torrent_file"])
|
||||||
|
|
||||||
self.add_header("Options", True)
|
self.add_header("Options", True)
|
||||||
self.add_checked_input("prioritize_first_last_pieces", "Prioritize first and last pieces of torrent",
|
self.add_checked_input("prioritize_first_last_pieces", "Prioritize first and last pieces of torrent",
|
||||||
|
@ -320,7 +310,8 @@ class NetworkPane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
BasePane.__init__(self, offset, parent, width)
|
BasePane.__init__(self, offset, parent, width)
|
||||||
self.add_header("Incomming Ports")
|
self.add_header("Incomming Ports")
|
||||||
inrand = CheckedInput(parent, "Use Random Ports Active Port: %d"%parent.active_port, "random_port", parent.core_config["random_port"])
|
inrand = CheckedInput(parent, "Use Random Ports Active Port: %d" % parent.active_port,
|
||||||
|
"random_port", parent.core_config["random_port"])
|
||||||
self.inputs.append(inrand)
|
self.inputs.append(inrand)
|
||||||
listen_ports = parent.core_config["listen_ports"]
|
listen_ports = parent.core_config["listen_ports"]
|
||||||
self.infrom = IntSpinInput(self.parent, " From:", "listen_ports_from", self.move, listen_ports[0], 0, 65535)
|
self.infrom = IntSpinInput(self.parent, " From:", "listen_ports_from", self.move, listen_ports[0], 0, 65535)
|
||||||
|
@ -330,9 +321,9 @@ class NetworkPane(BasePane):
|
||||||
self.inputs.append(self.infrom)
|
self.inputs.append(self.infrom)
|
||||||
self.inputs.append(self.into)
|
self.inputs.append(self.into)
|
||||||
|
|
||||||
|
|
||||||
self.add_header("Outgoing Ports", True)
|
self.add_header("Outgoing Ports", True)
|
||||||
outrand = CheckedInput(parent, "Use Random Ports", "random_outgoing_ports", parent.core_config["random_outgoing_ports"])
|
outrand = CheckedInput(parent, "Use Random Ports", "random_outgoing_ports",
|
||||||
|
parent.core_config["random_outgoing_ports"])
|
||||||
self.inputs.append(outrand)
|
self.inputs.append(outrand)
|
||||||
out_ports = parent.core_config["outgoing_ports"]
|
out_ports = parent.core_config["outgoing_ports"]
|
||||||
self.outfrom = IntSpinInput(self.parent, " From:", "out_ports_from", self.move, out_ports[0], 0, 65535)
|
self.outfrom = IntSpinInput(self.parent, " From:", "out_ports_from", self.move, out_ports[0], 0, 65535)
|
||||||
|
@ -342,9 +333,9 @@ class NetworkPane(BasePane):
|
||||||
self.inputs.append(self.outfrom)
|
self.inputs.append(self.outfrom)
|
||||||
self.inputs.append(self.outto)
|
self.inputs.append(self.outto)
|
||||||
|
|
||||||
|
|
||||||
self.add_header("Interface", True)
|
self.add_header("Interface", True)
|
||||||
self.add_text_input("listen_interface", "IP address of the interface to listen on (leave empty for default):", parent.core_config["listen_interface"])
|
self.add_text_input("listen_interface", "IP address of the interface to listen on (leave empty for default):",
|
||||||
|
parent.core_config["listen_interface"])
|
||||||
|
|
||||||
self.add_header("TOS", True)
|
self.add_header("TOS", True)
|
||||||
self.add_text_input("peer_tos", "Peer TOS Byte:", parent.core_config["peer_tos"])
|
self.add_text_input("peer_tos", "Peer TOS Byte:", parent.core_config["peer_tos"])
|
||||||
|
@ -358,28 +349,44 @@ class NetworkPane(BasePane):
|
||||||
self.add_checked_input("dht", "DHT", parent.core_config["dht"])
|
self.add_checked_input("dht", "DHT", parent.core_config["dht"])
|
||||||
|
|
||||||
self.add_header("Encryption", True)
|
self.add_header("Encryption", True)
|
||||||
self.add_select_input("enc_in_policy", "Inbound:", ["Forced", "Enabled", "Disabled"], [0, 1, 2], parent.core_config["enc_in_policy"])
|
self.add_select_input("enc_in_policy", "Inbound:", ["Forced", "Enabled", "Disabled"], [0, 1, 2],
|
||||||
self.add_select_input("enc_out_policy", "Outbound:", ["Forced", "Enabled", "Disabled"], [0, 1, 2], parent.core_config["enc_out_policy"])
|
parent.core_config["enc_in_policy"])
|
||||||
self.add_select_input("enc_level", "Level:", ["Handshake", "Full Stream", "Either"], [0, 1, 2], parent.core_config["enc_level"])
|
self.add_select_input("enc_out_policy", "Outbound:", ["Forced", "Enabled", "Disabled"], [0, 1, 2],
|
||||||
|
parent.core_config["enc_out_policy"])
|
||||||
|
self.add_select_input("enc_level", "Level:", ["Handshake", "Full Stream", "Either"], [0, 1, 2],
|
||||||
|
parent.core_config["enc_level"])
|
||||||
|
|
||||||
|
|
||||||
class BandwidthPane(BasePane):
|
class BandwidthPane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
BasePane.__init__(self, offset, parent, width)
|
BasePane.__init__(self, offset, parent, width)
|
||||||
self.add_header("Global Bandwidth Usage")
|
self.add_header("Global Bandwidth Usage")
|
||||||
self.add_int_spin_input("max_connections_global", "Maximum Connections:", parent.core_config["max_connections_global"], -1, 9000)
|
self.add_int_spin_input("max_connections_global", "Maximum Connections:",
|
||||||
self.add_int_spin_input("max_upload_slots_global", "Maximum Upload Slots:", parent.core_config["max_upload_slots_global"], -1, 9000)
|
parent.core_config["max_connections_global"], -1, 9000)
|
||||||
self.add_float_spin_input("max_download_speed", "Maximum Download Speed (KiB/s):", parent.core_config["max_download_speed"], 1.0, 1, -1.0, 60000.0)
|
self.add_int_spin_input("max_upload_slots_global", "Maximum Upload Slots:",
|
||||||
self.add_float_spin_input("max_upload_speed", "Maximum Upload Speed (KiB/s):", parent.core_config["max_upload_speed"], 1.0, 1, -1.0, 60000.0)
|
parent.core_config["max_upload_slots_global"], -1, 9000)
|
||||||
self.add_int_spin_input("max_half_open_connections", "Maximum Half-Open Connections:", parent.core_config["max_half_open_connections"], -1, 9999)
|
self.add_float_spin_input("max_download_speed", "Maximum Download Speed (KiB/s):",
|
||||||
self.add_int_spin_input("max_connections_per_second", "Maximum Connection Attempts per Second:", parent.core_config["max_connections_per_second"], -1, 9999)
|
parent.core_config["max_download_speed"], 1.0, 1, -1.0, 60000.0)
|
||||||
self.add_checked_input("ignore_limits_on_local_network", "Ignore limits on local network", parent.core_config["ignore_limits_on_local_network"])
|
self.add_float_spin_input("max_upload_speed", "Maximum Upload Speed (KiB/s):",
|
||||||
self.add_checked_input("rate_limit_ip_overhead", "Rate Limit IP Overhead", parent.core_config["rate_limit_ip_overhead"])
|
parent.core_config["max_upload_speed"], 1.0, 1, -1.0, 60000.0)
|
||||||
|
self.add_int_spin_input("max_half_open_connections", "Maximum Half-Open Connections:",
|
||||||
|
parent.core_config["max_half_open_connections"], -1, 9999)
|
||||||
|
self.add_int_spin_input("max_connections_per_second", "Maximum Connection Attempts per Second:",
|
||||||
|
parent.core_config["max_connections_per_second"], -1, 9999)
|
||||||
|
self.add_checked_input("ignore_limits_on_local_network", "Ignore limits on local network",
|
||||||
|
parent.core_config["ignore_limits_on_local_network"])
|
||||||
|
self.add_checked_input("rate_limit_ip_overhead", "Rate Limit IP Overhead",
|
||||||
|
parent.core_config["rate_limit_ip_overhead"])
|
||||||
self.add_header("Per Torrent Bandwidth Usage", True)
|
self.add_header("Per Torrent Bandwidth Usage", True)
|
||||||
self.add_int_spin_input("max_connections_per_torrent", "Maximum Connections:", parent.core_config["max_connections_per_torrent"], -1, 9000)
|
self.add_int_spin_input("max_connections_per_torrent", "Maximum Connections:",
|
||||||
self.add_int_spin_input("max_upload_slots_per_torrent", "Maximum Upload Slots:", parent.core_config["max_upload_slots_per_torrent"], -1, 9000)
|
parent.core_config["max_connections_per_torrent"], -1, 9000)
|
||||||
self.add_float_spin_input("max_download_speed_per_torrent", "Maximum Download Speed (KiB/s):", parent.core_config["max_download_speed_per_torrent"], 1.0, 1, -1.0, 60000.0)
|
self.add_int_spin_input("max_upload_slots_per_torrent", "Maximum Upload Slots:",
|
||||||
self.add_float_spin_input("max_upload_speed_per_torrent", "Maximum Upload Speed (KiB/s):", parent.core_config["max_upload_speed_per_torrent"], 1.0, 1, -1.0, 60000.0)
|
parent.core_config["max_upload_slots_per_torrent"], -1, 9000)
|
||||||
|
self.add_float_spin_input("max_download_speed_per_torrent", "Maximum Download Speed (KiB/s):",
|
||||||
|
parent.core_config["max_download_speed_per_torrent"], 1.0, 1, -1.0, 60000.0)
|
||||||
|
self.add_float_spin_input("max_upload_speed_per_torrent", "Maximum Upload Speed (KiB/s):",
|
||||||
|
parent.core_config["max_upload_speed_per_torrent"], 1.0, 1, -1.0, 60000.0)
|
||||||
|
|
||||||
|
|
||||||
class OtherPane(BasePane):
|
class OtherPane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
|
@ -392,6 +399,7 @@ class OtherPane(BasePane):
|
||||||
self.add_header("GeoIP Database", True)
|
self.add_header("GeoIP Database", True)
|
||||||
self.add_text_input("geoip_db_location", "Location:", parent.core_config["geoip_db_location"])
|
self.add_text_input("geoip_db_location", "Location:", parent.core_config["geoip_db_location"])
|
||||||
|
|
||||||
|
|
||||||
class DaemonPane(BasePane):
|
class DaemonPane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
BasePane.__init__(self, offset, parent, width)
|
BasePane.__init__(self, offset, parent, width)
|
||||||
|
@ -400,7 +408,9 @@ class DaemonPane(BasePane):
|
||||||
self.add_header("Connections", True)
|
self.add_header("Connections", True)
|
||||||
self.add_checked_input("allow_remote", "Allow remote connections", parent.core_config["allow_remote"])
|
self.add_checked_input("allow_remote", "Allow remote connections", parent.core_config["allow_remote"])
|
||||||
self.add_header("Other", True)
|
self.add_header("Other", True)
|
||||||
self.add_checked_input("new_release_check", "Periodically check the website for new releases", parent.core_config["new_release_check"])
|
self.add_checked_input("new_release_check", "Periodically check the website for new releases",
|
||||||
|
parent.core_config["new_release_check"])
|
||||||
|
|
||||||
|
|
||||||
class QueuePane(BasePane):
|
class QueuePane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
|
@ -409,17 +419,27 @@ class QueuePane(BasePane):
|
||||||
self.add_checked_input("queue_new_to_top", "Queue new torrents to top", parent.core_config["queue_new_to_top"])
|
self.add_checked_input("queue_new_to_top", "Queue new torrents to top", parent.core_config["queue_new_to_top"])
|
||||||
self.add_header("Active Torrents", True)
|
self.add_header("Active Torrents", True)
|
||||||
self.add_int_spin_input("max_active_limit", "Total active:", parent.core_config["max_active_limit"], -1, 9999)
|
self.add_int_spin_input("max_active_limit", "Total active:", parent.core_config["max_active_limit"], -1, 9999)
|
||||||
self.add_int_spin_input("max_active_downloading", "Total active downloading:", parent.core_config["max_active_downloading"], -1, 9999)
|
self.add_int_spin_input("max_active_downloading", "Total active downloading:",
|
||||||
self.add_int_spin_input("max_active_seeding", "Total active seeding:", parent.core_config["max_active_seeding"], -1, 9999)
|
parent.core_config["max_active_downloading"], -1, 9999)
|
||||||
self.add_checked_input("dont_count_slow_torrents", "Do not count slow torrents", parent.core_config["dont_count_slow_torrents"])
|
self.add_int_spin_input("max_active_seeding", "Total active seeding:",
|
||||||
self.add_checked_input("auto_manage_prefer_seeds", "Prefer Seeding over Downloading", parent.core_config["auto_manage_prefer_seeds"])
|
parent.core_config["max_active_seeding"], -1, 9999)
|
||||||
|
self.add_checked_input("dont_count_slow_torrents", "Do not count slow torrents",
|
||||||
|
parent.core_config["dont_count_slow_torrents"])
|
||||||
|
self.add_checked_input("auto_manage_prefer_seeds", "Prefer Seeding over Downloading",
|
||||||
|
parent.core_config["auto_manage_prefer_seeds"])
|
||||||
self.add_header("Seeding", True)
|
self.add_header("Seeding", True)
|
||||||
self.add_float_spin_input("share_ratio_limit", "Share Ratio Limit:", parent.core_config["share_ratio_limit"], 1.0, 2, -1.0, 100.0)
|
self.add_float_spin_input("share_ratio_limit", "Share Ratio Limit:",
|
||||||
self.add_float_spin_input("seed_time_ratio_limit", "Share Time Ratio:", parent.core_config["seed_time_ratio_limit"], 1.0, 2, -1.0, 100.0)
|
parent.core_config["share_ratio_limit"], 1.0, 2, -1.0, 100.0)
|
||||||
|
self.add_float_spin_input("seed_time_ratio_limit", "Share Time Ratio:",
|
||||||
|
parent.core_config["seed_time_ratio_limit"], 1.0, 2, -1.0, 100.0)
|
||||||
self.add_int_spin_input("seed_time_limit", "Seed time (m):", parent.core_config["seed_time_limit"], -1, 10000)
|
self.add_int_spin_input("seed_time_limit", "Seed time (m):", parent.core_config["seed_time_limit"], -1, 10000)
|
||||||
seedratio = FloatSpinInput(self.parent, "", "stop_seed_ratio", self.move, parent.core_config["stop_seed_ratio"], 0.1, 2, 0.5, 100.0)
|
seedratio = FloatSpinInput(self.parent, "", "stop_seed_ratio", self.move,
|
||||||
self.add_checkedplus_input("stop_seed_at_ratio", "Stop seeding when share ratio reaches:", seedratio, parent.core_config["stop_seed_at_ratio"])
|
parent.core_config["stop_seed_ratio"], 0.1, 2, 0.5, 100.0)
|
||||||
self.add_checked_input("remove_seed_at_ratio", "Remove torrent when share ratio reached", parent.core_config["remove_seed_at_ratio"])
|
self.add_checkedplus_input("stop_seed_at_ratio", "Stop seeding when share ratio reaches:", seedratio,
|
||||||
|
parent.core_config["stop_seed_at_ratio"])
|
||||||
|
self.add_checked_input("remove_seed_at_ratio", "Remove torrent when share ratio reached",
|
||||||
|
parent.core_config["remove_seed_at_ratio"])
|
||||||
|
|
||||||
|
|
||||||
class ProxyPane(BasePane):
|
class ProxyPane(BasePane):
|
||||||
def __init__(self, offset, parent, width):
|
def __init__(self, offset, parent, width):
|
||||||
|
@ -454,12 +474,13 @@ class CachePane(BasePane):
|
||||||
self.add_header(" Write")
|
self.add_header(" Write")
|
||||||
self.add_info_field(" Blocks Written:", self.parent.status["blocks_written"], "blocks_written")
|
self.add_info_field(" Blocks Written:", self.parent.status["blocks_written"], "blocks_written")
|
||||||
self.add_info_field(" Writes:", self.parent.status["writes"], "writes")
|
self.add_info_field(" Writes:", self.parent.status["writes"], "writes")
|
||||||
self.add_info_field(" Write Cache Hit Ratio:", "%.2f"%self.parent.status["write_hit_ratio"], "write_hit_ratio")
|
self.add_info_field(" Write Cache Hit Ratio:", "%.2f" % self.parent.status["write_hit_ratio"],
|
||||||
|
"write_hit_ratio")
|
||||||
self.add_header(" Read")
|
self.add_header(" Read")
|
||||||
self.add_info_field(" Blocks Read:", self.parent.status["blocks_read"], "blocks_read")
|
self.add_info_field(" Blocks Read:", self.parent.status["blocks_read"], "blocks_read")
|
||||||
self.add_info_field(" Blocks Read hit:", self.parent.status["blocks_read_hit"], "blocks_read_hit")
|
self.add_info_field(" Blocks Read hit:", self.parent.status["blocks_read_hit"], "blocks_read_hit")
|
||||||
self.add_info_field(" Reads:", self.parent.status["reads"], "reads")
|
self.add_info_field(" Reads:", self.parent.status["reads"], "reads")
|
||||||
self.add_info_field(" Read Cache Hit Ratio:", "%.2f"%self.parent.status["read_hit_ratio"], "read_hit_ratio")
|
self.add_info_field(" Read Cache Hit Ratio:", "%.2f" % self.parent.status["read_hit_ratio"], "read_hit_ratio")
|
||||||
self.add_header(" Size")
|
self.add_header(" Size")
|
||||||
self.add_info_field(" Cache Size:", self.parent.status["cache_size"], "cache_size")
|
self.add_info_field(" Cache Size:", self.parent.status["cache_size"], "cache_size")
|
||||||
self.add_info_field(" Read Cache Size:", self.parent.status["read_cache_size"], "read_cache_size")
|
self.add_info_field(" Read Cache Size:", self.parent.status["read_cache_size"], "read_cache_size")
|
||||||
|
|
|
@ -1,37 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# preferences.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -40,6 +13,7 @@ from collections import deque
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from basemode import BaseMode
|
from basemode import BaseMode
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
|
from popup import MessagePopup
|
||||||
from input_popup import Popup, SelectInput
|
from input_popup import Popup, SelectInput
|
||||||
from preference_panes import (BandwidthPane, CachePane, ColumnsPane, DaemonPane, DownloadsPane, InterfacePane,
|
from preference_panes import (BandwidthPane, CachePane, ColumnsPane, DaemonPane, DownloadsPane, InterfacePane,
|
||||||
NetworkPane, OtherPane, ProxyPane, QueuePane)
|
NetworkPane, OtherPane, ProxyPane, QueuePane)
|
||||||
|
@ -54,9 +28,7 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# Big help string that gets displayed when the user hits 'h'
|
# Big help string that gets displayed when the user hits 'h'
|
||||||
HELP_STR = \
|
HELP_STR = """This screen lets you view and configure various options in deluge.
|
||||||
"""This screen lets you view and configure various options
|
|
||||||
in deluge.
|
|
||||||
|
|
||||||
There are three main sections to this screen. Only one
|
There are three main sections to this screen. Only one
|
||||||
section is active at a time. You can switch the active
|
section is active at a time. You can switch the active
|
||||||
|
@ -95,7 +67,7 @@ Special keys for various input types are as follows:
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
HELP_LINES = HELP_STR.split('\n')
|
HELP_LINES = HELP_STR.split("\n")
|
||||||
|
|
||||||
|
|
||||||
class ZONE:
|
class ZONE:
|
||||||
|
@ -103,6 +75,7 @@ class ZONE:
|
||||||
PREFRENCES = 1
|
PREFRENCES = 1
|
||||||
ACTIONS = 2
|
ACTIONS = 2
|
||||||
|
|
||||||
|
|
||||||
class Preferences(BaseMode):
|
class Preferences(BaseMode):
|
||||||
def __init__(self, parent_mode, core_config, console_config, active_port, status, stdscr, encoding=None):
|
def __init__(self, parent_mode, core_config, console_config, active_port, status, stdscr, encoding=None):
|
||||||
self.parent_mode = parent_mode
|
self.parent_mode = parent_mode
|
||||||
|
@ -132,39 +105,39 @@ class Preferences(BaseMode):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def __calc_sizes(self):
|
def __calc_sizes(self):
|
||||||
self.prefs_width = self.cols-self.div_off-1
|
self.prefs_width = self.cols - self.div_off - 1
|
||||||
self.prefs_height = self.rows-4
|
self.prefs_height = self.rows - 4
|
||||||
# Needs to be same order as self.categories
|
# Needs to be same order as self.categories
|
||||||
self.panes = [
|
self.panes = [
|
||||||
InterfacePane(self.div_off+2, self, self.prefs_width),
|
InterfacePane(self.div_off + 2, self, self.prefs_width),
|
||||||
ColumnsPane(self.div_off+2, self, self.prefs_width),
|
ColumnsPane(self.div_off + 2, self, self.prefs_width),
|
||||||
DownloadsPane(self.div_off+2, self, self.prefs_width),
|
DownloadsPane(self.div_off + 2, self, self.prefs_width),
|
||||||
NetworkPane(self.div_off+2, self, self.prefs_width),
|
NetworkPane(self.div_off + 2, self, self.prefs_width),
|
||||||
BandwidthPane(self.div_off+2, self, self.prefs_width),
|
BandwidthPane(self.div_off + 2, self, self.prefs_width),
|
||||||
OtherPane(self.div_off+2, self, self.prefs_width),
|
OtherPane(self.div_off + 2, self, self.prefs_width),
|
||||||
DaemonPane(self.div_off+2, self, self.prefs_width),
|
DaemonPane(self.div_off + 2, self, self.prefs_width),
|
||||||
QueuePane(self.div_off+2, self, self.prefs_width),
|
QueuePane(self.div_off + 2, self, self.prefs_width),
|
||||||
ProxyPane(self.div_off+2, self, self.prefs_width),
|
ProxyPane(self.div_off + 2, self, self.prefs_width),
|
||||||
CachePane(self.div_off+2, self, self.prefs_width)
|
CachePane(self.div_off + 2, self, self.prefs_width)
|
||||||
]
|
]
|
||||||
|
|
||||||
def __draw_catetories(self):
|
def __draw_catetories(self):
|
||||||
for i, category in enumerate(self.categories):
|
for i, category in enumerate(self.categories):
|
||||||
if i == self.cur_cat and self.active_zone == ZONE.CATEGORIES:
|
if i == self.cur_cat and self.active_zone == ZONE.CATEGORIES:
|
||||||
self.add_string(i+1, "- {!black,white,bold!}%s"%category, pad=False)
|
self.add_string(i + 1, "- {!black,white,bold!}%s" % category, pad=False)
|
||||||
elif i == self.cur_cat:
|
elif i == self.cur_cat:
|
||||||
self.add_string(i+1, "- {!black,white!}%s"%category, pad=False)
|
self.add_string(i + 1, "- {!black,white!}%s" % category, pad=False)
|
||||||
else:
|
else:
|
||||||
self.add_string(i+1, "- %s"%category)
|
self.add_string(i + 1, "- %s" % category)
|
||||||
self.stdscr.vline(1, self.div_off, '|', self.rows-2)
|
self.stdscr.vline(1, self.div_off, "|", self.rows - 2)
|
||||||
|
|
||||||
def __draw_preferences(self):
|
def __draw_preferences(self):
|
||||||
self.panes[self.cur_cat].render(self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES)
|
self.panes[self.cur_cat].render(self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES)
|
||||||
|
|
||||||
def __draw_actions(self):
|
def __draw_actions(self):
|
||||||
selected = self.active_zone == ZONE.ACTIONS
|
selected = self.active_zone == ZONE.ACTIONS
|
||||||
self.stdscr.hline(self.rows-3, self.div_off+1, "_", self.cols)
|
self.stdscr.hline(self.rows - 3, self.div_off + 1, "_", self.cols)
|
||||||
self.action_input.render(self.stdscr, self.rows-2, self.cols, selected, self.cols-22)
|
self.action_input.render(self.stdscr, self.rows - 2, self.cols, selected, self.cols - 22)
|
||||||
|
|
||||||
def on_resize(self, *args):
|
def on_resize(self, *args):
|
||||||
BaseMode.on_resize_norefresh(self, *args)
|
BaseMode.on_resize_norefresh(self, *args)
|
||||||
|
@ -177,14 +150,14 @@ class Preferences(BaseMode):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
if self.popup == None and self.messages:
|
if self.popup is None and self.messages:
|
||||||
title, msg = self.messages.popleft()
|
title, msg = self.messages.popleft()
|
||||||
self.popup = MessagePopup(self, title, msg)
|
self.popup = MessagePopup(self, title, msg)
|
||||||
|
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
self.add_string(0, self.statusbars.topbar)
|
self.add_string(0, self.statusbars.topbar)
|
||||||
hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10))
|
hstr = "%sPress [h] for help" % (" " * (self.cols - len(self.statusbars.bottombar) - 10))
|
||||||
self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar, hstr))
|
self.add_string(self.rows - 1, "%s%s" % (self.statusbars.bottombar, hstr))
|
||||||
|
|
||||||
self.__draw_catetories()
|
self.__draw_catetories()
|
||||||
self.__draw_actions()
|
self.__draw_actions()
|
||||||
|
@ -205,9 +178,9 @@ class Preferences(BaseMode):
|
||||||
def __category_read(self, c):
|
def __category_read(self, c):
|
||||||
# Navigate prefs
|
# Navigate prefs
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
self.cur_cat = max(0, self.cur_cat-1)
|
self.cur_cat = max(0, self.cur_cat - 1)
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
self.cur_cat = min(len(self.categories)-1, self.cur_cat+1)
|
self.cur_cat = min(len(self.categories) - 1, self.cur_cat + 1)
|
||||||
|
|
||||||
def __prefs_read(self, c):
|
def __prefs_read(self, c):
|
||||||
self.panes[self.cur_cat].handle_read(c)
|
self.panes[self.cur_cat].handle_read(c)
|
||||||
|
@ -251,7 +224,6 @@ class Preferences(BaseMode):
|
||||||
self.console_config.save()
|
self.console_config.save()
|
||||||
self.parent_mode.update_config()
|
self.parent_mode.update_config()
|
||||||
|
|
||||||
|
|
||||||
def __update_preferences(self, core_config):
|
def __update_preferences(self, core_config):
|
||||||
self.core_config = core_config
|
self.core_config = core_config
|
||||||
for pane in self.panes:
|
for pane in self.panes:
|
||||||
|
@ -261,16 +233,15 @@ class Preferences(BaseMode):
|
||||||
self.action_input.handle_read(c)
|
self.action_input.handle_read(c)
|
||||||
if c == curses.KEY_ENTER or c == 10:
|
if c == curses.KEY_ENTER or c == 10:
|
||||||
# take action
|
# take action
|
||||||
if self.action_input.selidx == 0: # cancel
|
if self.action_input.selidx == 0: # Cancel
|
||||||
self.back_to_parent()
|
self.back_to_parent()
|
||||||
elif self.action_input.selidx == 1: # apply
|
elif self.action_input.selidx == 1: # Apply
|
||||||
self.__apply_prefs()
|
self.__apply_prefs()
|
||||||
client.core.get_config().addCallback(self.__update_preferences)
|
client.core.get_config().addCallback(self.__update_preferences)
|
||||||
elif self.action_input.selidx == 2: # OK
|
elif self.action_input.selidx == 2: # OK
|
||||||
self.__apply_prefs()
|
self.__apply_prefs()
|
||||||
self.back_to_parent()
|
self.back_to_parent()
|
||||||
|
|
||||||
|
|
||||||
def back_to_parent(self):
|
def back_to_parent(self):
|
||||||
self.stdscr.erase()
|
self.stdscr.erase()
|
||||||
component.get("ConsoleUI").set_mode(self.parent_mode)
|
component.get("ConsoleUI").set_mode(self.parent_mode)
|
||||||
|
@ -286,7 +257,7 @@ class Preferences(BaseMode):
|
||||||
return
|
return
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'Q':
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -295,7 +266,7 @@ class Preferences(BaseMode):
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'h':
|
elif chr(c) == "h":
|
||||||
self.popup = Popup(self, "Preferences Help")
|
self.popup = Popup(self, "Preferences Help")
|
||||||
for l in HELP_LINES:
|
for l in HELP_LINES:
|
||||||
self.popup.add_line(l)
|
self.popup.add_line(l)
|
||||||
|
|
|
@ -1,35 +1,10 @@
|
||||||
# torrent_actions.py
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@ -38,7 +13,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.console import colors, modes
|
from deluge.ui.console import colors
|
||||||
from input_popup import InputPopup
|
from input_popup import InputPopup
|
||||||
from popup import Popup, SelectablePopup
|
from popup import Popup, SelectablePopup
|
||||||
|
|
||||||
|
@ -74,38 +49,42 @@ torrent_options_to_names = {
|
||||||
"move_on_completed_path": "Folder to move the torrent to"
|
"move_on_completed_path": "Folder to move the torrent to"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ACTION:
|
class ACTION:
|
||||||
PAUSE=0
|
PAUSE = 0
|
||||||
RESUME=1
|
RESUME = 1
|
||||||
REANNOUNCE=2
|
REANNOUNCE = 2
|
||||||
EDIT_TRACKERS=3
|
EDIT_TRACKERS = 3
|
||||||
RECHECK=4
|
RECHECK = 4
|
||||||
REMOVE=5
|
REMOVE = 5
|
||||||
REMOVE_DATA=6
|
REMOVE_DATA = 6
|
||||||
REMOVE_NODATA=7
|
REMOVE_NODATA = 7
|
||||||
DETAILS=8
|
DETAILS = 8
|
||||||
MOVE_STORAGE=9
|
MOVE_STORAGE = 9
|
||||||
QUEUE=10
|
QUEUE = 10
|
||||||
QUEUE_TOP=11
|
QUEUE_TOP = 11
|
||||||
QUEUE_UP=12
|
QUEUE_UP = 12
|
||||||
QUEUE_DOWN=13
|
QUEUE_DOWN = 13
|
||||||
QUEUE_BOTTOM=14
|
QUEUE_BOTTOM = 14
|
||||||
TORRENT_OPTIONS=15
|
TORRENT_OPTIONS = 15
|
||||||
|
|
||||||
|
|
||||||
def action_error(error, mode):
|
def action_error(error, mode):
|
||||||
rerr = error.value
|
rerr = error.value
|
||||||
mode.report_message("An Error Occurred", "%s got error %s: %s"%(rerr.method, rerr.exception_type, rerr.exception_msg))
|
mode.report_message("An Error Occurred", "%s got error %s: %s" % (
|
||||||
|
rerr.method, rerr.exception_type, rerr.exception_msg))
|
||||||
mode.refresh()
|
mode.refresh()
|
||||||
|
|
||||||
|
|
||||||
def torrent_action(idx, data, mode, ids):
|
def torrent_action(idx, data, mode, ids):
|
||||||
if ids:
|
if ids:
|
||||||
if data==ACTION.PAUSE:
|
if data == ACTION.PAUSE:
|
||||||
log.debug("Pausing torrents: %s", ids)
|
log.debug("Pausing torrents: %s", ids)
|
||||||
client.core.pause_torrent(ids).addErrback(action_error, mode)
|
client.core.pause_torrent(ids).addErrback(action_error, mode)
|
||||||
elif data==ACTION.RESUME:
|
elif data == ACTION.RESUME:
|
||||||
log.debug("Resuming torrents: %s", ids)
|
log.debug("Resuming torrents: %s", ids)
|
||||||
client.core.resume_torrent(ids).addErrback(action_error, mode)
|
client.core.resume_torrent(ids).addErrback(action_error, mode)
|
||||||
elif data==ACTION.QUEUE:
|
elif data == ACTION.QUEUE:
|
||||||
def do_queue(idx, qact, mode, ids):
|
def do_queue(idx, qact, mode, ids):
|
||||||
def move_selection(r):
|
def move_selection(r):
|
||||||
if mode.config["move_selection"]:
|
if mode.config["move_selection"]:
|
||||||
|
@ -125,18 +104,18 @@ def torrent_action(idx, data, mode, ids):
|
||||||
mode.marked = range(1, selected_num + 1)
|
mode.marked = range(1, selected_num + 1)
|
||||||
elif qact == ACTION.QUEUE_UP:
|
elif qact == ACTION.QUEUE_UP:
|
||||||
mode.cursel = max(1, mode.cursel - 1)
|
mode.cursel = max(1, mode.cursel - 1)
|
||||||
mode.marked = map(lambda v: v-1, mode.marked)
|
mode.marked = map(lambda v: v - 1, mode.marked)
|
||||||
mode.marked = filter(lambda v: v>0, mode.marked)
|
mode.marked = filter(lambda v: v > 0, mode.marked)
|
||||||
elif qact == ACTION.QUEUE_DOWN:
|
elif qact == ACTION.QUEUE_DOWN:
|
||||||
mode.cursel = min(queue_length, mode.cursel + 1)
|
mode.cursel = min(queue_length, mode.cursel + 1)
|
||||||
mode.marked = map(lambda v: v+1, mode.marked)
|
mode.marked = map(lambda v: v + 1, mode.marked)
|
||||||
mode.marked = filter(lambda v: v<=queue_length, mode.marked)
|
mode.marked = filter(lambda v: v <= queue_length, mode.marked)
|
||||||
elif qact == ACTION.QUEUE_BOTTOM:
|
elif qact == ACTION.QUEUE_BOTTOM:
|
||||||
if mode.marked:
|
if mode.marked:
|
||||||
mode.cursel = queue_length - selected_num + 1 + sorted(mode.marked).index(mode.cursel)
|
mode.cursel = queue_length - selected_num + 1 + sorted(mode.marked).index(mode.cursel)
|
||||||
else:
|
else:
|
||||||
mode.cursel = queue_length
|
mode.cursel = queue_length
|
||||||
mode.marked = range(queue_length - selected_num + 1, queue_length+1)
|
mode.marked = range(queue_length - selected_num + 1, queue_length + 1)
|
||||||
|
|
||||||
if qact == ACTION.QUEUE_TOP:
|
if qact == ACTION.QUEUE_TOP:
|
||||||
log.debug("Queuing torrents top")
|
log.debug("Queuing torrents top")
|
||||||
|
@ -161,9 +140,10 @@ def torrent_action(idx, data, mode, ids):
|
||||||
popup.add_line("_Bottom", data=ACTION.QUEUE_BOTTOM)
|
popup.add_line("_Bottom", data=ACTION.QUEUE_BOTTOM)
|
||||||
mode.set_popup(popup)
|
mode.set_popup(popup)
|
||||||
return False
|
return False
|
||||||
elif data==ACTION.REMOVE:
|
elif data == ACTION.REMOVE:
|
||||||
def do_remove(data):
|
def do_remove(data):
|
||||||
if not data: return
|
if not data:
|
||||||
|
return
|
||||||
mode.clear_marks()
|
mode.clear_marks()
|
||||||
|
|
||||||
wd = data["remove_files"]
|
wd = data["remove_files"]
|
||||||
|
@ -171,17 +151,13 @@ def torrent_action(idx, data, mode, ids):
|
||||||
log.debug("Removing torrent: %s, %d", tid, wd)
|
log.debug("Removing torrent: %s, %d", tid, wd)
|
||||||
client.core.remove_torrent(tid, wd).addErrback(action_error, mode)
|
client.core.remove_torrent(tid, wd).addErrback(action_error, mode)
|
||||||
|
|
||||||
rem_msg = ""
|
|
||||||
|
|
||||||
|
|
||||||
def got_status(status):
|
def got_status(status):
|
||||||
return (status["name"], status["state"])
|
return (status["name"], status["state"])
|
||||||
|
|
||||||
callbacks = []
|
callbacks = []
|
||||||
for tid in ids:
|
for tid in ids:
|
||||||
d = client.core.get_torrent_status(tid, ["name", "state"])
|
d = client.core.get_torrent_status(tid, ["name", "state"])
|
||||||
callbacks.append( d.addCallback(got_status) )
|
callbacks.append(d.addCallback(got_status))
|
||||||
|
|
||||||
|
|
||||||
def finish_up(status):
|
def finish_up(status):
|
||||||
status = map(lambda x: x[1], status)
|
status = map(lambda x: x[1], status)
|
||||||
|
@ -202,15 +178,17 @@ def torrent_action(idx, data, mode, ids):
|
||||||
popup = InputPopup(mode, "(Esc to cancel, Enter to remove)", close_cb=do_remove)
|
popup = InputPopup(mode, "(Esc to cancel, Enter to remove)", close_cb=do_remove)
|
||||||
popup.add_text(rem_msg)
|
popup.add_text(rem_msg)
|
||||||
popup.add_spaces(1)
|
popup.add_spaces(1)
|
||||||
popup.add_select_input("{!info!}Torrent files:", 'remove_files', ["Keep", "Remove"], [False, True], False)
|
popup.add_select_input("{!info!}Torrent files:", "remove_files",
|
||||||
|
["Keep", "Remove"], [False, True], False)
|
||||||
mode.set_popup(popup)
|
mode.set_popup(popup)
|
||||||
defer.DeferredList(callbacks).addCallback(finish_up)
|
defer.DeferredList(callbacks).addCallback(finish_up)
|
||||||
return False
|
return False
|
||||||
elif data==ACTION.MOVE_STORAGE:
|
elif data == ACTION.MOVE_STORAGE:
|
||||||
def do_move(res):
|
def do_move(res):
|
||||||
import os.path
|
import os.path
|
||||||
if os.path.exists(res["path"]) and not os.path.isdir(res["path"]):
|
if os.path.exists(res["path"]) and not os.path.isdir(res["path"]):
|
||||||
mode.report_message("Cannot Move Download Folder", "{!error!}%s exists and is not a directory"%res["path"])
|
mode.report_message("Cannot Move Download Folder",
|
||||||
|
"{!error!}%s exists and is not a directory" % res["path"])
|
||||||
else:
|
else:
|
||||||
log.debug("Moving %s to: %s", ids, res["path"])
|
log.debug("Moving %s to: %s", ids, res["path"])
|
||||||
client.core.move_storage(ids, res["path"]).addErrback(action_error, mode)
|
client.core.move_storage(ids, res["path"]).addErrback(action_error, mode)
|
||||||
|
@ -221,20 +199,20 @@ def torrent_action(idx, data, mode, ids):
|
||||||
popup.add_text_input("Enter path to move to:", "path")
|
popup.add_text_input("Enter path to move to:", "path")
|
||||||
mode.set_popup(popup)
|
mode.set_popup(popup)
|
||||||
return False
|
return False
|
||||||
elif data==ACTION.RECHECK:
|
elif data == ACTION.RECHECK:
|
||||||
log.debug("Rechecking torrents: %s", ids)
|
log.debug("Rechecking torrents: %s", ids)
|
||||||
client.core.force_recheck(ids).addErrback(action_error, mode)
|
client.core.force_recheck(ids).addErrback(action_error, mode)
|
||||||
elif data==ACTION.REANNOUNCE:
|
elif data == ACTION.REANNOUNCE:
|
||||||
log.debug("Reannouncing torrents: %s", ids)
|
log.debug("Reannouncing torrents: %s", ids)
|
||||||
client.core.force_reannounce(ids).addErrback(action_error, mode)
|
client.core.force_reannounce(ids).addErrback(action_error, mode)
|
||||||
elif data==ACTION.DETAILS:
|
elif data == ACTION.DETAILS:
|
||||||
log.debug("Torrent details")
|
log.debug("Torrent details")
|
||||||
tid = mode.current_torrent_id()
|
tid = mode.current_torrent_id()
|
||||||
if tid:
|
if tid:
|
||||||
mode.show_torrent_details(tid)
|
mode.show_torrent_details(tid)
|
||||||
else:
|
else:
|
||||||
log.error("No current torrent in _torrent_action, this is a bug")
|
log.error("No current torrent in _torrent_action, this is a bug")
|
||||||
elif data==ACTION.TORRENT_OPTIONS:
|
elif data == ACTION.TORRENT_OPTIONS:
|
||||||
mode.popup = Popup(mode, "Torrent options")
|
mode.popup = Popup(mode, "Torrent options")
|
||||||
mode.popup.add_line("Querying core, please wait...")
|
mode.popup.add_line("Querying core, please wait...")
|
||||||
|
|
||||||
|
@ -247,7 +225,7 @@ def torrent_action(idx, data, mode, ids):
|
||||||
for opt in result:
|
for opt in result:
|
||||||
if result[opt] not in ["multiple", None]:
|
if result[opt] not in ["multiple", None]:
|
||||||
options[opt] = result[opt]
|
options[opt] = result[opt]
|
||||||
client.core.set_torrent_options( ids, options )
|
client.core.set_torrent_options(ids, options)
|
||||||
for tid in ids:
|
for tid in ids:
|
||||||
if "move_on_completed_path" in options:
|
if "move_on_completed_path" in options:
|
||||||
client.core.set_torrent_move_completed_path(tid, options["move_on_completed_path"])
|
client.core.set_torrent_move_completed_path(tid, options["move_on_completed_path"])
|
||||||
|
@ -262,7 +240,7 @@ def torrent_action(idx, data, mode, ids):
|
||||||
|
|
||||||
def on_torrent_status(status):
|
def on_torrent_status(status):
|
||||||
for key in status:
|
for key in status:
|
||||||
if key not in options:
|
if key not in options:
|
||||||
options[key] = status[key]
|
options[key] = status[key]
|
||||||
elif options[key] != status[key]:
|
elif options[key] != status[key]:
|
||||||
options[key] = "multiple"
|
options[key] = "multiple"
|
||||||
|
@ -274,7 +252,7 @@ def torrent_action(idx, data, mode, ids):
|
||||||
for (field, field_type) in torrent_options:
|
for (field, field_type) in torrent_options:
|
||||||
caption = "{!info!}" + torrent_options_to_names[field]
|
caption = "{!info!}" + torrent_options_to_names[field]
|
||||||
value = options[field]
|
value = options[field]
|
||||||
if field_type == str:
|
if field_type == str:
|
||||||
if not isinstance(value, basestring):
|
if not isinstance(value, basestring):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
option_popup.add_text_input(caption, field, value)
|
option_popup.add_text_input(caption, field, value)
|
||||||
|
@ -293,9 +271,9 @@ def torrent_action(idx, data, mode, ids):
|
||||||
)
|
)
|
||||||
option_popup.add_select_input(caption, field, choices[0], choices[1], choices[2])
|
option_popup.add_select_input(caption, field, choices[0], choices[1], choices[2])
|
||||||
elif field_type == float:
|
elif field_type == float:
|
||||||
option_popup.add_float_spin_input(caption, field, value, min_val = -1)
|
option_popup.add_float_spin_input(caption, field, value, min_val=-1)
|
||||||
elif field_type == int:
|
elif field_type == int:
|
||||||
option_popup.add_int_spin_input(caption, field, value, min_val = -1)
|
option_popup.add_int_spin_input(caption, field, value, min_val=-1)
|
||||||
|
|
||||||
mode.set_popup(option_popup)
|
mode.set_popup(option_popup)
|
||||||
mode.refresh()
|
mode.refresh()
|
||||||
|
@ -306,7 +284,7 @@ def torrent_action(idx, data, mode, ids):
|
||||||
|
|
||||||
for tid in torrents:
|
for tid in torrents:
|
||||||
deferred = component.get("SessionProxy").get_torrent_status(tid, field_list)
|
deferred = component.get("SessionProxy").get_torrent_status(tid, field_list)
|
||||||
callbacks.append( deferred.addCallback(on_torrent_status) )
|
callbacks.append(deferred.addCallback(on_torrent_status))
|
||||||
|
|
||||||
callbacks = defer.DeferredList(callbacks)
|
callbacks = defer.DeferredList(callbacks)
|
||||||
callbacks.addCallback(create_popup)
|
callbacks.addCallback(create_popup)
|
||||||
|
@ -315,9 +293,10 @@ def torrent_action(idx, data, mode, ids):
|
||||||
mode.clear_marks()
|
mode.clear_marks()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# Creates the popup. mode is the calling mode, tids is a list of torrents to take action upon
|
# Creates the popup. mode is the calling mode, tids is a list of torrents to take action upon
|
||||||
def torrent_actions_popup(mode,tids,details=False, action = None):
|
def torrent_actions_popup(mode, tids, details=False, action=None):
|
||||||
if action != None:
|
if action is not None:
|
||||||
torrent_action(-1, action, mode, tids)
|
torrent_action(-1, action, mode, tids)
|
||||||
return
|
return
|
||||||
popup = SelectablePopup(mode, "Torrent Actions", torrent_action, (mode, tids))
|
popup = SelectablePopup(mode, "Torrent Actions", torrent_action, (mode, tids))
|
||||||
|
|
|
@ -1,53 +1,24 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# torrentdetail.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from sys import maxint
|
from sys import maxint
|
||||||
|
|
||||||
import deluge.common as common
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
import deluge.ui.console.colors as colors
|
import deluge.ui.console.colors as colors
|
||||||
import format_utils
|
import format_utils
|
||||||
from add_util import add_torrent
|
|
||||||
from basemode import BaseMode
|
from basemode import BaseMode
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
from deluge.ui.sessionproxy import SessionProxy
|
from deluge.common import fsize, fdate, ftime, FILE_PRIORITY
|
||||||
from input_popup import InputPopup
|
from input_popup import InputPopup
|
||||||
from popup import MessagePopup, Popup, SelectablePopup
|
from popup import MessagePopup, SelectablePopup
|
||||||
from torrent_actions import ACTION, torrent_actions_popup
|
from torrent_actions import ACTION, torrent_actions_popup
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -90,6 +61,7 @@ download priority of selected files and folders.
|
||||||
{!info!}Left Arrow{!normal!} - Go back to torrent overview.
|
{!info!}Left Arrow{!normal!} - Go back to torrent overview.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TorrentDetail(BaseMode, component.Component):
|
class TorrentDetail(BaseMode, component.Component):
|
||||||
def __init__(self, alltorrentmode, torrentid, stdscr, console_config, encoding=None):
|
def __init__(self, alltorrentmode, torrentid, stdscr, console_config, encoding=None):
|
||||||
|
|
||||||
|
@ -140,6 +112,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
# component start/update
|
# component start/update
|
||||||
def start(self):
|
def start(self):
|
||||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||||
|
|
||||||
|
@ -147,17 +120,19 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
log.debug("got state")
|
log.debug("got state")
|
||||||
|
|
||||||
if state.get("files"):
|
if state.get("files"):
|
||||||
self.full_names = dict([ (x['index'], x['path']) for x in state["files"]])
|
self.full_names = dict([(x["index"], x["path"]) for x in state["files"]])
|
||||||
|
|
||||||
need_prio_update = False
|
need_prio_update = False
|
||||||
if not self.file_list:
|
if not self.file_list:
|
||||||
# don't keep getting the files once we've got them once
|
# don't keep getting the files once we've got them once
|
||||||
if state.get("files"):
|
if state.get("files"):
|
||||||
self.files_sep = "{!green,black,bold,underline!}%s"%(("Files (torrent has %d files)"%len(state["files"])).center(self.cols))
|
self.files_sep = "{!green,black,bold,underline!}%s" % (
|
||||||
self.file_list, self.file_dict = self.build_file_list(state["files"], state["file_progress"], state["file_priorities"])
|
("Files (torrent has %d files)" % len(state["files"])).center(self.cols))
|
||||||
|
self.file_list, self.file_dict = self.build_file_list(state["files"], state["file_progress"],
|
||||||
|
state["file_priorities"])
|
||||||
self._status_keys.remove("files")
|
self._status_keys.remove("files")
|
||||||
else:
|
else:
|
||||||
self.files_sep = "{!green,black,bold,underline!}%s"%(("Files (File list unknown)").center(self.cols))
|
self.files_sep = "{!green,black,bold,underline!}%s" % (("Files (File list unknown)").center(self.cols))
|
||||||
need_prio_update = True
|
need_prio_update = True
|
||||||
self.__fill_progress(self.file_list, state["file_progress"])
|
self.__fill_progress(self.file_list, state["file_progress"])
|
||||||
for i, prio in enumerate(state["file_priorities"]):
|
for i, prio in enumerate(state["file_priorities"]):
|
||||||
|
@ -194,13 +169,13 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
cl = []
|
cl = []
|
||||||
if p == fin:
|
if p == fin:
|
||||||
ent = [p, f["index"], f["size"], cl, False,
|
ent = [p, f["index"], f["size"], cl, False,
|
||||||
format_utils.format_progress(prog[f["index"]]*100),
|
format_utils.format_progress(prog[f["index"]] * 100),
|
||||||
prio[f["index"]]]
|
prio[f["index"]]]
|
||||||
retdict[f["index"]] = ent
|
retdict[f["index"]] = ent
|
||||||
else:
|
else:
|
||||||
ent = [p, diridx, -1, cl, False, 0, -1]
|
ent = [p, diridx, -1, cl, False, 0, -1]
|
||||||
retdict[diridx] = ent
|
retdict[diridx] = ent
|
||||||
diridx-=1
|
diridx -= 1
|
||||||
cur.append(ent)
|
cur.append(ent)
|
||||||
cur = cl
|
cur = cl
|
||||||
else:
|
else:
|
||||||
|
@ -224,21 +199,22 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
# fills in progress fields in all entries based on progs
|
# fills in progress fields in all entries based on progs
|
||||||
# returns the # of bytes complete in all the children of fs
|
# returns the # of bytes complete in all the children of fs
|
||||||
def __fill_progress(self, fs, progs):
|
def __fill_progress(self, fs, progs):
|
||||||
if not progs: return 0
|
if not progs:
|
||||||
|
return 0
|
||||||
tb = 0
|
tb = 0
|
||||||
for f in fs:
|
for f in fs:
|
||||||
if f[3]: # dir, has some children
|
if f[3]: # dir, has some children
|
||||||
bd = self.__fill_progress(f[3], progs)
|
bd = self.__fill_progress(f[3], progs)
|
||||||
f[5] = format_utils.format_progress((bd/f[2])*100)
|
f[5] = format_utils.format_progress((bd / f[2]) * 100)
|
||||||
else: # file, update own prog and add to total
|
else: # file, update own prog and add to total
|
||||||
bd = f[2]*progs[f[1]]
|
bd = f[2] * progs[f[1]]
|
||||||
f[5] = format_utils.format_progress(progs[f[1]]*100)
|
f[5] = format_utils.format_progress(progs[f[1]] * 100)
|
||||||
tb += bd
|
tb += bd
|
||||||
return tb
|
return tb
|
||||||
|
|
||||||
def __fill_prio(self, fs):
|
def __fill_prio(self, fs):
|
||||||
for f in fs:
|
for f in fs:
|
||||||
if f[3]: # dir, so fill in children and compute our prio
|
if f[3]: # dir, so fill in children and compute our prio
|
||||||
self.__fill_prio(f[3])
|
self.__fill_prio(f[3])
|
||||||
child_prios = [e[6] for e in f[3]]
|
child_prios = [e[6] for e in f[3]]
|
||||||
if len(child_prios) > 1:
|
if len(child_prios) > 1:
|
||||||
|
@ -248,21 +224,21 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
def __update_columns(self):
|
def __update_columns(self):
|
||||||
self.column_widths = [-1, 15, 15, 20]
|
self.column_widths = [-1, 15, 15, 20]
|
||||||
req = sum(filter(lambda x:x >= 0, self.column_widths))
|
req = sum(filter(lambda x: x >= 0, self.column_widths))
|
||||||
if (req > self.cols): # can't satisfy requests, just spread out evenly
|
if (req > self.cols): # can't satisfy requests, just spread out evenly
|
||||||
cw = int(self.cols/len(self.column_names))
|
cw = int(self.cols / len(self.column_names))
|
||||||
for i in range(0, len(self.column_widths)):
|
for i in range(0, len(self.column_widths)):
|
||||||
self.column_widths[i] = cw
|
self.column_widths[i] = cw
|
||||||
else:
|
else:
|
||||||
rem = self.cols - req
|
rem = self.cols - req
|
||||||
var_cols = len(filter(lambda x: x < 0, self.column_widths))
|
var_cols = len(filter(lambda x: x < 0, self.column_widths))
|
||||||
vw = int(rem/var_cols)
|
vw = int(rem / var_cols)
|
||||||
for i in range(0, len(self.column_widths)):
|
for i in range(0, len(self.column_widths)):
|
||||||
if (self.column_widths[i] < 0):
|
if (self.column_widths[i] < 0):
|
||||||
self.column_widths[i] = vw
|
self.column_widths[i] = vw
|
||||||
|
|
||||||
self.column_string = "{!green,black,bold!}%s"%("".join(["%s%s"%(self.column_names[i], " "*(self.column_widths[i]-len(self.column_names[i]))) for i in range(0, len(self.column_names))]))
|
self.column_string = "{!green,black,bold!}%s" % ("".join(["%s%s" % (self.column_names[i], " " * (
|
||||||
|
self.column_widths[i] - len(self.column_names[i]))) for i in range(0, len(self.column_names))]))
|
||||||
|
|
||||||
def report_message(self, title, message):
|
def report_message(self, title, message):
|
||||||
self.messages.append((title, message))
|
self.messages.append((title, message))
|
||||||
|
@ -281,7 +257,8 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
def _on_torrentfilerenamed_event(self, torrent_id, index, new_name):
|
def _on_torrentfilerenamed_event(self, torrent_id, index, new_name):
|
||||||
if torrent_id == self.torrentid:
|
if torrent_id == self.torrentid:
|
||||||
self.file_dict[index][0] = new_name.split("/")[-1]
|
self.file_dict[index][0] = new_name.split("/")[-1]
|
||||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
component.get("SessionProxy").get_torrent_status(
|
||||||
|
self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||||
|
|
||||||
def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder):
|
def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder):
|
||||||
if torrent_id == self.torrentid:
|
if torrent_id == self.torrentid:
|
||||||
|
@ -298,7 +275,8 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
fe[0] = new_folder.strip("/").rpartition("/")[-1]
|
fe[0] = new_folder.strip("/").rpartition("/")[-1]
|
||||||
|
|
||||||
#self.__get_file_by_name(old_folder, self.file_list)[0] = new_folder.strip("/")
|
#self.__get_file_by_name(old_folder, self.file_list)[0] = new_folder.strip("/")
|
||||||
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
|
component.get("SessionProxy").get_torrent_status(
|
||||||
|
self.torrentid, self._status_keys).addCallback(self.set_state)
|
||||||
|
|
||||||
def draw_files(self, files, depth, off, idx):
|
def draw_files(self, files, depth, off, idx):
|
||||||
|
|
||||||
|
@ -309,30 +287,27 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
#from sys import stderr
|
#from sys import stderr
|
||||||
#print >> stderr, fl[6]
|
#print >> stderr, fl[6]
|
||||||
# kick out if we're going to draw too low on the screen
|
# kick out if we're going to draw too low on the screen
|
||||||
if (off >= self.rows-1):
|
if (off >= self.rows - 1):
|
||||||
self.more_to_draw = True
|
self.more_to_draw = True
|
||||||
return -1, -1
|
return -1, -1
|
||||||
|
|
||||||
self.file_limit = idx
|
self.file_limit = idx
|
||||||
|
|
||||||
|
|
||||||
# default color values
|
# default color values
|
||||||
fg = "white"
|
fg = "white"
|
||||||
bg = "black"
|
bg = "black"
|
||||||
attr = ""
|
attr = ""
|
||||||
|
|
||||||
if fl[6] == -2: priority = -1 #Mixed
|
if fl[6] == -2:
|
||||||
|
pass # Mixed
|
||||||
elif fl[6] == 0:
|
elif fl[6] == 0:
|
||||||
priority = 0 #Do Not Download
|
fg = "red" # Do Not Download
|
||||||
fg = "red"
|
|
||||||
elif fl[6] == 1:
|
elif fl[6] == 1:
|
||||||
priority = 1 #Normal
|
pass # Normal
|
||||||
elif fl[6] <= 6:
|
elif fl[6] <= 6:
|
||||||
priority = 2 #High
|
fg = "yellow" # High
|
||||||
fg = "yellow"
|
|
||||||
elif fl[6] == 7:
|
elif fl[6] == 7:
|
||||||
priority = 3 #Highest
|
fg = "green" # Highest
|
||||||
fg = "green"
|
|
||||||
|
|
||||||
if idx >= self.file_off:
|
if idx >= self.file_off:
|
||||||
# set fg/bg colors based on whether the file is selected/marked or not
|
# set fg/bg colors based on whether the file is selected/marked or not
|
||||||
|
@ -350,7 +325,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
if fl[1] in self.marked:
|
if fl[1] in self.marked:
|
||||||
fg = color_selected
|
fg = color_selected
|
||||||
if fl[3]:
|
if fl[3]:
|
||||||
if self.marked[fl[1]] < self.__get_contained_files_count(file_list = fl[3]):
|
if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]):
|
||||||
fg = color_partially_selected
|
fg = color_partially_selected
|
||||||
else:
|
else:
|
||||||
if fg == "white":
|
if fg == "white":
|
||||||
|
@ -358,30 +333,31 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
attr = "bold"
|
attr = "bold"
|
||||||
|
|
||||||
if attr:
|
if attr:
|
||||||
color_string = "{!%s,%s,%s!}"%(fg, bg, attr)
|
color_string = "{!%s,%s,%s!}" % (fg, bg, attr)
|
||||||
else:
|
else:
|
||||||
color_string = "{!%s,%s!}"%(fg, bg)
|
color_string = "{!%s,%s!}" % (fg, bg)
|
||||||
|
|
||||||
#actually draw the dir/file string
|
#actually draw the dir/file string
|
||||||
if fl[3] and fl[4]: # this is an expanded directory
|
if fl[3] and fl[4]: # this is an expanded directory
|
||||||
xchar = 'v'
|
xchar = "v"
|
||||||
elif fl[3]: # collapsed directory
|
elif fl[3]: # collapsed directory
|
||||||
xchar = '>'
|
xchar = ">"
|
||||||
else: # file
|
else: # file
|
||||||
xchar = '-'
|
xchar = "-"
|
||||||
|
|
||||||
r = format_utils.format_row(["%s%s %s"%(" "*depth, xchar, fl[0]),
|
r = format_utils.format_row(["%s%s %s" % (" " * depth, xchar, fl[0]),
|
||||||
deluge.common.fsize(fl[2]), fl[5],
|
fsize(fl[2]), fl[5],
|
||||||
format_utils.format_priority(fl[6])],
|
format_utils.format_priority(fl[6])],
|
||||||
self.column_widths)
|
self.column_widths)
|
||||||
|
|
||||||
self.add_string(off, "%s%s"%(color_string, r), trim=False)
|
self.add_string(off, "%s%s" % (color_string, r), trim=False)
|
||||||
off += 1
|
off += 1
|
||||||
|
|
||||||
if fl[3] and fl[4]:
|
if fl[3] and fl[4]:
|
||||||
# recurse if we have children and are expanded
|
# recurse if we have children and are expanded
|
||||||
off, idx = self.draw_files(fl[3], depth+1, off, idx+1)
|
off, idx = self.draw_files(fl[3], depth + 1, off, idx + 1)
|
||||||
if off < 0: return (off, idx)
|
if off < 0:
|
||||||
|
return (off, idx)
|
||||||
else:
|
else:
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
|
@ -391,7 +367,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
"""
|
"""
|
||||||
Counts length of the displayed file list.
|
Counts length of the displayed file list.
|
||||||
"""
|
"""
|
||||||
if file_list == None:
|
if file_list is None:
|
||||||
file_list = self.file_list
|
file_list = self.file_list
|
||||||
length = 0
|
length = 0
|
||||||
if file_list:
|
if file_list:
|
||||||
|
@ -401,16 +377,16 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
length += self.__get_file_list_length(element[3])
|
length += self.__get_file_list_length(element[3])
|
||||||
return length
|
return length
|
||||||
|
|
||||||
def __get_contained_files_count(self, file_list=None, idx = None):
|
def __get_contained_files_count(self, file_list=None, idx=None):
|
||||||
length = 0
|
length = 0
|
||||||
if file_list == None:
|
if file_list is None:
|
||||||
file_list = self.file_list
|
file_list = self.file_list
|
||||||
if idx != None:
|
if idx is not None:
|
||||||
for element in file_list:
|
for element in file_list:
|
||||||
if element[1] == idx:
|
if element[1] == idx:
|
||||||
return self.__get_contained_files_count(file_list = element[3])
|
return self.__get_contained_files_count(file_list=element[3])
|
||||||
elif element[3]:
|
elif element[3]:
|
||||||
c = self.__get_contained_files_count(file_list = element[3], idx=idx)
|
c = self.__get_contained_files_count(file_list=element[3], idx=idx)
|
||||||
if c > 0:
|
if c > 0:
|
||||||
return c
|
return c
|
||||||
else:
|
else:
|
||||||
|
@ -443,67 +419,80 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
#Name
|
#Name
|
||||||
s = "{!info!}Name: {!input!}%s" % status["name"]
|
s = "{!info!}Name: {!input!}%s" % status["name"]
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Print DL info and ETA
|
#Print DL info and ETA
|
||||||
if status["download_payload_rate"] > 0:
|
if status["download_payload_rate"] > 0:
|
||||||
s = "%sDownloading: {!input!}" % down_color
|
s = "%sDownloading: {!input!}" % down_color
|
||||||
else:
|
else:
|
||||||
s = "{!info!}Downloaded: {!input!}"
|
s = "{!info!}Downloaded: {!input!}"
|
||||||
s+= common.fsize(status["all_time_download"])
|
s += fsize(status["all_time_download"])
|
||||||
if status["progress"] != 100.0:
|
if status["progress"] != 100.0:
|
||||||
s+= "/%s" % common.fsize(status["total_wanted"])
|
s += "/%s" % fsize(status["total_wanted"])
|
||||||
if status["download_payload_rate"] > 0:
|
if status["download_payload_rate"] > 0:
|
||||||
s+= " {!yellow!}@ %s%s" % (down_color, common.fsize(status["download_payload_rate"]))
|
s += " {!yellow!}@ %s%s" % (down_color, fsize(status["download_payload_rate"]))
|
||||||
s+= "{!info!} ETA: {!input!}%s" % format_utils.format_time(status["eta"])
|
s += "{!info!} ETA: {!input!}%s" % format_utils.format_time(status["eta"])
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Print UL info and ratio
|
#Print UL info and ratio
|
||||||
if status["upload_payload_rate"] > 0:
|
if status["upload_payload_rate"] > 0:
|
||||||
s = "%sUploading: {!input!}" % up_color
|
s = "%sUploading: {!input!}" % up_color
|
||||||
else:
|
else:
|
||||||
s = "{!info!}Uploaded: {!input!}"
|
s = "{!info!}Uploaded: {!input!}"
|
||||||
s+= common.fsize(status["total_uploaded"])
|
s += fsize(status["total_uploaded"])
|
||||||
if status["upload_payload_rate"] > 0:
|
if status["upload_payload_rate"] > 0:
|
||||||
s+= " {!yellow!}@ %s%s" % (up_color, common.fsize(status["upload_payload_rate"]))
|
s += " {!yellow!}@ %s%s" % (up_color, fsize(status["upload_payload_rate"]))
|
||||||
ratio_str = format_utils.format_float(status["ratio"])
|
ratio_str = format_utils.format_float(status["ratio"])
|
||||||
if ratio_str == "-": ratio_str = "inf"
|
if ratio_str == "-":
|
||||||
s+= " {!info!}Ratio: {!input!}%s" % ratio_str
|
ratio_str = "inf"
|
||||||
self.add_string(off, s); off += 1
|
s += " {!info!}Ratio: {!input!}%s" % ratio_str
|
||||||
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Seed/peer info
|
#Seed/peer info
|
||||||
s = "{!info!}Seeds:{!green!} %s {!input!}(%s)" % (status["num_seeds"], status["total_seeds"])
|
s = "{!info!}Seeds:{!green!} %s {!input!}(%s)" % (status["num_seeds"], status["total_seeds"])
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
s = "{!info!}Peers:{!red!} %s {!input!}(%s)" % (status["num_peers"], status["total_peers"])
|
s = "{!info!}Peers:{!red!} %s {!input!}(%s)" % (status["num_peers"], status["total_peers"])
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Tracker
|
#Tracker
|
||||||
if status["message"] == "OK":
|
if status["message"] == "OK":
|
||||||
color = "{!green!}"
|
color = "{!green!}"
|
||||||
else:
|
else:
|
||||||
color = "{!red!}"
|
color = "{!red!}"
|
||||||
s = "{!info!}Tracker: {!magenta!}%s{!input!} says \"%s%s{!input!}\"" % (status["tracker_host"], color, status["message"])
|
s = "{!info!}Tracker: {!magenta!}%s{!input!} says \"%s%s{!input!}\"" % (
|
||||||
self.add_string(off, s); off += 1
|
status["tracker_host"], color, status["message"])
|
||||||
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Pieces and availability
|
#Pieces and availability
|
||||||
s = "{!info!}Pieces: {!yellow!}%s {!input!}x {!yellow!}%s" % (status["num_pieces"], common.fsize(status["piece_length"]))
|
s = "{!info!}Pieces: {!yellow!}%s {!input!}x {!yellow!}%s" % (
|
||||||
|
status["num_pieces"], fsize(status["piece_length"]))
|
||||||
if status["distributed_copies"]:
|
if status["distributed_copies"]:
|
||||||
s+= " {!info!}Availability: {!input!}%s" % format_utils.format_float(status["distributed_copies"])
|
s += " {!info!}Availability: {!input!}%s" % format_utils.format_float(status["distributed_copies"])
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Time added
|
#Time added
|
||||||
s = "{!info!}Added: {!input!}%s" % common.fdate(status["time_added"])
|
s = "{!info!}Added: {!input!}%s" % fdate(status["time_added"])
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Time active
|
#Time active
|
||||||
s = "{!info!}Time active: {!input!}%s" % ( common.ftime(status["active_time"]) )
|
s = "{!info!}Time active: {!input!}%s" % (ftime(status["active_time"]))
|
||||||
if status["seeding_time"]:
|
if status["seeding_time"]:
|
||||||
s+= ", {!cyan!}%s{!input!} seeding" % ( common.ftime(status["seeding_time"]) )
|
s += ", {!cyan!}%s{!input!} seeding" % (ftime(status["seeding_time"]))
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Download Folder
|
#Download Folder
|
||||||
s = "{!info!}Download Folder: {!input!}%s" % status["download_location"]
|
s = "{!info!}Download Folder: {!input!}%s" % status["download_location"]
|
||||||
self.add_string(off, s); off += 1
|
self.add_string(off, s)
|
||||||
|
off += 1
|
||||||
|
|
||||||
#Owner
|
#Owner
|
||||||
if status["owner"]:
|
if status["owner"]:
|
||||||
|
@ -511,9 +500,9 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
return off
|
return off
|
||||||
|
|
||||||
def refresh(self,lines=None):
|
def refresh(self, lines=None):
|
||||||
# show a message popup if there's anything queued
|
# show a message popup if there's anything queued
|
||||||
if self.popup == None and self.messages:
|
if self.popup is None and self.messages:
|
||||||
title, msg = self.messages.popleft()
|
title, msg = self.messages.popleft()
|
||||||
self.popup = MessagePopup(self, title, msg)
|
self.popup = MessagePopup(self, title, msg)
|
||||||
|
|
||||||
|
@ -527,7 +516,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
string = self.statusbars.bottombar
|
string = self.statusbars.bottombar
|
||||||
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help"
|
||||||
|
|
||||||
string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr
|
||||||
|
|
||||||
self.add_string(self.rows - 1, string)
|
self.add_string(self.rows - 1, string)
|
||||||
except:
|
except:
|
||||||
|
@ -579,13 +568,13 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
if self.current_file_idx > maxlen:
|
if self.current_file_idx > maxlen:
|
||||||
self.current_file_idx = maxlen
|
self.current_file_idx = maxlen
|
||||||
|
|
||||||
if self.current_file_idx > self.file_off + (self._listing_space - 3):
|
if self.current_file_idx > self.file_off + (self._listing_space - 3):
|
||||||
self.file_off = self.current_file_idx - (self._listing_space - 3)
|
self.file_off = self.current_file_idx - (self._listing_space - 3)
|
||||||
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def file_list_up(self, rows=1):
|
def file_list_up(self, rows=1):
|
||||||
self.current_file_idx = max(0, self.current_file_idx-rows)
|
self.current_file_idx = max(0, self.current_file_idx - rows)
|
||||||
self.file_off = min(self.file_off, self.current_file_idx)
|
self.file_off = min(self.file_off, self.current_file_idx)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
@ -605,7 +594,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
#Do not set priorities for the whole dir, just selected contents
|
#Do not set priorities for the whole dir, just selected contents
|
||||||
if f[3]:
|
if f[3]:
|
||||||
self.build_prio_list(f[3], ret_list, parent_prio, selected_prio)
|
self.build_prio_list(f[3], ret_list, parent_prio, selected_prio)
|
||||||
else: # file, need to add to list
|
else: # file, need to add to list
|
||||||
if f[1] in self.marked or parent_prio >= 0:
|
if f[1] in self.marked or parent_prio >= 0:
|
||||||
# selected (or parent selected), use requested priority
|
# selected (or parent selected), use requested priority
|
||||||
ret_list.append((f[1], selected_prio))
|
ret_list.append((f[1], selected_prio))
|
||||||
|
@ -631,10 +620,10 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
func = lambda idx, data, we=was_empty: self.do_priority(idx, data, we)
|
func = lambda idx, data, we=was_empty: self.do_priority(idx, data, we)
|
||||||
if self.marked:
|
if self.marked:
|
||||||
self.popup = SelectablePopup(self, "Set File Priority", func)
|
self.popup = SelectablePopup(self, "Set File Priority", func)
|
||||||
self.popup.add_line("_Do Not Download", data=deluge.common.FILE_PRIORITY["Do Not Download"], foreground="red")
|
self.popup.add_line("_Do Not Download", data=FILE_PRIORITY["Do Not Download"], foreground="red")
|
||||||
self.popup.add_line("_Normal Priority", data=deluge.common.FILE_PRIORITY["Normal Priority"])
|
self.popup.add_line("_Normal Priority", data=FILE_PRIORITY["Normal Priority"])
|
||||||
self.popup.add_line("_High Priority", data=deluge.common.FILE_PRIORITY["High Priority"], foreground="yellow")
|
self.popup.add_line("_High Priority", data=FILE_PRIORITY["High Priority"], foreground="yellow")
|
||||||
self.popup.add_line("H_ighest Priority", data=deluge.common.FILE_PRIORITY["Highest Priority"], foreground="green")
|
self.popup.add_line("H_ighest Priority", data=FILE_PRIORITY["Highest Priority"], foreground="green")
|
||||||
self.popup._selected = 1
|
self.popup._selected = 1
|
||||||
|
|
||||||
def __mark_unmark(self, idx):
|
def __mark_unmark(self, idx):
|
||||||
|
@ -652,7 +641,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
#Selected, unselect it
|
#Selected, unselect it
|
||||||
self.__unmark_tree(self.file_list, idx)
|
self.__unmark_tree(self.file_list, idx)
|
||||||
|
|
||||||
def __mark_tree(self, file_list, idx, mark_all = False):
|
def __mark_tree(self, file_list, idx, mark_all=False):
|
||||||
"""
|
"""
|
||||||
Given file_list of TorrentDetail and index of file or folder,
|
Given file_list of TorrentDetail and index of file or folder,
|
||||||
recursively selects all files contained
|
recursively selects all files contained
|
||||||
|
@ -687,7 +676,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
return total_marked
|
return total_marked
|
||||||
|
|
||||||
def __get_file_by_num(self, num, file_list, idx = 0):
|
def __get_file_by_num(self, num, file_list, idx=0):
|
||||||
for element in file_list:
|
for element in file_list:
|
||||||
if idx == num:
|
if idx == num:
|
||||||
return element
|
return element
|
||||||
|
@ -703,7 +692,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
def __get_file_by_name(self, name, file_list, idx = 0):
|
def __get_file_by_name(self, name, file_list, idx=0):
|
||||||
for element in file_list:
|
for element in file_list:
|
||||||
if element[0].strip("/") == name.strip("/"):
|
if element[0].strip("/") == name.strip("/"):
|
||||||
return element
|
return element
|
||||||
|
@ -719,7 +708,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
def __unmark_tree(self, file_list, idx, unmark_all = False):
|
def __unmark_tree(self, file_list, idx, unmark_all=False):
|
||||||
"""
|
"""
|
||||||
Given file_list of TorrentDetail and index of file or folder,
|
Given file_list of TorrentDetail and index of file or folder,
|
||||||
recursively deselects all files contained
|
recursively deselects all files contained
|
||||||
|
@ -756,8 +745,9 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
total_marked += marked
|
total_marked += marked
|
||||||
return total_marked
|
return total_marked
|
||||||
|
|
||||||
def _selection_to_file_idx(self, file_list = None, idx = 0, true_idx = 0, closed=False):
|
def _selection_to_file_idx(self, file_list=None, idx=0, true_idx=0, closed=False):
|
||||||
if not file_list: file_list = self.file_list
|
if not file_list:
|
||||||
|
file_list = self.file_list
|
||||||
|
|
||||||
for element in file_list:
|
for element in file_list:
|
||||||
if idx == self.current_file_idx:
|
if idx == self.current_file_idx:
|
||||||
|
@ -782,8 +772,9 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
|
|
||||||
return (idx, true_idx)
|
return (idx, true_idx)
|
||||||
|
|
||||||
def _get_full_folder_path(self, num, file_list = None, path = "", idx = 0):
|
def _get_full_folder_path(self, num, file_list=None, path="", idx=0):
|
||||||
if not file_list: file_list = self.file_list
|
if not file_list:
|
||||||
|
file_list = self.file_list
|
||||||
|
|
||||||
for element in file_list:
|
for element in file_list:
|
||||||
if not element[3]:
|
if not element[3]:
|
||||||
|
@ -794,7 +785,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
return "%s%s/" % (path, element[0])
|
return "%s%s/" % (path, element[0])
|
||||||
|
|
||||||
if element[4]:
|
if element[4]:
|
||||||
i = self._get_full_folder_path(num, element[3], path + element[0] + "/", idx + 1 )
|
i = self._get_full_folder_path(num, element[3], path + element[0] + "/", idx + 1)
|
||||||
if not isinstance(i, int):
|
if not isinstance(i, int):
|
||||||
return i
|
return i
|
||||||
else:
|
else:
|
||||||
|
@ -863,7 +854,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
return
|
return
|
||||||
|
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'Q':
|
if chr(c) == "Q":
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
if client.connected():
|
if client.connected():
|
||||||
def on_disconnect(result):
|
def on_disconnect(result):
|
||||||
|
@ -872,7 +863,7 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
return
|
return
|
||||||
elif chr(c) == 'q':
|
elif chr(c) == "q":
|
||||||
self.back_to_overview()
|
self.back_to_overview()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -888,14 +879,14 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
if c == curses.KEY_UP:
|
if c == curses.KEY_UP:
|
||||||
self.file_list_up()
|
self.file_list_up()
|
||||||
elif c == curses.KEY_PPAGE:
|
elif c == curses.KEY_PPAGE:
|
||||||
self.file_list_up(self._listing_space-2)
|
self.file_list_up(self._listing_space - 2)
|
||||||
elif c == curses.KEY_HOME:
|
elif c == curses.KEY_HOME:
|
||||||
self.file_off = 0
|
self.file_off = 0
|
||||||
self.current_file_idx = 0
|
self.current_file_idx = 0
|
||||||
elif c == curses.KEY_DOWN:
|
elif c == curses.KEY_DOWN:
|
||||||
self.file_list_down()
|
self.file_list_down()
|
||||||
elif c == curses.KEY_NPAGE:
|
elif c == curses.KEY_NPAGE:
|
||||||
self.file_list_down(self._listing_space-2)
|
self.file_list_down(self._listing_space - 2)
|
||||||
elif c == curses.KEY_END:
|
elif c == curses.KEY_END:
|
||||||
self.current_file_idx = self.__get_file_list_length() - 1
|
self.current_file_idx = self.__get_file_list_length() - 1
|
||||||
self.file_off = self.current_file_idx - (self._listing_space - 3)
|
self.file_off = self.current_file_idx - (self._listing_space - 3)
|
||||||
|
@ -912,24 +903,24 @@ class TorrentDetail(BaseMode, component.Component):
|
||||||
self.expcol_cur_file()
|
self.expcol_cur_file()
|
||||||
else:
|
else:
|
||||||
if c > 31 and c < 256:
|
if c > 31 and c < 256:
|
||||||
if chr(c) == 'm':
|
if chr(c) == "m":
|
||||||
if self.current_file:
|
if self.current_file:
|
||||||
self.__mark_unmark(self.current_file[1])
|
self.__mark_unmark(self.current_file[1])
|
||||||
elif chr(c) == 'r':
|
elif chr(c) == "r":
|
||||||
self._show_rename_popup()
|
self._show_rename_popup()
|
||||||
elif chr(c) == 'c':
|
elif chr(c) == "c":
|
||||||
self.marked = {}
|
self.marked = {}
|
||||||
elif chr(c) == 'a':
|
elif chr(c) == "a":
|
||||||
torrent_actions_popup(self, [self.torrentid], details=False)
|
torrent_actions_popup(self, [self.torrentid], details=False)
|
||||||
return
|
return
|
||||||
elif chr(c) == 'o':
|
elif chr(c) == "o":
|
||||||
torrent_actions_popup(self, [self.torrentid], action=ACTION.TORRENT_OPTIONS)
|
torrent_actions_popup(self, [self.torrentid], action=ACTION.TORRENT_OPTIONS)
|
||||||
return
|
return
|
||||||
elif chr(c) == 'h':
|
elif chr(c) == "h":
|
||||||
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
|
||||||
elif chr(c) == 'j':
|
elif chr(c) == "j":
|
||||||
self.file_list_up()
|
self.file_list_up()
|
||||||
if chr(c) == 'k':
|
if chr(c) == "k":
|
||||||
self.file_list_down()
|
self.file_list_down()
|
||||||
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# statusbars.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
#
|
||||||
# Deluge is free software.
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
#
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
# See LICENSE for more details.
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge 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 deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor
|
|
||||||
# Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
# In addition, as a special exception, the copyright holders give
|
|
||||||
# permission to link the code of portions of this program with the OpenSSL
|
|
||||||
# library.
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
#
|
#
|
||||||
|
|
||||||
import deluge.common
|
import deluge.common
|
||||||
|
|
Loading…
Reference in New Issue