[Console] Flake8 all files

This commit is contained in:
Calum Lind 2014-09-18 13:21:29 +01:00
parent a68c3140af
commit 45ef6ac56d
39 changed files with 1300 additions and 2184 deletions

View File

@ -34,3 +34,4 @@
#
UI_PATH = __path__[0]
from main import start
assert start # silence pyflakes

View File

@ -1,36 +1,10 @@
#
# colors.py
# -*- coding: utf-8 -*-
#
# 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 deluge.ui.console.modes import format_utils
@ -41,14 +15,14 @@ except ImportError:
pass
colors = [
'COLOR_BLACK',
'COLOR_BLUE',
'COLOR_CYAN',
'COLOR_GREEN',
'COLOR_MAGENTA',
'COLOR_RED',
'COLOR_WHITE',
'COLOR_YELLOW'
"COLOR_BLACK",
"COLOR_BLUE",
"COLOR_CYAN",
"COLOR_GREEN",
"COLOR_MAGENTA",
"COLOR_RED",
"COLOR_WHITE",
"COLOR_YELLOW"
]
# {(fg, bg): pair_number, ...}
@ -92,6 +66,7 @@ type_color = {
dict: "{!white,black,bold!}"
}
def init_colors():
# Create the color_pairs dict
counter = 1
@ -111,9 +86,11 @@ def init_colors():
except:
pass
class BadColorString(Exception):
pass
def replace_tabs(line):
"""
Returns a string with tabs replaced with spaces.
@ -124,6 +101,7 @@ def replace_tabs(line):
line = line.replace("\t", " " * tab_length, 1)
return line
def strip_colors(line):
"""
Returns a string with the color formatting removed.
@ -135,6 +113,7 @@ def strip_colors(line):
return line
def get_line_length(line, encoding="UTF-8"):
"""
Returns the string length without the color formatting.
@ -153,6 +132,7 @@ def get_line_length(line, encoding="UTF-8"):
line = replace_tabs(line)
return len(line)
def get_line_width(line, encoding="UTF-8"):
"""
Get width of string considering double width characters
@ -171,6 +151,7 @@ def get_line_width(line, encoding="UTF-8"):
line = replace_tabs(line)
return format_utils.strwidth(line)
def parse_color_string(s, encoding="UTF-8"):
"""
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 = []
# Keep track of where the strings
col_index = 0
while s.find("{!") != -1:
begin = s.find("{!")
if begin > 0:
@ -200,7 +180,7 @@ def parse_color_string(s, encoding="UTF-8"):
# Get a list of attributes in the bracketed section
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 {! !}")
def apply_attrs(cp, a):

View File

@ -1,40 +1,15 @@
#
# commander.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 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.
#
from __future__ import print_function
import logging
@ -68,7 +43,7 @@ class Commander:
"""
if not cmd:
return
cmd, _, line = cmd.partition(' ')
cmd, _, line = cmd.partition(" ")
try:
parser = self._commands[cmd].create_parser()
except KeyError:
@ -103,7 +78,7 @@ class Commander:
self.write("{!error!}Error parsing options: %s" % ex)
return
if not getattr(options, '_exit', False):
if not getattr(options, "_exit", False):
try:
ret = self._commands[cmd].handle(*args, **options.__dict__)
except Exception as ex:
@ -119,7 +94,7 @@ class Commander:
commands = []
if args:
# 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_started(result):
@ -156,8 +131,8 @@ class Commander:
if not self.interactive:
if commands[0].startswith("connect"):
d = self.do_command(commands.pop(0))
elif 'help' in commands:
self.do_command('help')
elif "help" in commands:
self.do_command("help")
sys.exit(0)
d.addCallback(on_connect)
d.addErrback(on_connect_fail)

View File

@ -1,39 +1,13 @@
#
# cache.py
# -*- coding: utf-8 -*-
#
# 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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -41,6 +15,7 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Show information about the disk cache"""
usage = "Usage: cache"
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")

View File

@ -1,42 +1,15 @@
#
# config.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import cStringIO
import logging
import re
import tokenize
from optparse import make_option
@ -49,6 +22,7 @@ from deluge.ui.console.main import BaseCommand
log = logging.getLogger(__name__)
def atom(next, token):
"""taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
if token[1] == "(":
@ -71,15 +45,16 @@ def atom(next, token):
return float(token[-1])
except ValueError:
return str(token[-1])
elif token[1].lower() == 'true':
elif token[1].lower() == "true":
return True
elif token[1].lower() == 'false':
elif token[1].lower() == "false":
return False
elif token[0] is tokenize.STRING or token[1] == "/":
return token[-1].decode("string-escape")
raise SyntaxError("malformed expression (%s)" % token[1])
def simple_eval(source):
""" evaluates the 'source' string into a combination of primitive python objects
taken from http://effbot.org/zone/simple-iterator-parser.htm"""
@ -94,21 +69,19 @@ class Command(BaseCommand):
"""Show and set configuration values"""
option_list = BaseCommand.option_list + (
make_option('-s', '--set', action='store', nargs=2, dest='set',
help='set value for key'),
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
)
usage = "Usage: config [key1 [key2 ...]]\n"\
" config --set key value"
usage = """Usage: config [key1 [key2 ...]]"
config --set key value"""
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")
if options['set']:
if options["set"]:
return self._set_config(*args, **options)
else:
return self._get_config(*args, **options)
def _get_config(self, *args, **options):
deferred = defer.Deferred()
config = component.get("CoreConfig")
keys = config.keys()
keys.sort()

View File

@ -1,40 +1,14 @@
#
# connect.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -55,6 +29,7 @@ class Command(BaseCommand):
def do_connect():
d = client.connect(host, port, username, password)
def on_connect(result):
if self.console.interactive:
self.console.write("{!success!}Connected to %s:%s!" % (host, port))

View File

@ -1,55 +1,28 @@
#
# debug.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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 defer
import deluge.component as component
import deluge.log
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Enable and disable debugging"""
usage = 'Usage: debug [on|off]'
def handle(self, state='', **options):
if state == 'on':
usage = "Usage: debug [on|off]"
def handle(self, state="", **options):
if state == "on":
deluge.log.setLoggerLevel("debug")
elif state == 'off':
elif state == "off":
deluge.log.setLoggerLevel("error")
else:
component.get("ConsoleUI").write("{!error!}%s" % self.usage)
@ -57,4 +30,4 @@ class Command(BaseCommand):
return defer.succeed(True)
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)]

View File

@ -1,38 +1,12 @@
#
# status.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 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.
#
import deluge.common
import deluge.component as component
from deluge.ui.console.main import BaseCommand
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"""
usage = "Usage: gui"
interactive_only = True
def handle(self, *args, **options):
console = component.get("ConsoleUI")
try:

View File

@ -1,40 +1,14 @@
#
# halt.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -42,6 +16,7 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"Shutdown the deluge server"
usage = "Usage: halt"
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")

View File

@ -1,42 +1,16 @@
# help.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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 defer
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.console.main import BaseCommand
@ -60,14 +34,14 @@ class Command(BaseCommand):
parser = cmd.create_parser()
self.console.write(parser.format_help())
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(" ")
else:
self.console.set_batch_write(True)
for cmd in sorted(self._commands):
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
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)
return deferred

View File

@ -1,37 +1,11 @@
#
# info.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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 optparse import make_option
@ -47,7 +21,8 @@ from deluge.ui.console.modes import format_utils
strwidth = format_utils.strwidth
STATUS_KEYS = ["state",
STATUS_KEYS = [
"state",
"download_location",
"tracker_host",
"tracker_status",
@ -80,6 +55,7 @@ STATUS_KEYS = ["state",
# Add filter specific state to torrent states
STATES = ["Active"] + common.TORRENT_STATE
def format_progressbar(progress, width):
"""
Returns a string of a progress bar.
@ -112,31 +88,29 @@ def format_time(seconds):
class Command(BaseCommand):
"""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 + (
make_option('-v', '--verbose', action='store_true', default=False, dest='verbose',
help='shows more information per torrent'),
make_option('-d', '--detailed', action='store_true', default=False, dest='detailed',
help='shows detailed information about files and peers. '
"Implies -v"),
make_option('-s', '--state', action='store', dest='state',
help="show torrents with state STATE. "
"Possible values are: %s"%(", ".join(STATES))),
make_option('--sort', action='store', type='string', default='', dest='sort',
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.')
make_option("-v", "--verbose", action="store_true", default=False, dest="verbose",
help="Show more information per torrent."),
make_option("-d", "--detailed", action="store_true", default=False, dest="detailed",
help="Show more detailed information including files and peers."),
make_option("-s", "--state", action="store", dest="state",
help="Show torrents with state STATE: %s." % (", ".join(STATES))),
make_option("--sort", action="store", type="string", default="", dest="sort", help=sort_help),
make_option("--sort-reverse", action="store", type="string", default="", dest="sort_rev",
help="Same as --sort but items are in reverse order.")
)
usage = "Usage: info [-v | -d | -s <state>] [<torrent-id> [<torrent-id> ...]]\n"\
" You can give the first few characters of a torrent-id to identify the torrent.\n"\
" info * will list all torrents.\n\n"\
" Tab Completion (info *pattern*<tab>):\n"\
" | First press of <tab> will output up to 15 matches;\n"\
" | hitting <tab> a second time, will print 15 more matches; \n"\
" | and a third press will print all remaining matches.\n"\
" | (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)"
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.
info * will list all torrents.
Tab Completion (info *pattern*<tab>):
| First press of <tab> will output up to 15 matches;
| hitting <tab> a second time, will print 15 more matches;
| 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):
self.console = component.get("ConsoleUI")
@ -150,22 +124,20 @@ class Command(BaseCommand):
def on_torrents_status(status):
# Print out the information for each torrent
sort_key = options['sort']
sort_key = options["sort"]
sort_reverse = False
if not sort_key:
sort_key = options['sort_rev']
sort_key = options["sort_rev"]
sort_reverse = True
if not sort_key:
sort_key = 'name'
sort_key = "name"
sort_reverse = False
if sort_key not in status_keys:
self.console.write('')
if sort_key not in STATUS_KEYS:
self.console.write("")
self.console.write("{!error!}Unknown sort key: " + sort_key + ", will sort on name")
sort_key = 'name'
sort_key = "name"
sort_reverse = False
for key, value in sorted(status.items(),
key=lambda x : x[1].get(sort_key),
reverse=sort_reverse):
for key, value in sorted(status.items(), key=lambda x: x[1].get(sort_key), reverse=sort_reverse):
self.show_info(key, status[key], options["verbose"], options["detailed"])
def on_torrents_status_fail(reason):
@ -188,21 +160,20 @@ class Command(BaseCommand):
return d
def show_file_info(self, torrent_id, status):
SPACES_PER_LEVEL = 2
spaces_per_level = 2
if hasattr(self.console, "screen"):
cols = self.console.screen.cols
else:
cols = 80
dirs = []
prevpath = []
for i, file in enumerate(status["files"]):
filename = file["path"].split(dirsep)[-1]
filepath = file["path"].split(dirsep)[:-1]
for depth, subdir in enumerate(filepath):
indent = " "*depth*SPACES_PER_LEVEL
indent = " " * depth * spaces_per_level
if depth >= len(prevpath):
self.console.write("%s{!cyan!}%s" % (indent, subdir))
elif subdir != prevpath[depth]:
@ -210,7 +181,7 @@ class Command(BaseCommand):
depth = len(filepath)
indent = " " * depth * SPACES_PER_LEVEL
indent = " " * depth * spaces_per_level
col_filename = indent + filename
col_size = " ({!cyan!}%s{!input!})" % common.fsize(file["size"])
@ -232,7 +203,7 @@ class Command(BaseCommand):
tlen = lambda s: strwidth(rf(s))
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
#Check how much space we've got left after writing all the info
@ -262,7 +233,6 @@ class Command(BaseCommand):
col_filename = format_utils.trim_string(col_filename, space_left, True)
self.console.write(col_filename + col_all_info)
prevpath = filepath
def show_peer_info(self, torrent_id, status):

View File

@ -1,48 +1,18 @@
# -*- coding: utf-8 -*-
#
# manage.py
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import re
from optparse import make_option
from twisted.internet import defer
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.console.main import BaseCommand
@ -66,8 +36,7 @@ class Command(BaseCommand):
"""Show and manage per-torrent options"""
option_list = BaseCommand.option_list + (
make_option('-s', '--set', action='store', nargs=2, dest='set',
help='set value for key'),
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
)
usage = "Usage: manage <torrent-id> [<key1> [<key2> ...]]\n"\
" manage <torrent-id> --set <key> <value>"
@ -79,7 +48,6 @@ class Command(BaseCommand):
else:
return self._get_option(*args, **options)
def _get_option(self, *args, **options):
def on_torrents_status(status):
@ -113,7 +81,6 @@ class Command(BaseCommand):
d.addErrback(on_torrents_status_fail)
return d
def _set_option(self, *args, **options):
deferred = defer.Deferred()
torrent_ids = []
@ -133,7 +100,6 @@ class Command(BaseCommand):
self.console.write("Setting %s to %s for torrents %s.." % (key, val, torrent_ids))
for tid in torrent_ids:
if key == "move_on_completed_path":
client.core.set_torrent_move_completed_path(tid, val).addCallback(on_set_config)

View File

@ -1,36 +1,10 @@
#
# move.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 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.
#
import os.path

View File

@ -1,40 +1,14 @@
#
# pause.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -42,6 +16,7 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Pause a torrent"""
usage = "Usage: pause [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")

View File

@ -1,43 +1,16 @@
#
# plugin.py
# -*- coding: utf-8 -*-
#
# 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
import deluge.component as component
import deluge.configmanager
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -45,25 +18,25 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Manage plugins with this command"""
option_list = BaseCommand.option_list + (
make_option('-l', '--list', action='store_true', default=False, dest='list',
help='lists available plugins'),
make_option('-s', '--show', action='store_true', default=False, dest='show',
help='shows enabled plugins'),
make_option('-e', '--enable', dest='enable',
help='enables a plugin'),
make_option('-d', '--disable', dest='disable',
help='disables a plugin'),
make_option('-r', '--reload', action='store_true', default=False, dest='reload',
help='reload list of available plugins'),
make_option('-i', '--install', dest='plugin_file',
help='install a plugin from an .egg file'),
make_option("-l", "--list", action="store_true", default=False, dest="list",
help="Lists available plugins"),
make_option("-s", "--show", action="store_true", default=False, dest="show",
help="Shows enabled plugins"),
make_option("-e", "--enable", dest="enable",
help="Enables a plugin"),
make_option("-d", "--disable", dest="disable",
help="Disables a plugin"),
make_option("-r", "--reload", action="store_true", default=False, dest="reload",
help="Reload list of available plugins"),
make_option("-i", "--install", dest="plugin_file",
help="Install a plugin from an .egg file"),
)
usage = "Usage: plugin [ -l | --list ]\n"\
" plugin [ -s | --show ]\n"\
" plugin [ -e | --enable ] <plugin-name>\n"\
" plugin [ -d | --disable ] <plugin-name>\n"\
" plugin [ -i | --install ] <plugin-file>\n"\
" plugin [ -r | --reload]"
usage = """Usage: plugin [ -l | --list ]
plugin [ -s | --show ]
plugin [ -e | --enable ] <plugin-name>
plugin [ -d | --disable ] <plugin-name>
plugin [ -i | --install ] <plugin-file>
plugin [ -r | --reload]"""
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")
@ -131,7 +104,6 @@ class Command(BaseCommand):
self.console.write("{!error!}Invalid path: %s" % filepath)
return
config_dir = deluge.configmanager.get_config_dir()
filename = os.path.split(filepath)[1]
@ -153,6 +125,5 @@ class Command(BaseCommand):
self.console.write("{!green!}Plugin was successfully installed: %s" % filename)
def complete(self, line):
return component.get("ConsoleUI").tab_complete_path(line, ext=".egg", sort="name", dirs_first=-1)

View File

@ -1,38 +1,13 @@
#
# quit.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Coptright (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.
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
# 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 deluge.ui.client import client
@ -41,8 +16,9 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Exit from the client."""
aliases = ['exit']
aliases = ["exit"]
interactive_only = True
def handle(self, *args, **options):
if client.connected():
def on_disconnect(result):

View File

@ -1,39 +1,13 @@
#
# recheck.py
# -*- coding: utf-8 -*-
#
# 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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -41,13 +15,14 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Forces a recheck of the torrent data"""
usage = "Usage: recheck [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
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(""))
return

View File

@ -1,41 +1,14 @@
#
# resume.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -43,13 +16,14 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Resume a torrent"""
usage = "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
return
if len(args) > 0 and args[0] == '*':
if len(args) > 0 and args[0] == "*":
client.core.resume_session()
return

View File

@ -1,42 +1,16 @@
#
# rm.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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 optparse import make_option
import deluge.component as component
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -44,10 +18,10 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Remove a torrent"""
usage = "Usage: rm <torrent-id>"
aliases = ['del']
aliases = ["del"]
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"),
)
@ -61,7 +35,7 @@ class Command(BaseCommand):
torrent_ids.extend(self.console.match_torrent(arg))
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):
# We use the ConsoleUI torrent tab complete method

View File

@ -1,35 +1,10 @@
#
# status.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 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.
#
from optparse import make_option
@ -45,11 +20,11 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Shows a various status information from the daemon."""
option_list = BaseCommand.option_list + (
make_option('-r', '--raw', action='store_true', default=False, dest='raw',
help='Don\'t format upload/download rates in KiB/s \
(useful for scripts that want to do their own parsing)'),
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)'),
make_option("-r", "--raw", action="store_true", default=False, dest="raw",
help="Don't format upload/download rates in KiB/s \
(useful for scripts that want to do their own parsing)"),
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)"),
)
usage = "Usage: status [-r] [-n]"

View File

@ -1,42 +1,14 @@
#
# rm.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand

View File

@ -1,38 +1,11 @@
#
# eventlog.py
# -*- coding: utf-8 -*-
#
# 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 time

View File

@ -1,37 +1,11 @@
#
# main.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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 __future__ import print_function
@ -59,6 +33,7 @@ from deluge.ui.ui import _UI
log = logging.getLogger(__name__)
class Console(_UI):
help = """Starts the Deluge console interface"""
@ -74,7 +49,8 @@ class Console(_UI):
self.parser.add_option_group(group)
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):
def __init__(self, parser, title, description=None, cmds=None):
optparse.OptionGroup.__init__(self, parser, title, description)
@ -87,13 +63,14 @@ class Console(_UI):
result += "%s\n" % formatter.format_description(self.description)
for cname in self.cmds:
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.extend(cmd.aliases)
cname = "/".join(allnames)
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
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()
return result
@ -110,9 +87,11 @@ class Console(_UI):
ConsoleUI(self.args, self.console_cmds, (self.options.daemon_addr, self.options.daemon_port,
self.options.daemon_user, self.options.daemon_pass))
def start():
Console().start()
class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
"""
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
@ -181,6 +160,7 @@ class DelugeHelpFormatter (optparse.IndentedHelpFormatter):
result.append("\n")
return "".join(result)
class OptionParser(optparse.OptionParser):
"""subclass from optparse.OptionParser so exit() won't exit."""
def __init__(self, **kwargs):
@ -235,19 +215,20 @@ class OptionParser(optparse.OptionParser):
class BaseCommand(object):
usage = 'usage'
usage = "usage"
interactive_only = False
option_list = tuple()
aliases = []
def complete(self, text, *args):
return []
def handle(self, *args, **options):
pass
@property
def name(self):
return 'base'
return "base"
@property
def epilog(self):
@ -255,33 +236,30 @@ class BaseCommand(object):
def split(self, text):
if deluge.common.windows_check():
text = text.replace('\\', '\\\\')
text = text.replace("\\", "\\\\")
result = shlex.split(text)
for i, s in enumerate(result):
result[i] = s.replace(r'\ ', ' ')
result = filter(lambda s: s != '', result)
result[i] = s.replace(r"\ ", " ")
result = filter(lambda s: s != "", result)
return result
def create_parser(self):
return OptionParser(prog = self.name,
usage = self.usage,
epilog = self.epilog,
option_list = self.option_list)
return OptionParser(prog=self.name, usage=self.usage, epilog=self.epilog, option_list=self.option_list)
def load_commands(command_dir, exclude=[]):
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:
commands = []
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
if not (filename.endswith('.py') or filename.endswith('.pyc')):
if not (filename.endswith(".py") or filename.endswith(".pyc")):
continue
cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
aliases = [ filename.split('.')[len(filename.split('.')) - 2] ]
cmd = get_command(filename.split(".")[len(filename.split(".")) - 2])
aliases = [filename.split(".")[len(filename.split(".")) - 2]]
aliases.extend(cmd.aliases)
for a in aliases:
commands.append((a, cmd))
@ -298,14 +276,13 @@ class ConsoleUI(component.Component):
self.events = []
try:
locale.setlocale(locale.LC_ALL, '')
locale.setlocale(locale.LC_ALL, "")
self.encoding = locale.getpreferredencoding()
except:
self.encoding = sys.getdefaultencoding()
log.debug("Using encoding: %s", self.encoding)
# start up the session proxy
self.sessionproxy = SessionProxy()
@ -315,7 +292,7 @@ class ConsoleUI(component.Component):
self.interactive = True
self._commands = cmds
if args:
args = ' '.join(args)
args = " ".join(args)
self.interactive = False
if not cmds:
print("Sorry, couldn't find any commands")
@ -373,12 +350,12 @@ Please use commands from the command line, eg:\n
# Start the twisted mainloop
reactor.run()
def start(self):
# Maintain a list of (torrent_id, name) for use in tab completion
self.torrents = []
if not self.interactive:
self.started_deferred = defer.Deferred()
def on_session_state(result):
def on_torrents_status(torrents):
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_session_state().addCallback(on_session_state)
def match_torrent(self, string):
"""
Returns a list of torrent_id matches for the string. It will search both
@ -411,7 +387,6 @@ Please use commands from the command line, eg:\n
return matches
def get_torrent_name(self, torrent_id):
if self.interactive and hasattr(self.screen, "get_torrent_name"):
return self.screen.get_torrent_name(torrent_id)
@ -422,7 +397,6 @@ Please use commands from the command line, eg:\n
return None
def set_batch_write(self, batch):
if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy):
return self.screen.set_batch_write(batch)

View File

@ -1,56 +1,28 @@
# -*- 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) 2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2011 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.
#
import base64
import glob
import logging
import os
import deluge.common
import deluge.component as component
from deluge.ui.client import client
from deluge.ui.common import TorrentInfo
log = logging.getLogger(__name__)
def __bracket_fixup(path):
if (path.find("[") == -1 and
path.find("]") == -1):
if (path.find("[") == -1 and path.find("]") == -1):
return path
sentinal = 256
while (path.find(unichr(sentinal)) != -1):
@ -63,6 +35,7 @@ def __bracket_fixup(path):
newpath = newpath.replace(unichr(sentinal), "[]]")
return newpath
def add_torrent(t_file, options, success_cb, fail_cb, ress):
t_options = {}
if options["path"]:
@ -70,9 +43,9 @@ def add_torrent(t_file, options, success_cb, fail_cb, ress):
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_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]
else:
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:
if is_url:
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)
else:
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]
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)

View File

@ -1,37 +1,10 @@
# -*- coding: utf-8 -*-
#
# addtorrents.py
#
# Copyright (C) 2012 Arek Stefański <asmageddon@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.
#
import base64
@ -40,13 +13,11 @@ import os
import deluge.common as common
import deluge.component as component
import deluge.ui.console.colors as colors
import format_utils
from basemode import BaseMode
from deluge.ui.client import client
from deluge.ui.sessionproxy import SessionProxy
from input_popup import InputPopup
from popup import MessagePopup, Popup
from popup import MessagePopup
try:
import curses
@ -83,6 +54,7 @@ if a file is highlighted
{!info!}'q'{!normal!} - Go back to torrent overview
"""
class AddTorrents(BaseMode, component.Component):
def __init__(self, alltorrentmode, stdscr, console_config, encoding=None):
@ -118,7 +90,6 @@ class AddTorrents(BaseMode, component.Component):
self.__refresh_listing()
component.Component.__init__(self, "AddTorrents", 1, depend=["SessionProxy"])
component.start(["AddTorrents"])
@ -129,6 +100,7 @@ class AddTorrents(BaseMode, component.Component):
# component start/update
def start(self):
pass
def update(self):
pass
@ -321,8 +293,10 @@ class AddTorrents(BaseMode, component.Component):
s = ""
for i, (c, w) in enumerate(zip(cols, widths)):
cn = ""
if i == 0: cn = "name"
elif i == 2: cn = "date"
if i == 0:
cn = "name"
elif i == 2:
cn = "date"
if cn == self.sort_column:
s += "{!black,green,bold!}" + c.ljust(w - 2)
@ -423,10 +397,7 @@ class AddTorrents(BaseMode, component.Component):
def _show_add_dialog(self):
def _do_add(result):
ress = {"succ":0,
"fail":0,
"total": len(self.marked),
"fmsg":[]}
ress = {"succ": 0, "fail": 0, "total": len(self.marked), "fmsg": []}
def fail_cb(msg, t_file, ress):
log.debug("failed to add torrent: %s: %s" % (t_file, msg))
@ -485,7 +456,6 @@ class AddTorrents(BaseMode, component.Component):
self.popup.add_text_input("Download Folder:", "location", dl)
self.popup.add_select_input("Add Paused:", "add_paused", ["Yes", "No"], [True, False], ap)
def _go_up(self):
#Go up in directory hierarchy
if self.path_stack_pos > 1:
@ -508,7 +478,7 @@ class AddTorrents(BaseMode, component.Component):
return
if c > 31 and c < 256:
if chr(c) == 'Q':
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -517,7 +487,7 @@ class AddTorrents(BaseMode, component.Component):
else:
reactor.stop()
return
elif chr(c) == 'q':
elif chr(c) == "q":
self.back_to_overview()
return
@ -549,23 +519,23 @@ class AddTorrents(BaseMode, component.Component):
self.back_to_overview()
else:
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)
elif chr(c) == '>':
elif chr(c) == ">":
if self.sort_column == "date":
self.reverse_sort = not self.reverse_sort
else:
self.sort_column = "date"
self.reverse_sort = True
self.__sort_rows()
elif chr(c) == '<':
elif chr(c) == "<":
if self.sort_column == "name":
self.reverse_sort = not self.reverse_sort
else:
self.sort_column = "name"
self.reverse_sort = False
self.__sort_rows()
elif chr(c) == 'm':
elif chr(c) == "m":
s = self.raw_rows[self.cursel][0]
if s in self.marked:
self.marked.remove(s)
@ -573,11 +543,11 @@ class AddTorrents(BaseMode, component.Component):
self.marked.add(s)
self.last_mark = self.cursel
elif chr(c) == 'j':
elif chr(c) == "j":
self.scroll_list_up(1)
elif chr(c) == 'k':
elif chr(c) == "k":
self.scroll_list_down(1)
elif chr(c) == 'M':
elif chr(c) == "M":
if self.last_mark != -1:
if self.last_mark > self.cursel:
m = range(self.cursel, self.last_mark)

View File

@ -1,58 +1,27 @@
# -*- coding: utf-8 -*-
#
# alltorrents.py
#
# Copyright (C) 2011 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.
#
import logging
from collections import deque
from twisted.internet import defer
import column
import column as console_column
import deluge.common
import deluge.component as component
import format_utils
from add_util import add_torrent
from addtorrents import AddTorrents
from basemode import BaseMode
from deluge.configmanager import ConfigManager
from deluge.ui.client import client
from deluge.ui.sessionproxy import SessionProxy
from eventview import EventView
from input_popup import ALIGN, InputPopup
from input_popup import InputPopup
from legacy import Legacy
from popup import ALIGN, MessagePopup, Popup, SelectablePopup
from popup import MessagePopup, Popup, SelectablePopup
from preferences import Preferences
from torrent_actions import ACTION, torrent_actions_popup
from torrentdetail import TorrentDetail
@ -133,6 +102,7 @@ files in the torrent and the ability to set file priorities.
input something
"""
class FILTER:
ALL = 0
ACTIVE = 1
@ -300,6 +270,7 @@ SEARCH_SUCCESS = 2
SEARCH_START_REACHED = 3
SEARCH_END_REACHED = 4
class AllTorrents(BaseMode, component.Component):
def __init__(self, stdscr, encoding=None):
self.torrent_names = None
@ -376,25 +347,25 @@ class AllTorrents(BaseMode, component.Component):
# component start/update
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):
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:
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):
self.config = ConfigManager("console.conf", DEFAULT_PREFS)
s_primary = self.config["sort_primary"]
s_secondary = self.config["sort_secondary"]
self.__cols_to_show = [
pref for pref in column_pref_names
if ("show_%s" % pref) not in self.config
or self.config["show_%s"%pref]
]
self.__cols_to_show = [pref for pref in column_pref_names
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.__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
for rf in ["state", "name", "queue", "progress"]:
@ -506,16 +477,15 @@ class AllTorrents(BaseMode, component.Component):
if (self.popup):
self.popup.clear()
name = state["name"]
off = int((self.cols/4)-(len(name)/2))
self.popup.set_title(name)
for i, f in enumerate(self._info_fields):
if f[1] != None:
if f[1] is not None:
args = []
try:
for key in f[2]:
args.append(state[key])
except:
log.debug("Could not get info field: %s", e)
except Exception as ex:
log.debug("Could not get info field: %s", ex)
continue
info = f[1](*args)
else:
@ -535,7 +505,6 @@ class AllTorrents(BaseMode, component.Component):
else:
self.__torrent_info_id = None
def on_resize(self, *args):
BaseMode.on_resize_norefresh(self, *args)
if self.popup:
@ -575,6 +544,7 @@ class AllTorrents(BaseMode, component.Component):
cmp_func = self._queue_sort
sg = state.get
def sort_by_field(state, result, field):
if field in column_names_to_state_keys:
field = column_names_to_state_keys[field]
@ -650,14 +620,14 @@ class AllTorrents(BaseMode, component.Component):
def doprefs(arg):
if arg and True in arg[0]:
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:
self.messages.append(("Error", "An error occurred trying to display preferences"))
component.stop(["AllTorrents"]).addCallback(doprefs)
client.core.get_config().addCallback(_on_get_config)
def __show_events(self):
def doevents(arg):
if arg and True in arg[0]:
@ -804,7 +774,6 @@ class AllTorrents(BaseMode, component.Component):
self.popup.add_line("From _URL or Magnet", data=2)
self.popup.add_line("_Cancel", data=0)
def _do_set_column_visibility(self, data):
for key, value in data.items():
self.config[key] = value
@ -815,13 +784,9 @@ class AllTorrents(BaseMode, component.Component):
def _show_visible_columns_popup(self):
title = "Visible columns (Enter to exit)"
self.popup = InputPopup(self,
title,
close_cb=self._do_set_column_visibility,
immediate_action=True,
height_req= len(column_pref_names) + 1,
width_req= max([len(col) for col in column_pref_names + [title]]) + 8
)
self.popup = InputPopup(self, title, close_cb=self._do_set_column_visibility,
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:
name = prefs_to_names[col]
@ -854,7 +819,7 @@ class AllTorrents(BaseMode, component.Component):
self._go_top = False
# 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()
self.popup = MessagePopup(self, title, msg, width_req=1.0)
@ -864,7 +829,7 @@ class AllTorrents(BaseMode, component.Component):
self.stdscr.erase()
# Update the status bars
if self._curr_filter == None:
if self._curr_filter is None:
self.add_string(0, self.statusbars.topbar)
else:
self.add_string(0, "%s {!filterstatus!}Current filter: %s" % (self.statusbars.topbar, self._curr_filter))
@ -875,7 +840,8 @@ class AllTorrents(BaseMode, component.Component):
SEARCH_EMPTY: "{!black,white!}Search torrents: %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_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)"
}[self.search_state] % self.search_string
@ -904,11 +870,12 @@ class AllTorrents(BaseMode, component.Component):
#Because dots are slow
sorted_ids = self._sorted_ids
curstate = self.curstate
gcv = column.get_column_value
gcv = console_column.get_column_value
fr = format_utils.format_row
cols = self.__columns
colw = self.column_widths
cr = self._cached_rows
def draw_row(index):
if index not in cr:
ts = curstate[sorted_ids[index]]
@ -918,15 +885,19 @@ class AllTorrents(BaseMode, component.Component):
if lines:
todraw = []
for l in lines:
if l < tidx - 1: continue
if l >= tidx - 1 + self.rows - 3: break
if l >= self.numtorrents: break
if l < tidx - 1:
continue
if l >= tidx - 1 + self.rows - 3:
break
if l >= self.numtorrents:
break
todraw.append(draw_row(l))
lines.reverse()
else:
todraw = []
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)]
for row in todraw:
@ -1011,7 +982,6 @@ class AllTorrents(BaseMode, component.Component):
curses.doupdate()
def _mark_unmark(self, idx):
if idx in self.marked:
self.marked.remove(idx)
@ -1108,7 +1078,7 @@ class AllTorrents(BaseMode, component.Component):
self.search_state = SEARCH_EMPTY
self.refresh([])
elif c == ord('/'):
elif c == ord("/"):
self.entering_search = False
self.search_state = SEARCH_EMPTY
self.refresh([])
@ -1180,7 +1150,7 @@ class AllTorrents(BaseMode, component.Component):
return
if c > 31 and c < 256:
if chr(c) == 'Q':
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -1198,13 +1168,15 @@ class AllTorrents(BaseMode, component.Component):
return
if c == curses.KEY_UP:
if self.cursel == 1: return
if self.cursel == 1:
return
if not self._scroll_up(1):
effected_lines = [self.cursel - 1, self.cursel]
elif c == curses.KEY_PPAGE:
self._scroll_up(int(self.rows / 2))
elif c == curses.KEY_DOWN:
if self.cursel >= self.numtorrents: return
if self.cursel >= self.numtorrents:
return
if not self._scroll_down(1):
effected_lines = [self.cursel - 2, self.cursel - 1]
elif c == curses.KEY_NPAGE:
@ -1235,28 +1207,29 @@ class AllTorrents(BaseMode, component.Component):
return
else:
if c > 31 and c < 256:
if chr(c) == '/':
if chr(c) == "/":
self.search_string = ""
self.entering_search = True
elif chr(c) == 'n' and self.search_string:
elif chr(c) == "n" and self.search_string:
self.__do_search("next")
elif chr(c) == 'j':
elif chr(c) == "j":
if not self._scroll_up(1):
effected_lines = [self.cursel - 1, self.cursel]
elif chr(c) == 'k':
elif chr(c) == "k":
if not self._scroll_down(1):
effected_lines = [self.cursel - 2, self.cursel - 1]
elif chr(c) == 'i':
elif chr(c) == "i":
cid = self.current_torrent_id()
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.add_line("Getting torrent info...")
self.__torrent_info_id = cid
elif chr(c) == 'm':
elif chr(c) == "m":
self._mark_unmark(self.cursel)
effected_lines = [self.cursel - 1]
elif chr(c) == 'M':
elif chr(c) == "M":
if self.last_mark >= 0:
if (self.cursel + 1) > self.last_mark:
mrange = range(self.last_mark, self.cursel + 1)
@ -1267,14 +1240,14 @@ class AllTorrents(BaseMode, component.Component):
else:
self._mark_unmark(self.cursel)
effected_lines = [self.cursel - 1]
elif chr(c) == 'c':
elif chr(c) == "c":
self.marked = []
self.last_mark = -1
elif chr(c) == 'a':
elif chr(c) == "a":
self._show_torrent_add_popup()
elif chr(c) == 'v':
elif chr(c) == "v":
self._show_visible_columns_popup()
elif chr(c) == 'o':
elif chr(c) == "o":
if not self.marked:
self.marked = [self.cursel]
self.last_mark = self.cursel
@ -1282,7 +1255,7 @@ class AllTorrents(BaseMode, component.Component):
self.last_mark = -1
torrent_actions_popup(self, self._selected_torrent_ids(), action=ACTION.TORRENT_OPTIONS)
elif chr(c) == '<':
elif chr(c) == "<":
i = len(self.__cols_to_show)
try:
i = self.__cols_to_show.index(self.config["sort_primary"]) - 1
@ -1298,7 +1271,7 @@ class AllTorrents(BaseMode, component.Component):
self.__update_columns()
self.refresh([])
elif chr(c) == '>':
elif chr(c) == ">":
i = 0
try:
i = self.__cols_to_show.index(self.config["sort_primary"]) + 1
@ -1314,17 +1287,17 @@ class AllTorrents(BaseMode, component.Component):
self.__update_columns()
self.refresh([])
elif chr(c) == 'f':
elif chr(c) == "f":
self._show_torrent_filter_popup()
elif chr(c) == 'h':
elif chr(c) == "h":
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
elif chr(c) == 'p':
elif chr(c) == "p":
self.show_preferences()
return
elif chr(c) == 'e':
elif chr(c) == "e":
self.__show_events()
return
elif chr(c) == 'l':
elif chr(c) == "l":
self.__legacy_mode()
return

View File

@ -1,39 +1,11 @@
#
# basemode.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
# Most code in this file taken from screen.py:
# 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.
#
import logging
@ -60,6 +32,7 @@ except:
log = logging.getLogger(__name__)
class CursesStdIO(object):
"""fake fd to be registered as a reader with the twisted reactor.
Curses classes needing input should extend this"""
@ -71,7 +44,9 @@ class CursesStdIO(object):
def doRead(self):
"""called when input is ready"""
pass
def logPrefix(self): return 'CursesClient'
def logPrefix(self):
return "CursesClient"
class BaseMode(CursesStdIO):
@ -230,7 +205,7 @@ class BaseMode(CursesStdIO):
def _doRead(self):
# Read the character
c = self.stdscr.getch()
self.stdscr.getch()
self.stdscr.refresh()
def close(self):

View File

@ -1,37 +1,10 @@
# -*- coding: utf-8 -*-
#
# column.py
#
# Copyright (C) 2011 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.
#
import logging

View File

@ -1,39 +1,13 @@
# -*- coding: utf-8 -*-
#
# connectionmanager.py
#
# 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.
# Copyright (C) 2011 Nick Lanham <nick@afternight.org>
#
# 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 logging
@ -46,7 +20,6 @@ from alltorrents import AllTorrents
from basemode import BaseMode
from deluge.configmanager import ConfigManager
from deluge.ui.client import client
from deluge.ui.coreconfig import CoreConfig
from input_popup import InputPopup
from popup import MessagePopup, SelectablePopup
@ -77,10 +50,12 @@ class ConnectionManager(BaseMode):
def __update_popup(self):
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"]:
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:
self.popup.add_line("%s:%d [Offline]" % (host[1], host[2]), data=host[0], foreground="red")
self.inlist = True
@ -174,7 +149,7 @@ class ConnectionManager(BaseMode):
self.draw_statusbars()
self.stdscr.noutrefresh()
if self.popup == None and self.messages:
if self.popup is None and self.messages:
title, msg = self.messages.popleft()
self.popup = MessagePopup(self, title, msg)
@ -198,8 +173,9 @@ class ConnectionManager(BaseMode):
c = self.stdscr.getch()
if c > 31 and c < 256:
if chr(c) == 'q' and self.inlist: return
if chr(c) == 'Q':
if chr(c) == "q" and self.inlist:
return
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -208,13 +184,13 @@ class ConnectionManager(BaseMode):
else:
reactor.stop()
return
if chr(c) == 'D' and self.inlist:
if chr(c) == "D" and self.inlist:
self.__delete_current_host()
self.__update_popup()
return
if chr(c) == 'r' and self.inlist:
if chr(c) == "r" and self.inlist:
self.__update_statuses()
if chr(c) == 'a' and self.inlist:
if chr(c) == "a" and self.inlist:
self.__add_popup()
return

View File

@ -1,37 +1,10 @@
# -*- coding: utf-8 -*-
#
# eventview.py
#
# Copyright (C) 2011 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.
#
import logging
@ -116,7 +89,7 @@ class EventView(BaseMode):
c = self.stdscr.getch()
if c > 31 and c < 256:
if chr(c) == 'Q':
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -125,7 +98,7 @@ class EventView(BaseMode):
else:
reactor.stop()
return
elif chr(c) == 'q':
elif chr(c) == "q":
self.back_to_overview()
return
@ -149,9 +122,9 @@ class EventView(BaseMode):
self.offset += jumplen
elif c == curses.KEY_END:
self.offset += num_events
elif c == ord('j'):
elif c == ord("j"):
self.offset -= 1
elif c == ord('k'):
elif c == ord("k"):
self.offset += 1
if self.offset <= 0:

View File

@ -1,37 +1,10 @@
# -*- coding: utf-8 -*-
#
# format_utils.py
#
# Copyright (C) 2011 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.
#
import re
@ -46,57 +19,69 @@ try:
except:
haveud = False
def format_speed(speed):
if (speed > 0):
return deluge.common.fspeed(speed)
else:
return "-"
def format_time(time):
if (time > 0):
return deluge.common.ftime(time)
else:
return "-"
def format_date(time):
if (time > 0):
return deluge.common.fdate(time)
else:
return ""
def format_date_never(time):
if (time > 0):
return deluge.common.fdate(time)
else:
return "Never"
def format_float(x):
if x < 0:
return "-"
else:
return "%.3f" % x
def format_seeds_peers(num, total):
return "%d (%d)" % (num, total)
def format_progress(perc):
if perc < 100:
return "%.2f%%" % perc
else:
return "100%"
def format_pieces(num, size):
return "%d (%s)" % (num, deluge.common.fsize(size))
def format_priority(prio):
if prio == -2: return "[Mixed]"
if prio < 0: return "-"
if prio == - 2:
return "[Mixed]"
if prio < 0:
return "-"
pstring = deluge.common.FILE_PRIORITY[prio]
if prio > 0:
return pstring[:pstring.index("Priority") - 1]
else:
return pstring
def trim_string(string, w, have_dbls):
if w <= 0:
return ""
@ -109,14 +94,14 @@ def trim_string(string, w, have_dbls):
idx = 0
while width < w:
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
else:
width += 1
idx += 1
if width != w:
chrs.pop()
chrs.append('.')
chrs.append(".")
return u"%s " % ("".join(chrs))
else:
return u"%s " % (string[0:w - 1])
@ -124,6 +109,8 @@ def trim_string(string, w, have_dbls):
#Dots are slow
eaw = unicodedata.east_asian_width
ud_normalize = unicodedata.normalize
def format_column(col, lim):
dbls = 0
#Chosen over isinstance(col, unicode) and col.__class__ == unicode
@ -132,21 +119,24 @@ def format_column(col, lim):
if haveud and col.__class__ is unicode:
# might have some double width chars
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
if (size >= lim - 1):
return trim_string(col, lim, dbls > 0)
else:
return "%s%s" % (col, " " * (lim - size))
def format_row(row, column_widths):
return "".join([format_column(row[i], column_widths[i]) for i in range(0, len(row))])
_strip_re = re.compile("\{!.*?!\}")
def remove_formatting(string):
return re.sub(_strip_re, "", string)
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.
@ -170,14 +160,14 @@ def wrap_string(string,width,min_lines=0,strip_colors=True):
return s
for s in s1:
cur_pos = offset = 0
offset = 0
if strip_colors:
mtchs = deque()
clrs = deque()
for m in _strip_re.finditer(s):
mtchs.append(m.start())
clrs.append(m.group())
cstr = _strip_re.sub('', s)
cstr = _strip_re.sub("", s)
else:
cstr = s
while len(cstr) > width:
@ -208,7 +198,7 @@ def wrap_string(string,width,min_lines=0,strip_colors=True):
if not cstr:
cstr = None
break
if cstr != None:
if cstr is not None:
if strip_colors:
ret.append(insert_clr(cstr, offset, mtchs, clrs))
else:
@ -236,17 +226,9 @@ def strwidth(string):
Measure width of a string considering asian double width characters
"""
if not isinstance(string, unicode):
string = unicode(string, 'utf-8')
eaw = east_asian_width
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
string = unicode(string, "utf-8")
return sum([1 + (east_asian_width(char) in ["W", "F"]) for char in string])
def pad_string(string, length, character=" ", side="right"):
"""

View File

@ -1,40 +1,12 @@
#
# input_popup.py
# -*- coding: utf-8 -*-
#
# 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) 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.
#
try:
@ -51,6 +23,7 @@ from popup import ALIGN, Popup
log = logging.getLogger(__name__)
class InputField:
depend = None
# render the input. return number of rows taken up
@ -60,12 +33,15 @@ class InputField:
def render(self, screen, row, width, selected, col=1):
return 0
def handle_read(self, c):
if c in [curses.KEY_ENTER, 10, 127, 113]:
return True
return False
def get_value(self):
return None
def set_value(self, value):
pass
@ -83,6 +59,7 @@ class InputField:
else:
return not self.depend.checked
class CheckedInput(InputField):
def __init__(self, parent, message, name, checked=False, additional_formatting=False):
self.parent = parent
@ -118,6 +95,7 @@ class CheckedInput(InputField):
def set_value(self, c):
self.checked = c
class CheckedPlusInput(InputField):
def __init__(self, parent, message, name, child, checked=False, additional_formatting=False):
self.parent = parent
@ -132,7 +110,7 @@ class CheckedPlusInput(InputField):
self.child = child
self.child_active = False
def get_height():
def get_height(self):
return max(2, self.child.height)
def render(self, screen, row, width, active, col=1):
@ -154,9 +132,11 @@ class CheckedPlusInput(InputField):
# show child
if self.checked:
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:
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)
else:
self.parent.add_string(row, "(enable to view/edit value)", screen, col + self.msglen, False, True)
@ -186,7 +166,8 @@ class CheckedPlusInput(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.message = message
self.name = name
@ -208,14 +189,14 @@ class IntSpinInput(InputField):
return 1
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
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
def render(self, screen, row, width, active, col=1, cursor_offset=0):
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.valstr = self.default_str
try:
@ -229,7 +210,7 @@ class IntSpinInput(InputField):
self.cursor = len(self.valstr)
self.cursor = colors.get_line_width(self.valstr)
self.need_update = False
elif self.need_update and self.valstr != '-':
elif self.need_update and self.valstr != "-":
self.real_value = True
try:
self.value = int(self.valstr)
@ -242,9 +223,11 @@ class IntSpinInput(InputField):
if not self.valstr:
self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True)
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:
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:
self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True)
@ -273,16 +256,20 @@ class IntSpinInput(InputField):
self.valstr = "%d" % self.value
self.cursor = len(self.valstr)
elif c == curses.KEY_LEFT:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = max(0, self.cursor - 1)
elif c == curses.KEY_RIGHT:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = min(len(self.valstr), self.cursor + 1)
elif c == curses.KEY_HOME:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = 0
elif c == curses.KEY_END:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = len(self.valstr)
elif c == curses.KEY_BACKSPACE or c == 127:
if not self.real_value:
@ -295,7 +282,8 @@ class IntSpinInput(InputField):
self.cursor -= 1
self.need_update = True
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):
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:]
self.need_update = True
@ -305,9 +293,11 @@ class IntSpinInput(InputField):
self.cursor = 1
self.real_value = True
self.need_update = True
if self.cursor != 0: return
minus_place = self.valstr.find('-')
if minus_place >= 0: return
if self.cursor != 0:
return
minus_place = self.valstr.find("-")
if minus_place >= 0:
return
self.valstr = chr(c) + self.valstr
self.cursor += 1
self.need_update = True
@ -317,9 +307,11 @@ class IntSpinInput(InputField):
self.cursor = 0
self.real_value = True
self.need_update = True
if c == 48 and self.cursor == 0: return
minus_place = self.valstr.find('-')
if self.cursor <= minus_place: return
if c == 48 and self.cursor == 0:
return
minus_place = self.valstr.find("-")
if self.cursor <= minus_place:
return
if self.cursor == len(self.valstr):
self.valstr += chr(c)
else:
@ -329,7 +321,6 @@ class IntSpinInput(InputField):
# Move the cursor forward
self.cursor += 1
def get_value(self):
if self.real_value:
self.__limit_value()
@ -348,9 +339,10 @@ class IntSpinInput(InputField):
self.valstr = val
self.cursor = len(self.valstr)
#TODO: This vvvvv
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.message = message
self.name = name
@ -376,15 +368,15 @@ class FloatSpinInput(InputField):
return 1
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
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.valstr = self.fmt % self.value
def render(self, screen, row, width, active, col=1, cursor_offset=0):
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.valstr = self.default_str
try:
@ -398,7 +390,7 @@ class FloatSpinInput(InputField):
self.cursor = len(self.valstr)
self.cursor = colors.get_line_width(self.valstr)
self.need_update = False
elif self.need_update and self.valstr != '-':
elif self.need_update and self.valstr != "-":
self.real_value = True
try:
self.value = round(float(self.valstr), self.precision)
@ -412,9 +404,11 @@ class FloatSpinInput(InputField):
if not self.valstr:
self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True)
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:
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:
self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True)
if active:
@ -444,16 +438,20 @@ class FloatSpinInput(InputField):
self.valstr = self.fmt % self.value
self.cursor = len(self.valstr)
elif c == curses.KEY_LEFT:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = max(0, self.cursor - 1)
elif c == curses.KEY_RIGHT:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = min(len(self.valstr), self.cursor + 1)
elif c == curses.KEY_HOME:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = 0
elif c == curses.KEY_END:
if not self.real_value: return None
if not self.real_value:
return None
self.cursor = len(self.valstr)
elif c == curses.KEY_BACKSPACE or c == 127:
if not self.real_value:
@ -466,7 +464,8 @@ class FloatSpinInput(InputField):
self.cursor -= 1
self.need_update = True
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):
self.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:]
self.need_update = True
@ -476,9 +475,11 @@ class FloatSpinInput(InputField):
self.cursor = 1
self.need_update = True
self.real_value = True
if self.cursor != 0: return
minus_place = self.valstr.find('-')
if minus_place >= 0: return
if self.cursor != 0:
return
minus_place = self.valstr.find("-")
if minus_place >= 0:
return
self.valstr = chr(c) + self.valstr
self.cursor += 1
self.need_update = True
@ -488,10 +489,12 @@ class FloatSpinInput(InputField):
self.cursor = 2
self.real_value = True
self.need_update = True
minus_place = self.valstr.find('-')
if self.cursor <= minus_place: return
point_place = self.valstr.find('.')
if point_place >= 0: return
minus_place = self.valstr.find("-")
if self.cursor <= minus_place:
return
point_place = self.valstr.find(".")
if point_place >= 0:
return
if self.cursor == len(self.valstr):
self.valstr += chr(c)
else:
@ -508,8 +511,9 @@ class FloatSpinInput(InputField):
self.need_update = True
if self.value == "mixed":
self.value = ""
minus_place = self.valstr.find('-')
if self.cursor <= minus_place: return
minus_place = self.valstr.find("-")
if self.cursor <= minus_place:
return
if self.cursor == len(self.valstr):
self.valstr += chr(c)
else:
@ -537,6 +541,7 @@ class FloatSpinInput(InputField):
self.valstr = val
self.cursor = len(self.valstr)
class SelectInput(InputField):
def __init__(self, parent, message, name, opts, vals, selidx, additional_formatting=False):
self.parent = parent
@ -565,7 +570,8 @@ class SelectInput(InputField):
elif self.additional_formatting:
self.parent.add_string(row, "[{!white,blue!}%s{!white,black!}]" % opt, screen, off, False, True)
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:
self.parent.add_string(row, "[%s]" % opt, screen, off, False, True)
off += len(opt) + 3
@ -590,6 +596,7 @@ class SelectInput(InputField):
return
raise Exception("Invalid value for SelectInput")
class TextInput(InputField):
def __init__(self, parent, move_func, width, message, name, value, docmp, additional_formatting=False):
self.parent = parent
@ -730,7 +737,6 @@ class TextInput(InputField):
# Move the cursor forward
self.cursor += 1
def complete(self, line):
line = os.path.abspath(os.path.expanduser(line))
ret = []
@ -769,12 +775,10 @@ class TextInput(InputField):
class InputPopup(Popup):
def __init__(self,parent_mode,title,width_req=0,height_req=0,
align=ALIGN.DEFAULT,
close_cb=None,
additional_formatting=True,
immediate_action=False):
Popup.__init__(self, parent_mode, title, width_req=width_req, height_req=height_req, align=align, close_cb=close_cb)
def __init__(self, parent_mode, title, width_req=0, height_req=0, align=ALIGN.DEFAULT, close_cb=None,
additional_formatting=True, 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.lines = []
self.current_input = 0
@ -798,8 +802,7 @@ class InputPopup(Popup):
:param value: initial value of the field
: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,
name, value, complete,
self.inputs.append(TextInput(self, self.move, self.width, message, name, value, complete,
additional_formatting=self.additional_formatting))
def getmaxyx(self):
@ -849,7 +852,6 @@ class InputPopup(Popup):
start_row = 0
end_row = 0
spos = 0
for i, ipt in enumerate(self.inputs):
for line in self.lines:
if line[0] == i:
@ -866,7 +868,6 @@ class InputPopup(Popup):
self.content_height = end_row
crow = 1 - self.lineoff
spos = 0
for i, ipt in enumerate(self.inputs):
for line in self.lines:
if line[0] == i:

View File

@ -1,38 +1,11 @@
# -*- coding: utf-8 -*-
#
# legacy.py
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@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.
#
try:
@ -45,7 +18,7 @@ import logging
import os
import re
from twisted.internet import defer, reactor
from twisted.internet import defer
import deluge.component as component
import deluge.configmanager
@ -64,10 +37,12 @@ INPUT_HISTORY_SIZE = 500
MAX_HISTFILE_SIZE = 2000
def complete_line(line, possible_matches):
"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"\ ", " ")
@ -79,13 +54,16 @@ def complete_line(line, possible_matches):
match = match.replace(r"\ ", " ")
m1, m2 = "", ""
for i, c in enumerate(line):
if m1 and m2: break
if m1 and m2:
break
if not m1 and c != line[i]:
m1 = line[:i]
if not m2 and c.lower() != line[i].lower():
m2 = line[:i]
if not m1: matches1.append(match)
elif not m2: matches2.append(match)
if not m1:
matches1.append(match)
elif not m2:
matches2.append(match)
possible_matches = matches1 + matches2
@ -103,15 +81,17 @@ def complete_line(line, possible_matches):
return possible_matches[0][:maxlen].replace(" ", r"\ ")
def commonprefix(m):
"Given a list of pathnames, returns the longest common leading component"
if not m: return ''
if not m:
return ""
s1 = min(m)
s2 = max(m)
for i, c in enumerate(s1):
if c != s2[i]:
return s1[:i]
return s
return s2
class Legacy(BaseMode, component.Component):
@ -131,7 +111,7 @@ class Legacy(BaseMode, component.Component):
self.input_incomplete = ""
self._old_char = 0
self._last_char = 0
self._last_del_char = ''
self._last_del_char = ""
# Keep track of where the cursor is
self.input_cursor = 0
@ -160,14 +140,14 @@ class Legacy(BaseMode, component.Component):
if self.console_config["save_legacy_history"]:
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)
except:
lines1 = []
self._hf_lines[0] = 0
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)
except:
lines2 = []
@ -202,7 +182,6 @@ class Legacy(BaseMode, component.Component):
component.start("LegacyUI")
# show the cursor
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
self.torrents = []
def on_session_state(result):
def on_torrents_status(torrents):
for torrent_id, status in torrents.items():
@ -258,7 +238,7 @@ class Legacy(BaseMode, component.Component):
if self.input:
self.input = self.input.encode(self.encoding)
if self.input.endswith('\\'):
if self.input.endswith("\\"):
self.input = self.input[:-1]
self.input_cursor -= 1
self.add_line("{!yellow,black,bold!}>>>{!input!} %s" % self.input)
@ -456,7 +436,6 @@ class Legacy(BaseMode, component.Component):
self.stdscr.redrawwin()
self.stdscr.refresh()
def add_line(self, text, refresh=True):
"""
Add a line to the screen. This will be showed between the two bars.
@ -495,7 +474,7 @@ class Legacy(BaseMode, component.Component):
active_file = 0
#Write the line
f = open(self.history_file[active_file], 'a')
f = open(self.history_file[active_file], "a")
if isinstance(text, unicode):
text = text.encode(self.encoding)
@ -510,7 +489,7 @@ class Legacy(BaseMode, component.Component):
# therefore swapping the currently active file
if self._hf_lines[active_file] == MAX_HISTFILE_SIZE:
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)
def get_line_chunks(line):
@ -522,17 +501,17 @@ class Legacy(BaseMode, component.Component):
return []
chunks = []
if not line.startswith('{!'):
begin = line.find('{!')
if not line.startswith("{!"):
begin = line.find("{!")
if begin == -1:
begin = len(line)
chunks.append( ('', line[:begin]) )
chunks.append(("", line[:begin]))
line = line[begin:]
while line:
# We know the line starts with '{!' here
end_color = line.find('!}')
next_color = line.find('{!', end_color)
# We know the line starts with "{!" here
end_color = line.find("!}")
next_color = line.find("{!", end_color)
if next_color == -1:
next_color = len(line)
chunks.append((line[:end_color + 2], line[end_color + 2:next_color]))
@ -582,7 +561,6 @@ class Legacy(BaseMode, component.Component):
if refresh:
self.refresh()
def add_string(self, row, string):
"""
Adds a string to the desired `:param:row`.
@ -608,7 +586,6 @@ class Legacy(BaseMode, component.Component):
col += strwidth(s)
def do_command(self, cmd):
"""
Processes a command.
@ -618,7 +595,7 @@ class Legacy(BaseMode, component.Component):
"""
if not cmd:
return
cmd, _, line = cmd.partition(' ')
cmd, _, line = cmd.partition(" ")
try:
parser = self.console._commands[cmd].create_parser()
except KeyError:
@ -633,6 +610,7 @@ class Legacy(BaseMode, component.Component):
# Do a little hack here to print 'command --help' properly
parser._print_help = parser.print_help
def print_help(f=None):
parser._print_help(f)
parser.print_help = print_help
@ -654,7 +632,7 @@ class Legacy(BaseMode, component.Component):
self.write("{!error!}Error parsing options: %s" % ex)
return
if not getattr(options, '_exit', False):
if not getattr(options, "_exit", False):
try:
ret = self.console._commands[cmd].handle(*args, **options.__dict__)
except Exception as ex:
@ -666,8 +644,6 @@ class Legacy(BaseMode, component.Component):
else:
return ret
def set_batch_write(self, batch):
"""
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)
def tab_completer(self, line, cursor, hits):
"""
Called when the user hits 'tab' and will autocomplete or show options.
@ -787,7 +762,8 @@ class Legacy(BaseMode, component.Component):
for i in range(listed, listed + max_list):
match = possible_matches[i]
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:
self.tab_count = 0
for match in possible_matches[listed:]:
@ -818,9 +794,9 @@ class Legacy(BaseMode, component.Component):
continue
f = os.path.join(line, f)
if os.path.isdir(f):
if os.sep == '\\': # Windows path support :|
if os.sep == "\\": # Windows path support
f += "\\"
else: # \o/ Unix
else: # Unix
f += "/"
elif not f.endswith(ext):
continue
@ -847,9 +823,9 @@ class Legacy(BaseMode, component.Component):
p = os.path.join(os.path.dirname(line), f)
if os.path.isdir(p):
if os.sep == '\\': # Windows path support :|
if os.sep == "\\": # Windows path support
p += "\\"
else: # \o/ Unix
else: # Unix
p += "/"
ret.append(p)
except OSError:
@ -909,7 +885,8 @@ class Legacy(BaseMode, component.Component):
# Find all possible matches
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(" ", "\\ ")
# If we only matched one torrent, don't add the full name or it'll also get autocompleted
if match_count == 1:
@ -932,10 +909,12 @@ class Legacy(BaseMode, component.Component):
text = "{!info!}%s{!input!}%s - '%s'" % (torrent_id[:l], torrent_id[l:], torrent_name)
possible_matches.append(text)
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)
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)
return possible_matches + possible_matches2

View File

@ -1,42 +1,14 @@
# -*- coding: utf-8 -*-
#
# popup.py
#
# Copyright (C) 2011 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.
#
try:
import curses
import signal
except ImportError:
pass
@ -46,6 +18,7 @@ import format_utils
log = logging.getLogger(__name__)
class ALIGN:
TOP_LEFT = 1
TOP_CENTER = 2
@ -58,8 +31,10 @@ class ALIGN:
BOTTOM_RIGHT = 9
DEFAULT = MIDDLE_CENTER
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.
@ -155,7 +130,6 @@ class Popup:
self.x, self.y = bx, by
self.height, self.width = self.screen.getmaxyx()
def refresh(self):
self.screen.erase()
self.screen.border(0, 0, 0, 0)
@ -169,7 +143,8 @@ class Popup:
sb_pos = int((self.height - 2) * perc_sc) + 1
if (sb_pos == 1) and (self.lineoff != 0):
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.noutrefresh()
@ -198,7 +173,7 @@ class Popup:
self.close_cb()
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:
self.close_cb()
return True # close the popup
@ -240,7 +215,7 @@ class SelectablePopup(Popup):
def add_line(self, string, selectable=True, use_underline=True, data=None, foreground=None):
if use_underline:
udx = string.find('_')
udx = string.find("_")
if udx >= 0:
string = string[:udx] + string[udx + 1:]
self._udxs[len(self._lines) + 1] = udx
@ -264,20 +239,24 @@ class SelectablePopup(Popup):
fg = self._line_foregrounds[row]
udx = self._udxs.get(crow)
if row == self._selected:
if fg == None: fg = "black"
if fg is None:
fg = "black"
colorstr = "{!%s,white,bold!}" % fg
if udx >= 0:
ustr = "{!%s,white,bold,underline!}" % fg
else:
if fg == None: fg = "white"
if fg is None:
fg = "white"
colorstr = "{!%s,black!}" % fg
if udx >= 0:
ustr = "{!%s,black,underline!}" % fg
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:
# 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:
self.parent.add_string(crow, "- %s%s" % (colorstr, line), self.screen, 1, False, True)
crow += 1
@ -337,7 +316,7 @@ class SelectablePopup(Popup):
return self._selection_callback(idx, self._select_data[idx], *self._selection_args)
if c > 31 and c < 256:
if chr(c) == 'q':
if chr(c) == "q":
return True # close the popup
uc = chr(c).lower()
if uc in self._hotkeys:

View File

@ -1,36 +1,10 @@
#
# preference_panes.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 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.
#
import logging
@ -52,6 +26,7 @@ class NoInput:
def depend_skip(self):
return False
class Header(NoInput):
def __init__(self, parent, header, space_above, space_below):
self.parent = parent
@ -66,9 +41,11 @@ class Header(NoInput):
row += 1
rows += 1
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
class InfoField(NoInput):
def __init__(self, parent, label, value, name):
self.parent = parent
@ -88,6 +65,7 @@ class InfoField(NoInput):
else:
self.txt = "%s %s" % (self.label, self.value)
class BasePane:
def __init__(self, offset, parent, width):
self.offset = offset + 1
@ -137,8 +115,6 @@ class BasePane:
conf_dict.setdefault("proxy", {})["proxy_hostnames"] = ipt.get_value()
elif ipt.name == "proxy_peer_connections":
conf_dict.setdefault("proxy", {})["proxy_peer_connections"] = ipt.get_value()
else:
conf_dict[ipt.name] = ipt.get_value()
if hasattr(ipt, "get_child"):
@ -176,7 +152,8 @@ class BasePane:
return 0
continue
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)
if crow >= (mode.prefs_height):
break
@ -203,7 +180,8 @@ class BasePane:
nc = max(0, self.active_input - 1)
while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
nc -= 1
if nc <= 0: break
if nc <= 0:
break
if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip():
self.active_input = nc
elif c == curses.KEY_DOWN:
@ -219,7 +197,6 @@ class BasePane:
else:
self.inputs[self.active_input].handle_read(c)
def add_header(self, header, space_above=False, space_below=False):
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))
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):
@ -250,22 +228,30 @@ class InterfacePane(BasePane):
BasePane.__init__(self, offset, parent, width)
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_checked_input("separate_complete", "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_checked_input("separate_complete",
"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_checked_input("ignore_duplicate_lines", "Do not store duplicate input in 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_checked_input("ignore_duplicate_lines", "Do not store duplicate input in 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_checked_input("third_tab_lists_all", "Third tab lists all remaining torrents in legacy mode", 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)
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_int_spin_input("torrents_per_tab_press", "Torrents per tab press",
parent.console_config["torrents_per_tab_press"], 5, 100)
class ColumnsPane(BasePane):
@ -300,11 +286,15 @@ class DownloadsPane(BasePane):
self.add_header("Folders")
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"])
copytxt = TextInput(self.parent, self.move, self.width, None, "torrentfiles_location", parent.core_config["torrentfiles_location"], False)
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"])
copytxt = TextInput(self.parent, self.move, self.width, None, "torrentfiles_location",
parent.core_config["torrentfiles_location"], False)
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_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):
BasePane.__init__(self, offset, parent, width)
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)
listen_ports = parent.core_config["listen_ports"]
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.into)
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)
out_ports = parent.core_config["outgoing_ports"]
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.outto)
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_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_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_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"])
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_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):
def __init__(self, offset, parent, width):
BasePane.__init__(self, offset, parent, width)
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_upload_slots_global", "Maximum Upload Slots:", parent.core_config["max_upload_slots_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_float_spin_input("max_upload_speed", "Maximum Upload Speed (KiB/s):", 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_int_spin_input("max_connections_global", "Maximum Connections:",
parent.core_config["max_connections_global"], -1, 9000)
self.add_int_spin_input("max_upload_slots_global", "Maximum Upload Slots:",
parent.core_config["max_upload_slots_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_float_spin_input("max_upload_speed", "Maximum Upload Speed (KiB/s):",
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_int_spin_input("max_connections_per_torrent", "Maximum Connections:", parent.core_config["max_connections_per_torrent"], -1, 9000)
self.add_int_spin_input("max_upload_slots_per_torrent", "Maximum Upload Slots:", 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)
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_upload_slots_per_torrent", "Maximum Upload Slots:",
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):
def __init__(self, offset, parent, width):
@ -392,6 +399,7 @@ class OtherPane(BasePane):
self.add_header("GeoIP Database", True)
self.add_text_input("geoip_db_location", "Location:", parent.core_config["geoip_db_location"])
class DaemonPane(BasePane):
def __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_checked_input("allow_remote", "Allow remote connections", parent.core_config["allow_remote"])
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):
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_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_downloading", "Total active downloading:", parent.core_config["max_active_downloading"], -1, 9999)
self.add_int_spin_input("max_active_seeding", "Total active seeding:", 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_int_spin_input("max_active_downloading", "Total active downloading:",
parent.core_config["max_active_downloading"], -1, 9999)
self.add_int_spin_input("max_active_seeding", "Total active seeding:",
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_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("seed_time_ratio_limit", "Share Time Ratio:", parent.core_config["seed_time_ratio_limit"], 1.0, 2, -1.0, 100.0)
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("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)
seedratio = FloatSpinInput(self.parent, "", "stop_seed_ratio", self.move, parent.core_config["stop_seed_ratio"], 0.1, 2, 0.5, 100.0)
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"])
seedratio = FloatSpinInput(self.parent, "", "stop_seed_ratio", self.move,
parent.core_config["stop_seed_ratio"], 0.1, 2, 0.5, 100.0)
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):
def __init__(self, offset, parent, width):
@ -454,7 +474,8 @@ class CachePane(BasePane):
self.add_header(" Write")
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(" 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_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")

View File

@ -1,37 +1,10 @@
# -*- coding: utf-8 -*-
#
# preferences.py
#
# Copyright (C) 2011 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.
#
import logging
@ -40,6 +13,7 @@ from collections import deque
import deluge.component as component
from basemode import BaseMode
from deluge.ui.client import client
from popup import MessagePopup
from input_popup import Popup, SelectInput
from preference_panes import (BandwidthPane, CachePane, ColumnsPane, DaemonPane, DownloadsPane, InterfacePane,
NetworkPane, OtherPane, ProxyPane, QueuePane)
@ -54,9 +28,7 @@ log = logging.getLogger(__name__)
# Big help string that gets displayed when the user hits 'h'
HELP_STR = \
"""This screen lets you view and configure various options
in deluge.
HELP_STR = """This screen lets you view and configure various options in deluge.
There are three main sections to this screen. Only one
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:
@ -103,6 +75,7 @@ class ZONE:
PREFRENCES = 1
ACTIONS = 2
class Preferences(BaseMode):
def __init__(self, parent_mode, core_config, console_config, active_port, status, stdscr, encoding=None):
self.parent_mode = parent_mode
@ -156,7 +129,7 @@ class Preferences(BaseMode):
self.add_string(i + 1, "- {!black,white!}%s" % category, pad=False)
else:
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):
self.panes[self.cur_cat].render(self, self.stdscr, self.prefs_width, self.active_zone == ZONE.PREFRENCES)
@ -177,7 +150,7 @@ class Preferences(BaseMode):
self.refresh()
def refresh(self):
if self.popup == None and self.messages:
if self.popup is None and self.messages:
title, msg = self.messages.popleft()
self.popup = MessagePopup(self, title, msg)
@ -251,7 +224,6 @@ class Preferences(BaseMode):
self.console_config.save()
self.parent_mode.update_config()
def __update_preferences(self, core_config):
self.core_config = core_config
for pane in self.panes:
@ -261,16 +233,15 @@ class Preferences(BaseMode):
self.action_input.handle_read(c)
if c == curses.KEY_ENTER or c == 10:
# take action
if self.action_input.selidx == 0: # cancel
if self.action_input.selidx == 0: # Cancel
self.back_to_parent()
elif self.action_input.selidx == 1: # apply
elif self.action_input.selidx == 1: # Apply
self.__apply_prefs()
client.core.get_config().addCallback(self.__update_preferences)
elif self.action_input.selidx == 2: # OK
self.__apply_prefs()
self.back_to_parent()
def back_to_parent(self):
self.stdscr.erase()
component.get("ConsoleUI").set_mode(self.parent_mode)
@ -286,7 +257,7 @@ class Preferences(BaseMode):
return
if c > 31 and c < 256:
if chr(c) == 'Q':
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -295,7 +266,7 @@ class Preferences(BaseMode):
else:
reactor.stop()
return
elif chr(c) == 'h':
elif chr(c) == "h":
self.popup = Popup(self, "Preferences Help")
for l in HELP_LINES:
self.popup.add_line(l)

View File

@ -1,35 +1,10 @@
# torrent_actions.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 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.
#
import logging
@ -38,7 +13,7 @@ from twisted.internet import defer
import deluge.component as component
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 popup import Popup, SelectablePopup
@ -74,6 +49,7 @@ torrent_options_to_names = {
"move_on_completed_path": "Folder to move the torrent to"
}
class ACTION:
PAUSE = 0
RESUME = 1
@ -92,11 +68,14 @@ class ACTION:
QUEUE_BOTTOM = 14
TORRENT_OPTIONS = 15
def action_error(error, mode):
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()
def torrent_action(idx, data, mode, ids):
if ids:
if data == ACTION.PAUSE:
@ -163,7 +142,8 @@ def torrent_action(idx, data, mode, ids):
return False
elif data == ACTION.REMOVE:
def do_remove(data):
if not data: return
if not data:
return
mode.clear_marks()
wd = data["remove_files"]
@ -171,9 +151,6 @@ def torrent_action(idx, data, mode, ids):
log.debug("Removing torrent: %s, %d", tid, wd)
client.core.remove_torrent(tid, wd).addErrback(action_error, mode)
rem_msg = ""
def got_status(status):
return (status["name"], status["state"])
@ -182,7 +159,6 @@ def torrent_action(idx, data, mode, ids):
d = client.core.get_torrent_status(tid, ["name", "state"])
callbacks.append(d.addCallback(got_status))
def finish_up(status):
status = map(lambda x: x[1], status)
@ -202,7 +178,8 @@ def torrent_action(idx, data, mode, ids):
popup = InputPopup(mode, "(Esc to cancel, Enter to remove)", close_cb=do_remove)
popup.add_text(rem_msg)
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)
defer.DeferredList(callbacks).addCallback(finish_up)
return False
@ -210,7 +187,8 @@ def torrent_action(idx, data, mode, ids):
def do_move(res):
import os.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:
log.debug("Moving %s to: %s", ids, res["path"])
client.core.move_storage(ids, res["path"]).addErrback(action_error, mode)
@ -315,9 +293,10 @@ def torrent_action(idx, data, mode, ids):
mode.clear_marks()
return True
# 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):
if action != None:
if action is not None:
torrent_action(-1, action, mode, tids)
return
popup = SelectablePopup(mode, "Torrent Actions", torrent_action, (mode, tids))

View File

@ -1,53 +1,24 @@
# -*- coding: utf-8 -*-
#
# torrentdetail.py
#
# Copyright (C) 2011 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.
#
import logging
from collections import deque
from sys import maxint
import deluge.common as common
import deluge.component as component
import deluge.ui.console.colors as colors
import format_utils
from add_util import add_torrent
from basemode import BaseMode
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 popup import MessagePopup, Popup, SelectablePopup
from popup import MessagePopup, SelectablePopup
from torrent_actions import ACTION, torrent_actions_popup
try:
@ -90,6 +61,7 @@ download priority of selected files and folders.
{!info!}Left Arrow{!normal!} - Go back to torrent overview.
"""
class TorrentDetail(BaseMode, component.Component):
def __init__(self, alltorrentmode, torrentid, stdscr, console_config, encoding=None):
@ -140,6 +112,7 @@ class TorrentDetail(BaseMode, component.Component):
# component start/update
def start(self):
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
def update(self):
component.get("SessionProxy").get_torrent_status(self.torrentid, self._status_keys).addCallback(self.set_state)
@ -147,14 +120,16 @@ class TorrentDetail(BaseMode, component.Component):
log.debug("got state")
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
if not self.file_list:
# don't keep getting the files once we've got them once
if state.get("files"):
self.files_sep = "{!green,black,bold,underline!}%s"%(("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.files_sep = "{!green,black,bold,underline!}%s" % (
("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")
else:
self.files_sep = "{!green,black,bold,underline!}%s" % (("Files (File list unknown)").center(self.cols))
@ -224,7 +199,8 @@ class TorrentDetail(BaseMode, component.Component):
# fills in progress fields in all entries based on progs
# returns the # of bytes complete in all the children of fs
def __fill_progress(self, fs, progs):
if not progs: return 0
if not progs:
return 0
tb = 0
for f in fs:
if f[3]: # dir, has some children
@ -261,8 +237,8 @@ class TorrentDetail(BaseMode, component.Component):
if (self.column_widths[i] < 0):
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):
self.messages.append((title, message))
@ -281,7 +257,8 @@ class TorrentDetail(BaseMode, component.Component):
def _on_torrentfilerenamed_event(self, torrent_id, index, new_name):
if torrent_id == self.torrentid:
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):
if torrent_id == self.torrentid:
@ -298,7 +275,8 @@ class TorrentDetail(BaseMode, component.Component):
fe[0] = new_folder.strip("/").rpartition("/")[-1]
#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):
@ -315,24 +293,21 @@ class TorrentDetail(BaseMode, component.Component):
self.file_limit = idx
# default color values
fg = "white"
bg = "black"
attr = ""
if fl[6] == -2: priority = -1 #Mixed
if fl[6] == -2:
pass # Mixed
elif fl[6] == 0:
priority = 0 #Do Not Download
fg = "red"
fg = "red" # Do Not Download
elif fl[6] == 1:
priority = 1 #Normal
pass # Normal
elif fl[6] <= 6:
priority = 2 #High
fg = "yellow"
fg = "yellow" # High
elif fl[6] == 7:
priority = 3 #Highest
fg = "green"
fg = "green" # Highest
if idx >= self.file_off:
# set fg/bg colors based on whether the file is selected/marked or not
@ -364,14 +339,14 @@ class TorrentDetail(BaseMode, component.Component):
#actually draw the dir/file string
if fl[3] and fl[4]: # this is an expanded directory
xchar = 'v'
xchar = "v"
elif fl[3]: # collapsed directory
xchar = '>'
xchar = ">"
else: # file
xchar = '-'
xchar = "-"
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])],
self.column_widths)
@ -381,7 +356,8 @@ class TorrentDetail(BaseMode, component.Component):
if fl[3] and fl[4]:
# recurse if we have children and are expanded
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:
idx += 1
@ -391,7 +367,7 @@ class TorrentDetail(BaseMode, component.Component):
"""
Counts length of the displayed file list.
"""
if file_list == None:
if file_list is None:
file_list = self.file_list
length = 0
if file_list:
@ -403,9 +379,9 @@ class TorrentDetail(BaseMode, component.Component):
def __get_contained_files_count(self, file_list=None, idx=None):
length = 0
if file_list == None:
if file_list is None:
file_list = self.file_list
if idx != None:
if idx is not None:
for element in file_list:
if element[1] == idx:
return self.__get_contained_files_count(file_list=element[3])
@ -443,67 +419,80 @@ class TorrentDetail(BaseMode, component.Component):
#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
if status["download_payload_rate"] > 0:
s = "%sDownloading: {!input!}" % down_color
else:
s = "{!info!}Downloaded: {!input!}"
s+= common.fsize(status["all_time_download"])
s += fsize(status["all_time_download"])
if status["progress"] != 100.0:
s+= "/%s" % common.fsize(status["total_wanted"])
s += "/%s" % fsize(status["total_wanted"])
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"])
self.add_string(off, s); off += 1
self.add_string(off, s)
off += 1
#Print UL info and ratio
if status["upload_payload_rate"] > 0:
s = "%sUploading: {!input!}" % up_color
else:
s = "{!info!}Uploaded: {!input!}"
s+= common.fsize(status["total_uploaded"])
s += fsize(status["total_uploaded"])
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"])
if ratio_str == "-": ratio_str = "inf"
if ratio_str == "-":
ratio_str = "inf"
s += " {!info!}Ratio: {!input!}%s" % ratio_str
self.add_string(off, s); off += 1
self.add_string(off, s)
off += 1
#Seed/peer info
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"])
self.add_string(off, s); off += 1
self.add_string(off, s)
off += 1
#Tracker
if status["message"] == "OK":
color = "{!green!}"
else:
color = "{!red!}"
s = "{!info!}Tracker: {!magenta!}%s{!input!} says \"%s%s{!input!}\"" % (status["tracker_host"], color, status["message"])
self.add_string(off, s); off += 1
s = "{!info!}Tracker: {!magenta!}%s{!input!} says \"%s%s{!input!}\"" % (
status["tracker_host"], color, status["message"])
self.add_string(off, s)
off += 1
#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"]:
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
s = "{!info!}Added: {!input!}%s" % common.fdate(status["time_added"])
self.add_string(off, s); off += 1
s = "{!info!}Added: {!input!}%s" % fdate(status["time_added"])
self.add_string(off, s)
off += 1
#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"]:
s+= ", {!cyan!}%s{!input!} seeding" % ( common.ftime(status["seeding_time"]) )
self.add_string(off, s); off += 1
s += ", {!cyan!}%s{!input!} seeding" % (ftime(status["seeding_time"]))
self.add_string(off, s)
off += 1
#Download Folder
s = "{!info!}Download Folder: {!input!}%s" % status["download_location"]
self.add_string(off, s); off += 1
self.add_string(off, s)
off += 1
#Owner
if status["owner"]:
@ -513,7 +502,7 @@ class TorrentDetail(BaseMode, component.Component):
def refresh(self, lines=None):
# 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()
self.popup = MessagePopup(self, title, msg)
@ -631,10 +620,10 @@ class TorrentDetail(BaseMode, component.Component):
func = lambda idx, data, we=was_empty: self.do_priority(idx, data, we)
if self.marked:
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("_Normal Priority", data=deluge.common.FILE_PRIORITY["Normal Priority"])
self.popup.add_line("_High Priority", data=deluge.common.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("_Do Not Download", data=FILE_PRIORITY["Do Not Download"], foreground="red")
self.popup.add_line("_Normal Priority", data=FILE_PRIORITY["Normal Priority"])
self.popup.add_line("_High Priority", data=FILE_PRIORITY["High Priority"], foreground="yellow")
self.popup.add_line("H_ighest Priority", data=FILE_PRIORITY["Highest Priority"], foreground="green")
self.popup._selected = 1
def __mark_unmark(self, idx):
@ -757,7 +746,8 @@ class TorrentDetail(BaseMode, component.Component):
return total_marked
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:
if idx == self.current_file_idx:
@ -783,7 +773,8 @@ class TorrentDetail(BaseMode, component.Component):
return (idx, true_idx)
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:
if not element[3]:
@ -863,7 +854,7 @@ class TorrentDetail(BaseMode, component.Component):
return
if c > 31 and c < 256:
if chr(c) == 'Q':
if chr(c) == "Q":
from twisted.internet import reactor
if client.connected():
def on_disconnect(result):
@ -872,7 +863,7 @@ class TorrentDetail(BaseMode, component.Component):
else:
reactor.stop()
return
elif chr(c) == 'q':
elif chr(c) == "q":
self.back_to_overview()
return
@ -912,24 +903,24 @@ class TorrentDetail(BaseMode, component.Component):
self.expcol_cur_file()
else:
if c > 31 and c < 256:
if chr(c) == 'm':
if chr(c) == "m":
if self.current_file:
self.__mark_unmark(self.current_file[1])
elif chr(c) == 'r':
elif chr(c) == "r":
self._show_rename_popup()
elif chr(c) == 'c':
elif chr(c) == "c":
self.marked = {}
elif chr(c) == 'a':
elif chr(c) == "a":
torrent_actions_popup(self, [self.torrentid], details=False)
return
elif chr(c) == 'o':
elif chr(c) == "o":
torrent_actions_popup(self, [self.torrentid], action=ACTION.TORRENT_OPTIONS)
return
elif chr(c) == 'h':
elif chr(c) == "h":
self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75)
elif chr(c) == 'j':
elif chr(c) == "j":
self.file_list_up()
if chr(c) == 'k':
if chr(c) == "k":
self.file_list_down()
self.refresh()

View File

@ -1,36 +1,10 @@
#
# statusbars.py
# -*- coding: utf-8 -*-
#
# 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.
#
import deluge.common