diff --git a/deluge/ui/console/__init__.py b/deluge/ui/console/__init__.py index 0747fac8a..5e839a409 100644 --- a/deluge/ui/console/__init__.py +++ b/deluge/ui/console/__init__.py @@ -34,3 +34,4 @@ # UI_PATH = __path__[0] from main import start +assert start # silence pyflakes diff --git a/deluge/ui/console/colors.py b/deluge/ui/console/colors.py index fe0e719c1..1c87a2b19 100644 --- a/deluge/ui/console/colors.py +++ b/deluge/ui/console/colors.py @@ -1,36 +1,10 @@ -# -# colors.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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,19 +15,19 @@ 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, ...} color_pairs = { - ("white", "black"): 0 # Special case, can't be changed + ("white", "black"): 0 # Special case, can't be changed } # Some default color schemes @@ -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: @@ -198,9 +178,9 @@ def parse_color_string(s, encoding="UTF-8"): raise BadColorString("Missing closing '!}'") # Get a list of attributes in the bracketed section - attrs = s[begin+2:end].split(",") + attrs = s[begin + 2:end].split(",") - if len(attrs) == 1 and not attrs[0].strip(' '): + if len(attrs) == 1 and not attrs[0].strip(" "): raise BadColorString("No description in {! !}") def apply_attrs(cp, a): @@ -238,10 +218,10 @@ def parse_color_string(s, encoding="UTF-8"): next_begin = s.find("{!", end) if next_begin == -1: - ret.append((color_pair, replace_tabs(s[end+2:]))) + ret.append((color_pair, replace_tabs(s[end + 2:]))) break else: - ret.append((color_pair, replace_tabs(s[end+2:next_begin]))) + ret.append((color_pair, replace_tabs(s[end + 2:next_begin]))) s = s[next_begin:] if not ret: diff --git a/deluge/ui/console/commander.py b/deluge/ui/console/commander.py index 942932140..80b1126b6 100644 --- a/deluge/ui/console/commander.py +++ b/deluge/ui/console/commander.py @@ -1,40 +1,15 @@ -# -# commander.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # Copyright (C) 2011 Nick Lanham # -# 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) diff --git a/deluge/ui/console/commands/cache.py b/deluge/ui/console/commands/cache.py index d101c6ed6..02dad18d1 100644 --- a/deluge/ui/console/commands/cache.py +++ b/deluge/ui/console/commands/cache.py @@ -1,39 +1,13 @@ -# -# cache.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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") diff --git a/deluge/ui/console/commands/config.py b/deluge/ui/console/commands/config.py index 99cafbe2f..247bad755 100644 --- a/deluge/ui/console/commands/config.py +++ b/deluge/ui/console/commands/config.py @@ -1,42 +1,15 @@ -# -# config.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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() @@ -161,4 +134,4 @@ class Command(BaseCommand): return deferred def complete(self, text): - return [ k for k in component.get("CoreConfig").keys() if k.startswith(text) ] + return [k for k in component.get("CoreConfig").keys() if k.startswith(text)] diff --git a/deluge/ui/console/commands/connect.py b/deluge/ui/console/commands/connect.py index f969c269b..948c2c8a8 100644 --- a/deluge/ui/console/commands/connect.py +++ b/deluge/ui/console/commands/connect.py @@ -1,40 +1,14 @@ -# -# connect.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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)) diff --git a/deluge/ui/console/commands/debug.py b/deluge/ui/console/commands/debug.py index 5fa409058..73419e677 100644 --- a/deluge/ui/console/commands/debug.py +++ b/deluge/ui/console/commands/debug.py @@ -1,55 +1,28 @@ -# -# debug.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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)] diff --git a/deluge/ui/console/commands/gui.py b/deluge/ui/console/commands/gui.py index 7021da3a6..a37df3601 100644 --- a/deluge/ui/console/commands/gui.py +++ b/deluge/ui/console/commands/gui.py @@ -1,38 +1,12 @@ -# -# status.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham # -# 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: diff --git a/deluge/ui/console/commands/halt.py b/deluge/ui/console/commands/halt.py index 0bd9708ad..932142f8c 100644 --- a/deluge/ui/console/commands/halt.py +++ b/deluge/ui/console/commands/halt.py @@ -1,40 +1,14 @@ -# -# halt.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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") diff --git a/deluge/ui/console/commands/help.py b/deluge/ui/console/commands/help.py index 9e7eff4b4..e922a3a55 100644 --- a/deluge/ui/console/commands/help.py +++ b/deluge/ui/console/commands/help.py @@ -1,49 +1,23 @@ -# help.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 class Command(BaseCommand): """displays help on other commands""" - usage = "Usage: help [command]" + usage = "Usage: help [command]" def handle(self, *args, **options): self.console = component.get("ConsoleUI") @@ -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 " --help"') + self.console.write("For help on a specific command, use ' --help'") self.console.set_batch_write(False) return deferred diff --git a/deluge/ui/console/commands/info.py b/deluge/ui/console/commands/info.py index 7242eff00..14205823f 100644 --- a/deluge/ui/console/commands/info.py +++ b/deluge/ui/console/commands/info.py @@ -1,37 +1,11 @@ -# -# info.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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,39 +21,41 @@ from deluge.ui.console.modes import format_utils strwidth = format_utils.strwidth -STATUS_KEYS = ["state", - "download_location", - "tracker_host", - "tracker_status", - "next_announce", - "name", - "total_size", - "progress", - "num_seeds", - "total_seeds", - "num_peers", - "total_peers", - "eta", - "download_payload_rate", - "upload_payload_rate", - "ratio", - "distributed_copies", - "num_pieces", - "piece_length", - "total_done", - "files", - "file_priorities", - "file_progress", - "peers", - "is_seed", - "is_finished", - "active_time", - "seeding_time" - ] +STATUS_KEYS = [ + "state", + "download_location", + "tracker_host", + "tracker_status", + "next_announce", + "name", + "total_size", + "progress", + "num_seeds", + "total_seeds", + "num_peers", + "total_peers", + "eta", + "download_payload_rate", + "upload_payload_rate", + "ratio", + "distributed_copies", + "num_pieces", + "piece_length", + "total_done", + "files", + "file_priorities", + "file_progress", + "peers", + "is_seed", + "is_finished", + "active_time", + "seeding_time" +] # Add filter specific state to torrent states STATES = ["Active"] + common.TORRENT_STATE + def format_progressbar(progress, width): """ Returns a string of a progress bar. @@ -90,9 +66,9 @@ def format_progressbar(progress, width): """ - w = width - 2 # we use a [] for the beginning and end + w = width - 2 # we use a [] for the beginning and end s = "[" - p = int(round((progress/100) * w)) + p = int(round((progress / 100) * w)) s += "#" * p s += "-" * (w - p) s += "]" @@ -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 ] [ [ ...]]\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*):\n"\ - " | First press of will output up to 15 matches;\n"\ - " | hitting a second time, will print 15 more matches; \n"\ - " | and a third press will print all remaining matches.\n"\ - " | (To modify behaviour of third , set `third_tab_lists_all` to False)" + usage = """Usage: info [-v | -d | -s ] [ [ ...]] + You can give the first few characters of a torrent-id to identify the torrent. + info * will list all torrents. + + Tab Completion (info *pattern*): + | First press of will output up to 15 matches; + | hitting a second time, will print 15 more matches; + | and a third press will print all remaining matches. + | (To modify behaviour of third , 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): @@ -178,8 +150,8 @@ class Command(BaseCommand): if options["state"] in STATES: status_dict["state"] = options["state"] else: - self.console.write("Invalid state: %s"%options["state"]) - self.console.write("Possible values are: %s."%(", ".join(STATES))) + self.console.write("Invalid state: %s" % options["state"]) + self.console.write("Possible values are: %s." % (", ".join(STATES))) return d = client.core.get_torrents_status(status_dict, STATUS_KEYS) @@ -188,29 +160,28 @@ 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 - if depth >= len(prevpath): + indent = " " * depth * spaces_per_level + if depth >= len(prevpath): self.console.write("%s{!cyan!}%s" % (indent, subdir)) elif subdir != prevpath[depth]: self.console.write("%s{!cyan!}%s" % (indent, subdir)) 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"]) @@ -226,20 +197,20 @@ class Command(BaseCommand): col_priority += "{!success!}" else: col_priority += "{!input!}" - col_priority+= fp + col_priority += fp rf = format_utils.remove_formatting 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 space_left = cols - tlen(col_all_info) #And how much we will potentially have with the longest possible column maxlen_space_left = cols - tlen(" (1000.0 MiB) 100.00% Priority: Do Not Download") - if maxlen_space_left > tlen(col_filename) + 1: + if maxlen_space_left > tlen(col_filename) + 1: #If there is enough space, pad it all nicely col_all_info = "" col_all_info += " (" @@ -253,16 +224,15 @@ class Command(BaseCommand): col_all_info += col_priority col_all_info += " " * spaces_to_add #And remember to put it to the left! - col_filename = format_utils.pad_string(col_filename, maxlen_space_left - 2, side = "right") + col_filename = format_utils.pad_string(col_filename, maxlen_space_left - 2, side="right") elif space_left > tlen(col_filename) + 1: #If there is enough space, put the info to the right - col_filename = format_utils.pad_string(col_filename, space_left - 2, side = "right") + col_filename = format_utils.pad_string(col_filename, space_left - 2, side="right") else: #And if there is not, shorten the name - col_filename = format_utils.trim_string( col_filename, space_left, True) + col_filename = format_utils.trim_string(col_filename, space_left, True) self.console.write(col_filename + col_all_info) - prevpath = filepath def show_peer_info(self, torrent_id, status): @@ -375,43 +345,43 @@ class Command(BaseCommand): s = "%s%s" % (colors.state_color[status["state"]], "[" + status["state"][0] + "]") - s+= " {!info!}" + ("%.2f%%" % status["progress"]).ljust(7, " ") - s+= " {!input!}%s" % (status["name"]) + s += " {!info!}" + ("%.2f%%" % status["progress"]).ljust(7, " ") + s += " {!input!}%s" % (status["name"]) #Shorten the ID if it's necessary. Pretty hacky #I _REALLY_ should make a nice function for it that can partition and shorten stuff - space_left = cols - strwidth("[s] 100.00% " + status["name"] + " "*3) - 2 + space_left = cols - strwidth("[s] 100.00% " + status["name"] + " " * 3) - 2 if space_left >= len(torrent_id) - 2: #There's enough space, print it s += " {!cyan!}%s" % torrent_id else: #Shorten the ID - a = int(space_left * 2/3.0) + a = int(space_left * 2 / 3.0) b = space_left - a if a < 8: - b = b - (8-a) + b = b - (8 - a) a = 8 if b < 0: - a+= b + a += b b = 0 if a > 8: #Print the shortened ID - s += " {!cyan!}%s" % (torrent_id[0:a] + ".." + torrent_id[-b-1:-1]) + s += " {!cyan!}%s" % (torrent_id[0:a] + ".." + torrent_id[-b - 1:-1]) else: #It has wrapped over to the second row anyway s += " {!cyan!}%s" % torrent_id self.console.write(s) dl_info = "{!info!}DL: {!input!}" - dl_info+= "%s" % common.fsize(status["total_done"]) + dl_info += "%s" % common.fsize(status["total_done"]) if status["total_done"] != status["total_size"]: - dl_info+= "/%s" % common.fsize(status["total_size"]) + dl_info += "/%s" % common.fsize(status["total_size"]) if status["download_payload_rate"] > 0: dl_info += " @ %s%s" % (down_color, common.fspeed(status["download_payload_rate"])) ul_info = " {!info!}UL: {!input!}" - ul_info+= "%s" % common.fsize(status["ratio"] * status["total_done"]) + ul_info += "%s" % common.fsize(status["ratio"] * status["total_done"]) if status["upload_payload_rate"] > 0: ul_info += " @ %s%s" % (up_color, common.fspeed(status["upload_payload_rate"])) diff --git a/deluge/ui/console/commands/manage.py b/deluge/ui/console/commands/manage.py index a026c762a..baa9569fc 100644 --- a/deluge/ui/console/commands/manage.py +++ b/deluge/ui/console/commands/manage.py @@ -1,48 +1,18 @@ # -*- coding: utf-8 -*- # -# manage.py -# # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 @@ -59,15 +29,14 @@ torrent_options = { "remove_at_ratio": bool, "move_on_completed": bool, "move_on_completed_path": str - } +} 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 [ [ ...]]\n"\ " manage --set " @@ -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): @@ -105,7 +73,7 @@ class Command(BaseCommand): return request_options.append(opt) if not request_options: - request_options = [ opt for opt in torrent_options.keys() ] + request_options = [opt for opt in torrent_options.keys()] request_options.append('name') d = client.core.get_torrents_status({"id": torrent_ids}, request_options) @@ -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) diff --git a/deluge/ui/console/commands/move.py b/deluge/ui/console/commands/move.py index 6a6f75522..9b630a54a 100644 --- a/deluge/ui/console/commands/move.py +++ b/deluge/ui/console/commands/move.py @@ -1,36 +1,10 @@ -# -# move.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham # -# 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 diff --git a/deluge/ui/console/commands/pause.py b/deluge/ui/console/commands/pause.py index 04c0f2347..b670c4a1c 100644 --- a/deluge/ui/console/commands/pause.py +++ b/deluge/ui/console/commands/pause.py @@ -1,40 +1,14 @@ -# -# pause.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 [ * | [ ...] ]" + def handle(self, *args, **options): self.console = component.get("ConsoleUI") diff --git a/deluge/ui/console/commands/plugin.py b/deluge/ui/console/commands/plugin.py index c5af8b55e..149c3eff5 100644 --- a/deluge/ui/console/commands/plugin.py +++ b/deluge/ui/console/commands/plugin.py @@ -1,43 +1,16 @@ -# -# plugin.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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 ] \n"\ - " plugin [ -d | --disable ] \n"\ - " plugin [ -i | --install ] \n"\ - " plugin [ -r | --reload]" + usage = """Usage: plugin [ -l | --list ] + plugin [ -s | --show ] + plugin [ -e | --enable ] + plugin [ -d | --disable ] + plugin [ -i | --install ] + 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) diff --git a/deluge/ui/console/commands/quit.py b/deluge/ui/console/commands/quit.py index 09da2fe2b..0272f87f2 100644 --- a/deluge/ui/console/commands/quit.py +++ b/deluge/ui/console/commands/quit.py @@ -1,38 +1,13 @@ -# -# quit.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich -# Coptright (C) 2009 Andrew Resch -# -# 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 # +# 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): diff --git a/deluge/ui/console/commands/recheck.py b/deluge/ui/console/commands/recheck.py index f5c9d7564..d0f4e0202 100644 --- a/deluge/ui/console/commands/recheck.py +++ b/deluge/ui/console/commands/recheck.py @@ -1,39 +1,13 @@ -# -# recheck.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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 [ * | [ ...] ]" + 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 diff --git a/deluge/ui/console/commands/resume.py b/deluge/ui/console/commands/resume.py index 6b9faf8a9..9e61b3bcc 100644 --- a/deluge/ui/console/commands/resume.py +++ b/deluge/ui/console/commands/resume.py @@ -1,41 +1,14 @@ -# -# resume.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 [ * | [ ...] ]" + 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 diff --git a/deluge/ui/console/commands/rm.py b/deluge/ui/console/commands/rm.py index f438a219b..0a6e7ccd8 100644 --- a/deluge/ui/console/commands/rm.py +++ b/deluge/ui/console/commands/rm.py @@ -1,42 +1,16 @@ -# -# rm.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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,11 +18,11 @@ from deluge.ui.console.main import BaseCommand class Command(BaseCommand): """Remove a torrent""" usage = "Usage: rm " - aliases = ['del'] + aliases = ["del"] option_list = BaseCommand.option_list + ( - make_option('--remove_data', action='store_true', default=False, - help="remove the torrent's data"), + make_option("--remove_data", action="store_true", default=False, + help="remove the torrent's data"), ) def handle(self, *args, **options): @@ -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 diff --git a/deluge/ui/console/commands/status.py b/deluge/ui/console/commands/status.py index 122a3cdb9..80060b20e 100644 --- a/deluge/ui/console/commands/status.py +++ b/deluge/ui/console/commands/status.py @@ -1,35 +1,10 @@ -# -# status.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham # -# 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]" diff --git a/deluge/ui/console/commands/update_tracker.py b/deluge/ui/console/commands/update_tracker.py index 487fc1b8e..8e80b83a2 100644 --- a/deluge/ui/console/commands/update_tracker.py +++ b/deluge/ui/console/commands/update_tracker.py @@ -1,42 +1,14 @@ -# -# rm.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 diff --git a/deluge/ui/console/eventlog.py b/deluge/ui/console/eventlog.py index 621dfe33f..ad08fc2b5 100644 --- a/deluge/ui/console/eventlog.py +++ b/deluge/ui/console/eventlog.py @@ -1,38 +1,11 @@ -# -# eventlog.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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 diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py index 7d3d2546c..ffec72051 100644 --- a/deluge/ui/console/main.py +++ b/deluge/ui/console/main.py @@ -1,37 +1,11 @@ -# -# main.py +# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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) @@ -84,16 +60,17 @@ class Console(_UI): result = formatter.format_heading(self.title) formatter.indent() if self.description: - result += "%s\n"%formatter.format_description(self.description) + 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... @@ -165,7 +144,7 @@ class DelugeHelpFormatter (optparse.IndentedHelpFormatter): opts = "%*s%s\n" % (self.current_indent, "", opts) opts = self._format_colors(opts) indent_first = self.help_position - else: # start help on same line as opts + else: # start help on same line as opts opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) opts = self._format_colors(opts) indent_first = 0 @@ -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): @@ -202,13 +182,13 @@ class OptionParser(optparse.OptionParser): """ raise Exception(msg) - def print_usage(self, file = None): + def print_usage(self, file=None): console = component.get("ConsoleUI") if self.usage: for line in self.get_usage().splitlines(): console.write(line) - def print_help(self, file = None): + def print_help(self, file=None): console = component.get("ConsoleUI") console.set_batch_write(True) for line in self.format_help().splitlines(): @@ -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 @@ -402,7 +378,7 @@ Please use commands from the command line, eg:\n """ if self.interactive and isinstance(self.screen, deluge.ui.console.modes.legacy.Legacy): return self.screen.match_torrent(string) - matches = [] + matches = [] string = string.decode(self.encoding) for tid, name in self.torrents: @@ -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) diff --git a/deluge/ui/console/modes/add_util.py b/deluge/ui/console/modes/add_util.py index dd43f7e3c..8cb0a3c35 100644 --- a/deluge/ui/console/modes/add_util.py +++ b/deluge/ui/console/modes/add_util.py @@ -1,61 +1,33 @@ +# -*- coding: utf-8 -*- # -# add_util.py -# -# Copyright (C) 2011 Nick Lanham -# -# Modified function from commands/add.py: # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch +# Copyright (C) 2011 Nick Lanham # -# 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): - sentinal+=1 - if sentinal > 65535: + sentinal += 1 + if sentinal > 65535: log.error("Can't fix brackets in path, path contains all possible sentinal characters") return path newpath = path.replace("]", unichr(sentinal)) @@ -63,16 +35,17 @@ 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"]: t_options["download_location"] = os.path.expanduser(options["path"]) 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_url = (not (options["path_type"] == 1)) and (deluge.common.is_url(t_file) or options["path_type"] == 2) + 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) diff --git a/deluge/ui/console/modes/addtorrents.py b/deluge/ui/console/modes/addtorrents.py index 3ba8fc70a..03fe0d2e9 100644 --- a/deluge/ui/console/modes/addtorrents.py +++ b/deluge/ui/console/modes/addtorrents.py @@ -1,37 +1,10 @@ # -*- coding: utf-8 -*- # -# addtorrents.py -# # Copyright (C) 2012 Arek Stefański # -# 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 @@ -168,8 +140,8 @@ class AddTorrents(BaseMode, component.Component): row = [dirname, size, time, full_path, 1] - self.raw_rows.append( row ) - self.raw_rows_dirs.append( row ) + self.raw_rows.append(row) + self.raw_rows_dirs.append(row) #Highlight the directory we came from if self.path_stack_pos < len(self.path_stack): @@ -189,8 +161,8 @@ class AddTorrents(BaseMode, component.Component): row = [filename, size, time, full_path, 0] - self.raw_rows.append( row ) - self.raw_rows_files.append( row ) + self.raw_rows.append(row) + self.raw_rows_files.append(row) self.__sort_rows() @@ -201,7 +173,7 @@ class AddTorrents(BaseMode, component.Component): self.raw_rows_dirs.sort(key=lambda r: r[0].lower()) - if self.sort_column == "name": + if self.sort_column == "name": self.raw_rows_files.sort(key=lambda r: r[0].lower(), reverse=self.reverse_sort) elif self.sort_column == "date": self.raw_rows_files.sort(key=lambda r: r[2], reverse=self.reverse_sort) @@ -275,7 +247,7 @@ class AddTorrents(BaseMode, component.Component): self.refresh() - def refresh(self,lines=None): + def refresh(self, lines=None): # Update the status bars self.stdscr.erase() @@ -287,7 +259,7 @@ class AddTorrents(BaseMode, component.Component): string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help" - string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr + string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr self.add_string(self.rows - 1, string) except: @@ -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) @@ -361,7 +335,7 @@ class AddTorrents(BaseMode, component.Component): color_string = "{!white,blue,bold!}" self.add_string(off, color_string + row) - off+= 1 + off += 1 if off > self.rows - 2: break @@ -403,7 +377,7 @@ class AddTorrents(BaseMode, component.Component): self.path_stack = self.path_stack[:self.path_stack_pos] self.path_stack.append(dirname) - path = os.path.join(*self.path_stack[:self.path_stack_pos+1]) + path = os.path.join(*self.path_stack[:self.path_stack_pos + 1]) if not os.access(path, os.R_OK): self.path_stack = self.path_stack[:self.path_stack_pos] @@ -423,23 +397,20 @@ class AddTorrents(BaseMode, component.Component): def _show_add_dialog(self): def _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)) - ress["fail"]+=1 - ress["fmsg"].append("{!input!} * %s: {!error!}%s"%(t_file, msg)) - if (ress["succ"]+ress["fail"]) >= ress["total"]: + log.debug("failed to add torrent: %s: %s" % (t_file, msg)) + ress["fail"] += 1 + ress["fmsg"].append("{!input!} * %s: {!error!}%s" % (t_file, msg)) + if (ress["succ"] + ress["fail"]) >= ress["total"]: self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"]) def success_cb(tid, t_file, ress): if tid: - log.debug("added torrent: %s (%s)"%(t_file, tid)) - ress["succ"]+=1 - if (ress["succ"]+ress["fail"]) >= ress["total"]: + log.debug("added torrent: %s (%s)" % (t_file, tid)) + ress["succ"] += 1 + if (ress["succ"] + ress["fail"]) >= ress["total"]: self.alltorrentmode._report_add_status(ress["succ"], ress["fail"], ress["fmsg"]) else: fail_cb("Already in session (probably)", t_file, ress) @@ -485,7 +456,6 @@ class AddTorrents(BaseMode, component.Component): self.popup.add_text_input("Download Folder:", "location", dl) self.popup.add_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) diff --git a/deluge/ui/console/modes/alltorrents.py b/deluge/ui/console/modes/alltorrents.py index e2f038291..125f90a3e 100644 --- a/deluge/ui/console/modes/alltorrents.py +++ b/deluge/ui/console/modes/alltorrents.py @@ -1,58 +1,27 @@ # -*- coding: utf-8 -*- # -# alltorrents.py -# # Copyright (C) 2011 Nick Lanham # -# 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,17 +102,18 @@ files in the torrent and the ability to set file priorities. input something """ + class FILTER: - ALL=0 - ACTIVE=1 - DOWNLOADING=2 - SEEDING=3 - PAUSED=4 - CHECKING=5 - ERROR=6 - QUEUED=7 - ALLOCATING=8 - MOVING=9 + ALL = 0 + ACTIVE = 1 + DOWNLOADING = 2 + SEEDING = 3 + PAUSED = 4 + CHECKING = 5 + ERROR = 6 + QUEUED = 7 + ALLOCATING = 8 + MOVING = 9 DEFAULT_PREFS = { "show_queue": True, @@ -199,21 +169,21 @@ DEFAULT_PREFS = { "down_limit_width": 7, "up_limit_width": 7, "shared_width": 10, - "ignore_duplicate_lines": False, - "move_selection": True, - "third_tab_lists_all": False, - "torrents_per_tab_press": 15, - "sort_primary": "queue", - "sort_secondary": "name", - "separate_complete": True, - "ring_bell": False, - "save_legacy_history": True, - "first_run": True, - "addtorrents_show_misc_files": False, #TODO: Showing/hiding this - "addtorrents_show_hidden_folders": False, #TODO: Showing/hiding this - "addtorrents_sort_column": "date", - "addtorrents_reverse_sort": True, - "addtorrents_last_path": "~" + "ignore_duplicate_lines": False, + "move_selection": True, + "third_tab_lists_all": False, + "torrents_per_tab_press": 15, + "sort_primary": "queue", + "sort_secondary": "name", + "separate_complete": True, + "ring_bell": False, + "save_legacy_history": True, + "first_run": True, + "addtorrents_show_misc_files": False, # TODO: Showing/hiding this + "addtorrents_show_hidden_folders": False, # TODO: Showing/hiding this + "addtorrents_sort_column": "date", + "addtorrents_reverse_sort": True, + "addtorrents_last_path": "~" } column_pref_names = ["queue", "name", "size", "state", "progress", "seeds", @@ -300,13 +270,14 @@ 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 self.numtorrents = -1 self._cached_rows = {} self.cursel = 1 - self.curoff = 1 # TODO: this should really be 0 indexed + self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" self.popup = None self.messages = deque() @@ -357,15 +328,15 @@ class AllTorrents(BaseMode, component.Component): ("Availability", format_utils.format_float, ("distributed_copies",)), ("Pieces", format_utils.format_pieces, ("num_pieces", "piece_length")), ("Seed Rank", str, ("seed_rank",)), - ] + ] self.__status_keys = ["name", "state", "download_payload_rate", "upload_payload_rate", - "progress", "eta", "download_location", "all_time_download", "total_uploaded", - "ratio", "num_seeds", "total_seeds", "num_peers", "total_peers", - "active_time", "seeding_time", "last_seen_complete", "time_added", - "completed_time", "distributed_copies", "num_pieces", "piece_length", - "seed_rank" - ] + "progress", "eta", "download_location", "all_time_download", "total_uploaded", + "ratio", "num_seeds", "total_seeds", "num_peers", "total_peers", + "active_time", "seeding_time", "last_seen_complete", "time_added", + "completed_time", "distributed_copies", "num_pieces", "piece_length", + "seed_rank" + ] self.legacy_mode = Legacy(self.stdscr, self.encoding) @@ -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"]: @@ -414,17 +385,17 @@ class AllTorrents(BaseMode, component.Component): self.refresh() def __update_columns(self): - self.column_widths = [self.config["%s_width"%c] for c in self.__cols_to_show] - req = sum(filter(lambda x:x >= 0, self.column_widths)) - if (req > self.cols): # can't satisfy requests, just spread out evenly - cw = int(self.cols/len(self.__columns)) + self.column_widths = [self.config["%s_width" % c] for c in self.__cols_to_show] + req = sum(filter(lambda x: x >= 0, self.column_widths)) + if (req > self.cols): # can't satisfy requests, just spread out evenly + cw = int(self.cols / len(self.__columns)) for i in range(0, len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0, self.column_widths)) if (var_cols > 0): - vw = int(rem/var_cols) + vw = int(rem / var_cols) for i in range(0, len(self.column_widths)): if (self.column_widths[i] < 0): self.column_widths[i] = vw @@ -457,7 +428,7 @@ class AllTorrents(BaseMode, component.Component): self.column_string += ccol def set_state(self, state, refresh): - self.curstate = state # cache in case we change sort order + self.curstate = state # cache in case we change sort order newnames = [] self._cached_rows = {} self._sorted_ids = self._sort_torrents(self.curstate) @@ -492,50 +463,48 @@ class AllTorrents(BaseMode, component.Component): def current_torrent_id(self): if self._sorted_ids: - return self._sorted_ids[self.cursel-1] + return self._sorted_ids[self.cursel - 1] else: return None def _selected_torrent_ids(self): ret = [] for i in self.marked: - ret.append(self._sorted_ids[i-1]) + ret.append(self._sorted_ids[i - 1]) return ret def _on_torrent_status(self, state): 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: info = state[f[2][0]] - nl = len(f[0])+4 - if (nl+len(info))>self.popup.width: - self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0], info[:(self.popup.width - nl)])) + nl = len(f[0]) + 4 + if (nl + len(info)) > self.popup.width: + self.popup.add_line("{!info!}%s: {!input!}%s" % (f[0], info[:(self.popup.width - nl)])) info = info[(self.popup.width - nl):] - n = self.popup.width-3 - chunks = [info[i:i+n] for i in xrange(0, len(info), n)] + n = self.popup.width - 3 + chunks = [info[i:i + n] for i in xrange(0, len(info), n)] for c in chunks: - self.popup.add_line(" %s"%c) + self.popup.add_line(" %s" % c) else: - self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0], info)) + self.popup.add_line("{!info!}%s: {!input!}%s" % (f[0], info)) self.refresh() else: self.__torrent_info_id = None - def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args) if self.popup: @@ -564,7 +533,7 @@ class AllTorrents(BaseMode, component.Component): if not state: return {} - s_primary = self.config["sort_primary"] + s_primary = self.config["sort_primary"] s_secondary = self.config["sort_secondary"] result = state @@ -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] @@ -585,10 +555,10 @@ class AllTorrents(BaseMode, component.Component): # and if it's a string first_element = state[state.keys()[0]] if field in first_element: - is_string = isinstance( first_element[field], basestring) + is_string = isinstance(first_element[field], basestring) - sort_key = lambda s:sg(s)[field] - sort_key2 = lambda s:sg(s)[field].lower() + sort_key = lambda s: sg(s)[field] + sort_key2 = lambda s: sg(s)[field].lower() #If it's a string, sort case-insensitively but preserve A>a order if is_string: @@ -617,7 +587,7 @@ class AllTorrents(BaseMode, component.Component): def _format_queue(self, qnum): if (qnum >= 0): - return "%d"%(qnum+1) + return "%d" % (qnum + 1) else: return "" @@ -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]: @@ -679,35 +649,35 @@ class AllTorrents(BaseMode, component.Component): component.stop(["AllTorrents"]).addCallback(dolegacy) def _torrent_filter(self, idx, data): - if data==FILTER.ALL: + if data == FILTER.ALL: self.__status_dict = {} self._curr_filter = None - elif data==FILTER.ACTIVE: - self.__status_dict = {"state":"Active"} + elif data == FILTER.ACTIVE: + self.__status_dict = {"state": "Active"} self._curr_filter = "Active" - elif data==FILTER.DOWNLOADING: - self.__status_dict = {"state":"Downloading"} + elif data == FILTER.DOWNLOADING: + self.__status_dict = {"state": "Downloading"} self._curr_filter = "Downloading" - elif data==FILTER.SEEDING: - self.__status_dict = {"state":"Seeding"} + elif data == FILTER.SEEDING: + self.__status_dict = {"state": "Seeding"} self._curr_filter = "Seeding" - elif data==FILTER.PAUSED: - self.__status_dict = {"state":"Paused"} + elif data == FILTER.PAUSED: + self.__status_dict = {"state": "Paused"} self._curr_filter = "Paused" - elif data==FILTER.CHECKING: - self.__status_dict = {"state":"Checking"} + elif data == FILTER.CHECKING: + self.__status_dict = {"state": "Checking"} self._curr_filter = "Checking" - elif data==FILTER.ERROR: - self.__status_dict = {"state":"Error"} + elif data == FILTER.ERROR: + self.__status_dict = {"state": "Error"} self._curr_filter = "Error" - elif data==FILTER.QUEUED: - self.__status_dict = {"state":"Queued"} + elif data == FILTER.QUEUED: + self.__status_dict = {"state": "Queued"} self._curr_filter = "Queued" - elif data==FILTER.ALLOCATING: - self.__status_dict = {"state":"Allocating"} + elif data == FILTER.ALLOCATING: + self.__status_dict = {"state": "Allocating"} self._curr_filter = "Allocating" - elif data==FILTER.MOVING: - self.__status_dict = {"state":"Moving"} + elif data == FILTER.MOVING: + self.__status_dict = {"state": "Moving"} self._curr_filter = "Moving" self._go_top = True @@ -728,11 +698,11 @@ class AllTorrents(BaseMode, component.Component): def _report_add_status(self, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: - self.report_message("Torrents Added", "{!success!}Successfully added %d torrent(s)"%succ_cnt) + self.report_message("Torrents Added", "{!success!}Successfully added %d torrent(s)" % succ_cnt) else: - msg = ("{!error!}Failed to add the following %d torrent(s):\n {!input!}"%fail_cnt)+"\n ".join(fail_msgs) + msg = ("{!error!}Failed to add the following %d torrent(s):\n {!input!}" % fail_cnt) + "\n ".join(fail_msgs) if succ_cnt != 0: - msg += "\n \n{!success!}Successfully added %d torrent(s)"%succ_cnt + msg += "\n \n{!success!}Successfully added %d torrent(s)" % succ_cnt self.report_message("Torrent Add Report", msg) def _show_torrent_add_popup(self): @@ -741,11 +711,11 @@ class AllTorrents(BaseMode, component.Component): def fail_cb(msg, url): log.debug("failed to add torrent: %s: %s" % (url, msg)) error_msg = "{!input!} * %s: {!error!}%s" % (url, msg) - self._report_add_status(0, 1, [error_msg] ) + self._report_add_status(0, 1, [error_msg]) def success_cb(tid, url): if tid: - log.debug("added torrent: %s (%s)"%(url, tid)) + log.debug("added torrent: %s (%s)" % (url, tid)) self._report_add_status(1, 0, []) else: fail_cb("Already in session (probably)", url) @@ -794,7 +764,7 @@ class AllTorrents(BaseMode, component.Component): if not data: return - if data == 1: + if data == 1: self.show_addtorrents_screen() elif data == 2: show_add_url_popup() @@ -804,7 +774,6 @@ class AllTorrents(BaseMode, component.Component): self.popup.add_line("From _URL or Magnet", data=2) self.popup.add_line("_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] @@ -843,7 +808,7 @@ class AllTorrents(BaseMode, component.Component): self.popup = pu self.refresh() - def refresh(self,lines=None): + def refresh(self, lines=None): #log.error("ref") #import traceback #traceback.print_stack() @@ -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,10 +829,10 @@ 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)) + self.add_string(0, "%s {!filterstatus!}Current filter: %s" % (self.statusbars.topbar, self._curr_filter)) self.add_string(1, self.column_string) if self.entering_search: @@ -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 @@ -887,7 +853,7 @@ class AllTorrents(BaseMode, component.Component): string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help" - string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr + string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr self.add_string(self.rows - 1, string) except: @@ -896,7 +862,7 @@ class AllTorrents(BaseMode, component.Component): # add all the torrents if self.numtorrents == 0: msg = "No torrents match filter".center(self.cols) - self.add_string(3, "{!info!}%s"%msg) + self.add_string(3, "{!info!}%s" % msg) elif self.numtorrents > 0: tidx = self.curoff currow = 2 @@ -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 - fr = format_utils.format_row + gcv = console_column.get_column_value + fr = format_utils.format_row cols = self.__columns colw = self.column_widths - cr = self._cached_rows + 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 + for i in range(tidx - 1, tidx - 1 + self.rows - 3): + if i >= self.numtorrents: + break todraw += [draw_row(i)] for row in todraw: @@ -935,8 +906,8 @@ class AllTorrents(BaseMode, component.Component): bg = "black" attr = None if lines: - tidx = lines.pop()+1 - currow = tidx-self.curoff+2 + tidx = lines.pop() + 1 + currow = tidx - self.curoff + 2 if tidx in self.marked: bg = "blue" @@ -964,7 +935,7 @@ class AllTorrents(BaseMode, component.Component): fg = "green" if self.entering_search and len(self.search_string) > 1: - lcase_name = self.torrent_names[tidx-1].lower() + lcase_name = self.torrent_names[tidx - 1].lower() sstring_lower = self.search_string.lower() if lcase_name.find(sstring_lower) != -1: if tidx == self.cursel: @@ -978,12 +949,12 @@ class AllTorrents(BaseMode, component.Component): attr = "bold" if attr: - colorstr = "{!%s,%s,%s!}"%(fg, bg, attr) + colorstr = "{!%s,%s,%s!}" % (fg, bg, attr) else: - colorstr = "{!%s,%s!}"%(fg, bg) + colorstr = "{!%s,%s!}" % (fg, bg) try: - self.add_string(currow, "%s%s"%(colorstr, row[0]), trim=False) + self.add_string(currow, "%s%s" % (colorstr, row[0]), trim=False) except: #Yeah, this should be fixed in some better way pass @@ -997,7 +968,7 @@ class AllTorrents(BaseMode, component.Component): #self.stdscr.redrawwin() if self.entering_search: curses.curs_set(2) - self.stdscr.move(self.rows-1, len(self.search_string)+17) + self.stdscr.move(self.rows - 1, len(self.search_string) + 17) else: curses.curs_set(0) @@ -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) @@ -1041,7 +1011,7 @@ class AllTorrents(BaseMode, component.Component): :returns: Nothing """ - if direction == "first": + if direction == "first": search_space = enumerate(self.torrent_names) elif direction == "last": search_space = enumerate(self.torrent_names) @@ -1053,7 +1023,7 @@ class AllTorrents(BaseMode, component.Component): search_space = search_space[self.cursel:] elif direction == "previous": search_space = enumerate(self.torrent_names) - search_space = list(search_space)[:self.cursel-1] + search_space = list(search_space)[:self.cursel - 1] search_space = reversed(search_space) search_string = self.search_string.lower() @@ -1063,10 +1033,10 @@ class AllTorrents(BaseMode, component.Component): if skip > 0: skip -= 1 continue - self.cursel = (i+1) + self.cursel = (i + 1) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 - elif ((self.curoff +1) > self.cursel): + elif ((self.curoff + 1) > self.cursel): self.curoff = max(1, self.cursel - 1) self.search_state = SEARCH_SUCCESS return @@ -1078,7 +1048,7 @@ class AllTorrents(BaseMode, component.Component): self.search_state = SEARCH_START_REACHED def __update_search(self, c): - cname = self.torrent_names[self.cursel-1] + cname = self.torrent_names[self.cursel - 1] if c == curses.KEY_BACKSPACE or c == 127: if self.search_string: self.search_string = self.search_string[:-1] @@ -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,21 +1168,23 @@ 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] + effected_lines = [self.cursel - 1, self.cursel] elif c == curses.KEY_PPAGE: - self._scroll_up(int(self.rows/2)) + 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] + effected_lines = [self.cursel - 2, self.cursel - 1] elif c == curses.KEY_NPAGE: - self._scroll_down(int(self.rows/2)) + self._scroll_down(int(self.rows / 2)) elif c == curses.KEY_HOME: self._scroll_up(self.cursel) elif c == curses.KEY_END: - self._scroll_down(self.numtorrents-self.cursel) + self._scroll_down(self.numtorrents - self.cursel) elif c == curses.KEY_DC: if self.cursel not in self.marked: self.marked.append(self.cursel) @@ -1235,46 +1207,47 @@ 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': + effected_lines = [self.cursel - 1, self.cursel] + elif chr(c) == "k": if not self._scroll_down(1): - effected_lines = [self.cursel-2, self.cursel-1] - elif chr(c) == 'i': + effected_lines = [self.cursel - 2, self.cursel - 1] + 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': + effected_lines = [self.cursel - 1] + elif chr(c) == "M": if self.last_mark >= 0: - if (self.cursel+1) > self.last_mark: - mrange = range(self.last_mark, self.cursel+1) + if (self.cursel + 1) > self.last_mark: + mrange = range(self.last_mark, self.cursel + 1) else: - mrange = range(self.cursel-1, self.last_mark) + mrange = range(self.cursel - 1, self.last_mark) self.marked.extend(mrange[1:]) effected_lines = mrange else: self._mark_unmark(self.cursel) - effected_lines = [self.cursel-1] - elif chr(c) == 'c': + effected_lines = [self.cursel - 1] + 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 diff --git a/deluge/ui/console/modes/basemode.py b/deluge/ui/console/modes/basemode.py index fe0b3663c..bf49c5e1c 100644 --- a/deluge/ui/console/modes/basemode.py +++ b/deluge/ui/console/modes/basemode.py @@ -1,39 +1,11 @@ -# -# basemode.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham +# Copyright (C) 2009 Andrew Resch # -# Most code in this file taken from screen.py: -# Copyright (C) 2009 Andrew Resch -# -# 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): @@ -125,7 +100,7 @@ class BaseMode(CursesStdIO): def on_resize_norefresh(self, *args): log.debug("on_resize_from_signal") # Get the new rows and cols value - self.rows, self.cols = struct.unpack("hhhh", ioctl(0, termios.TIOCGWINSZ, "\000"*8))[0:2] + self.rows, self.cols = struct.unpack("hhhh", ioctl(0, termios.TIOCGWINSZ, "\000" * 8))[0:2] curses.resizeterm(self.rows, self.cols) def on_resize(self, *args): @@ -135,7 +110,7 @@ class BaseMode(CursesStdIO): def connectionLost(self, reason): self.close() - def add_string(self, row, string, scr=None, col = 0, pad=True, trim=True): + def add_string(self, row, string, scr=None, col=0, pad=True, trim=True): """ Adds a string to the desired `:param:row`. @@ -181,8 +156,8 @@ class BaseMode(CursesStdIO): s += " " * (self.cols - (col + len(s)) - 1) if trim: y, x = screen.getmaxyx() - if (col+len(s)) > x: - s = "%s..."%s[0:x-4-col] + if (col + len(s)) > x: + s = "%s..." % s[0:x - 4 - col] screen.addstr(row, col, s, color) col += len(s) @@ -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): diff --git a/deluge/ui/console/modes/column.py b/deluge/ui/console/modes/column.py index 0e1f1c3e7..f1c423c2b 100644 --- a/deluge/ui/console/modes/column.py +++ b/deluge/ui/console/modes/column.py @@ -1,37 +1,10 @@ # -*- coding: utf-8 -*- # -# column.py -# # Copyright (C) 2011 Nick Lanham # -# 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 @@ -44,7 +17,7 @@ log = logging.getLogger(__name__) def format_queue(qnum): if (qnum >= 0): - return "%d" % (qnum+1) + return "%d" % (qnum + 1) else: return "" @@ -76,7 +49,7 @@ columns = { "Seeds:Peers": (("seeds_peers_ratio",), format_utils.format_float), "Down Limit": (("max_download_speed",), format_utils.format_speed), "Up Limit": (("max_upload_speed",), format_utils.format_speed), - } +} def get_column_value(name, state): diff --git a/deluge/ui/console/modes/connectionmanager.py b/deluge/ui/console/modes/connectionmanager.py index d2fd238b0..ef9766781 100644 --- a/deluge/ui/console/modes/connectionmanager.py +++ b/deluge/ui/console/modes/connectionmanager.py @@ -1,39 +1,13 @@ +# -*- coding: utf-8 -*- # -# connectionmanager.py -# -# Copyright (C) 2007-2009 Nick Lanham -# -# 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 # +# 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,12 +50,14 @@ 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.popup.add_line("%s:%d [Offline]" % (host[1], host[2]), data=host[0], foreground="red") self.inlist = True self.refresh() @@ -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 diff --git a/deluge/ui/console/modes/eventview.py b/deluge/ui/console/modes/eventview.py index b11f35800..7de3705b1 100644 --- a/deluge/ui/console/modes/eventview.py +++ b/deluge/ui/console/modes/eventview.py @@ -1,37 +1,10 @@ # -*- coding: utf-8 -*- # -# eventview.py -# # Copyright (C) 2011 Nick Lanham # -# 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 @@ -62,14 +35,14 @@ class EventView(BaseMode): self.stdscr.erase() self.add_string(0, self.statusbars.topbar) - hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10)) + hstr = "%sPress [h] for help" % (" " * (self.cols - len(self.statusbars.bottombar) - 10)) #This will quite likely fail when switching modes try: rf = format_utils.remove_formatting string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help" - string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr + string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr self.add_string(self.rows - 1, string) except: @@ -80,15 +53,15 @@ class EventView(BaseMode): if i - self.offset >= self.rows - 2: more = len(events) - self.offset - self.rows + 2 if more > 0: - self.add_string(i-self.offset, " (And %i more)" % more) + self.add_string(i - self.offset, " (And %i more)" % more) break elif i - self.offset < 0: continue try: - self.add_string(i+1-self.offset, event) + self.add_string(i + 1 - self.offset, event) except curses.error: - pass #This'll just cut the line. Note: This seriously should be fixed in a better way + pass # This'll just cut the line. Note: This seriously should be fixed in a better way else: self.add_string(1, "{!white,black,bold!}No events to show yet") @@ -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 @@ -135,7 +108,7 @@ class EventView(BaseMode): # TODO: Scroll event list jumplen = self.rows - 3 - num_events = len( component.get("ConsoleUI").events ) + num_events = len(component.get("ConsoleUI").events) if c == curses.KEY_UP: self.offset -= 1 @@ -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: diff --git a/deluge/ui/console/modes/format_utils.py b/deluge/ui/console/modes/format_utils.py index 20bc2f76e..3b7cda1f9 100644 --- a/deluge/ui/console/modes/format_utils.py +++ b/deluge/ui/console/modes/format_utils.py @@ -1,37 +1,10 @@ # -*- coding: utf-8 -*- # -# format_utils.py -# # Copyright (C) 2011 Nick Lanham # -# 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 + return "%.3f" % x + def format_seeds_peers(num, total): - return "%d (%d)"%(num, total) + return "%d (%d)" % (num, total) + def format_progress(perc): if perc < 100: - return "%.2f%%"%perc + return "%.2f%%" % perc else: return "100%" + def format_pieces(num, size): - return "%d (%s)"%(num, deluge.common.fsize(size)) + return "%d (%s)" % (num, deluge.common.fsize(size)) + def format_priority(prio): - 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] + return pstring[:pstring.index("Priority") - 1] else: return pstring + def trim_string(string, w, have_dbls): if w <= 0: return "" @@ -109,21 +94,23 @@ 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('.') - return u"%s "%("".join(chrs)) + chrs.append(".") + return u"%s " % ("".join(chrs)) else: - return u"%s "%(string[0:w-1]) + return u"%s " % (string[0:w - 1]) #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,22 +119,25 @@ 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) - size = len(col)+dbls + 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) + return trim_string(col, lim, dbls > 0) else: - return "%s%s"%(col, " "*(lim-size)) + 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): + +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. @@ -161,27 +151,27 @@ def wrap_string(string,width,min_lines=0,strip_colors=True): s1 = string.split("\n") def insert_clr(s, offset, mtchs, clrs): - end_pos = offset+len(s) + end_pos = offset + len(s) while mtchs and (mtchs[0] <= end_pos) and (mtchs[0] >= offset): - mtc = mtchs.popleft()-offset + mtc = mtchs.popleft() - offset clr = clrs.popleft() end_pos += len(clr) - s = "%s%s%s"%(s[:mtc], clr, s[mtc:]) + s = "%s%s%s" % (s[:mtc], clr, s[mtc:]) 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: - sidx = cstr.rfind(" ", 0, width-1) + sidx = cstr.rfind(" ", 0, width - 1) sidx += 1 if sidx > 0: if strip_colors: @@ -208,13 +198,13 @@ 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: ret.append(cstr) - if min_lines>0: + if min_lines > 0: for i in range(len(ret), min_lines): ret.append(" ") @@ -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"): """ @@ -254,7 +236,7 @@ def pad_string(string, length, character=" ", side="right"): """ w = strwidth(string) diff = length - w - if side == "left": + if side == "left": return "%s%s" % (character * diff, string) elif side == "right": return "%s%s" % (string, character * diff) diff --git a/deluge/ui/console/modes/input_popup.py b/deluge/ui/console/modes/input_popup.py index 86f382d8e..07c81069e 100644 --- a/deluge/ui/console/modes/input_popup.py +++ b/deluge/ui/console/modes/input_popup.py @@ -1,40 +1,12 @@ -# -# input_popup.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham -# -# Complete function from commands/add.py: # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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 @@ -58,18 +31,21 @@ class InputField: def get_height(self): return 0 - def render(self,screen,row,width,selected,col=1): + 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 - def set_depend(self,i,inverse=False): + def set_depend(self, i, inverse=False): if not isinstance(i, CheckedInput): raise Exception("Can only depend on CheckedInputs") self.depend = i @@ -83,14 +59,15 @@ 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 self.additional_formatting = additional_formatting - self.chkd_inact = "[X] %s"%message - self.unchkd_inact = "[ ] %s"%message - self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s"%message - self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s"%message + self.chkd_inact = "[X] %s" % message + self.unchkd_inact = "[ ] %s" % message + self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s" % message + self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s" % message self.name = name self.checked = checked @@ -118,21 +95,22 @@ 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 self.additional_formatting = additional_formatting - self.chkd_inact = "[X] %s"%message - self.unchkd_inact = "[ ] %s"%message - self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s"%message - self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s"%message + self.chkd_inact = "[X] %s" % message + self.unchkd_inact = "[ ] %s" % message + self.chkd_act = "[{!black,white,bold!}X{!white,black!}] %s" % message + self.unchkd_act = "[{!black,white,bold!} {!white,black!}] %s" % message self.name = name self.checked = checked - self.msglen = len(self.chkd_inact)+1 + self.msglen = len(self.chkd_inact) + 1 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): @@ -147,24 +125,26 @@ class CheckedPlusInput(InputField): self.parent.add_string(row, self.unchkd_inact, screen, col, False, True) if active and self.checked and self.child_active: - self.parent.add_string(row+1, "(esc to leave)", screen, col, False, True) + self.parent.add_string(row + 1, "(esc to leave)", screen, col, False, True) elif active and self.checked: - self.parent.add_string(row+1, "(right arrow to edit)", screen, col, False, True) + self.parent.add_string(row + 1, "(right arrow to edit)", screen, col, False, True) rows = 2 # 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) + self.parent.add_string(row, "(enable to view/edit value)", screen, col + self.msglen, False, True) return rows def handle_read(self, c): if self.child_active: - if c == 27: # leave child on esc + if c == 27: # leave child on esc self.child_active = False return # pass keys through to child @@ -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 @@ -194,11 +175,11 @@ class IntSpinInput(InputField): self.additional_formatting = additional_formatting self.default_str = str(value) - self.set_value( value) + self.set_value(value) self.default_value = self.value self.cursor = len(self.valstr) - self.cursoff = colors.get_line_width(self.message)+4 # + 4 for the " [ " in the rendered string + self.cursoff = colors.get_line_width(self.message) + 4 # + 4 for the " [ " in the rendered string self.move_func = move_func self.min_val = min_val self.max_val = max_val @@ -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: @@ -225,11 +206,11 @@ class IntSpinInput(InputField): else: self.value = int(self.valstr) self.__limit_value() - self.valstr = "%d"%self.value + self.valstr = "%d" % self.value 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) @@ -240,49 +221,55 @@ class IntSpinInput(InputField): except: self.real_value = False if not self.valstr: - self.parent.add_string(row, "%s {!input!}[ ]"%self.message, screen, col, False, True) + self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True) elif active: - 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) + self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True) if active: - self.move_func(row, self.cursor+self.cursoff+cursor_offset) + self.move_func(row, self.cursor + self.cursoff + cursor_offset) return 1 def handle_read(self, c): - if c == curses.KEY_PPAGE and self.value < self.max_val: + if c == curses.KEY_PPAGE and self.value < self.max_val: if not self.real_value: self.value = self.min_val - self.valstr = "%d"%self.value + self.valstr = "%d" % self.value self.real_value = True else: - self.value+=1 - self.valstr = "%d"%self.value + self.value += 1 + self.valstr = "%d" % self.value self.cursor = len(self.valstr) elif c == curses.KEY_NPAGE and self.value > self.min_val: if not self.real_value: self.value = self.min_val - self.valstr = "%d"%self.value + self.valstr = "%d" % self.value self.real_value = True else: - self.value-=1 - self.valstr = "%d"%self.value + self.value -= 1 + self.valstr = "%d" % self.value self.cursor = len(self.valstr) elif c == curses.KEY_LEFT: - if not self.real_value: return None - self.cursor = max(0, self.cursor-1) + 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 - self.cursor = min(len(self.valstr), self.cursor+1) + 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: @@ -290,14 +277,15 @@ class IntSpinInput(InputField): self.cursor = 0 self.real_value = True self.need_update = True - elif self.valstr and self.cursor > 0: + elif self.valstr and self.cursor > 0: self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:] - self.cursor-=1 + 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.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:] self.need_update = True elif c == 45 and self.min_val < 0: if not self.real_value: @@ -305,10 +293,12 @@ 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 - self.valstr = chr(c)+self.valstr + 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 elif c > 47 and c < 58: @@ -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: @@ -327,8 +319,7 @@ class IntSpinInput(InputField): self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] self.need_update = True # Move the cursor forward - self.cursor+=1 - + self.cursor += 1 def get_value(self): if self.real_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 @@ -359,14 +351,14 @@ class FloatSpinInput(InputField): self.additional_formatting = additional_formatting - self.fmt = "%%.%df"%precision + self.fmt = "%%.%df" % precision self.default_str = str(value) self.set_value(value) self.default_value = self.value self.cursor = len(self.valstr) - self.cursoff = colors.get_line_width(self.message)+4 # + 4 for the " [ " in the rendered string + self.cursoff = colors.get_line_width(self.message) + 4 # + 4 for the " [ " in the rendered string self.move_func = move_func self.min_val = min_val self.max_val = max_val @@ -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) @@ -410,50 +402,56 @@ class FloatSpinInput(InputField): self.real_value = False if not self.valstr: - self.parent.add_string(row, "%s {!input!}[ ]"%self.message, screen, col, False, True) + self.parent.add_string(row, "%s {!input!}[ ]" % self.message, screen, col, False, True) elif active: - 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) + self.parent.add_string(row, "%s {!input!}[ %s ]" % (self.message, self.valstr), screen, col, False, True) if active: - self.move_func(row, self.cursor+self.cursoff+cursor_offset) + self.move_func(row, self.cursor + self.cursoff + cursor_offset) return 1 def handle_read(self, c): - if c == curses.KEY_PPAGE: + if c == curses.KEY_PPAGE: if not self.real_value: self.value = self.min_val - self.valstr = "%d"%self.value + self.valstr = "%d" % self.value self.real_value = True else: - self.value+=self.inc_amt + self.value += self.inc_amt self.__limit_value() - self.valstr = self.fmt%self.value + self.valstr = self.fmt % self.value self.cursor = len(self.valstr) elif c == curses.KEY_NPAGE: if not self.real_value: self.value = self.min_val - self.valstr = "%d"%self.value + self.valstr = "%d" % self.value self.real_value = True else: - self.value-=self.inc_amt + self.value -= self.inc_amt self.__limit_value() - self.valstr = self.fmt%self.value + self.valstr = self.fmt % self.value self.cursor = len(self.valstr) elif c == curses.KEY_LEFT: - if not self.real_value: return None - self.cursor = max(0, self.cursor-1) + 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 - self.cursor = min(len(self.valstr), self.cursor+1) + 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: @@ -461,14 +459,15 @@ class FloatSpinInput(InputField): self.cursor = 0 self.real_value = True self.need_update = True - elif self.valstr and self.cursor > 0: + elif self.valstr and self.cursor > 0: self.valstr = self.valstr[:self.cursor - 1] + self.valstr[self.cursor:] - self.cursor-=1 + 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.valstr = self.valstr[:self.cursor] + self.valstr[self.cursor + 1:] self.need_update = True elif c == 45 and self.min_val < 0: if not self.real_value: @@ -476,10 +475,12 @@ 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 - self.valstr = chr(c)+self.valstr + 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 elif c == 46: @@ -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: @@ -499,7 +502,7 @@ class FloatSpinInput(InputField): self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] self.need_update = True # Move the cursor forward - self.cursor+=1 + self.cursor += 1 elif (c > 47 and c < 58): if (not self.real_value) and self.valstr: self.valstr = "" @@ -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: @@ -517,7 +521,7 @@ class FloatSpinInput(InputField): self.valstr = self.valstr[:self.cursor] + chr(c) + self.valstr[self.cursor:] self.need_update = True # Move the cursor forward - self.cursor+=1 + self.cursor += 1 def get_value(self): if self.real_value: @@ -537,8 +541,9 @@ 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): + def __init__(self, parent, message, name, opts, vals, selidx, additional_formatting=False): self.parent = parent self.message = message self.additional_formatting = additional_formatting @@ -555,20 +560,21 @@ class SelectInput(InputField): if self.message: self.parent.add_string(row, self.message, screen, col, False, True) row += 1 - off = col+1 + off = col + 1 for i, opt in enumerate(self.opts): if selected and i == self.selidx: - self.parent.add_string(row, "{!black,white,bold!}[%s]"%opt, screen, off, False, True) + self.parent.add_string(row, "{!black,white,bold!}[%s]" % opt, screen, off, False, True) elif i == self.selidx: if self.additional_formatting and i == self.default_option: - self.parent.add_string(row, "[{!magenta,black!}%s{!white,black!}]"%opt, screen, off, False, True) + self.parent.add_string(row, "[{!magenta,black!}%s{!white,black!}]" % opt, screen, off, False, True) elif self.additional_formatting: - self.parent.add_string(row, "[{!white,blue!}%s{!white,black!}]"%opt, screen, off, False, True) + self.parent.add_string(row, "[{!white,blue!}%s{!white,black!}]" % opt, screen, off, False, True) else: - 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 + self.parent.add_string(row, "[%s]" % opt, screen, off, False, True) + off += len(opt) + 3 if self.message: return 2 else: @@ -576,9 +582,9 @@ class SelectInput(InputField): def handle_read(self, c): if c == curses.KEY_LEFT: - self.selidx = max(0, self.selidx-1) + self.selidx = max(0, self.selidx - 1) if c == curses.KEY_RIGHT: - self.selidx = min(len(self.opts)-1, self.selidx+1) + self.selidx = min(len(self.opts) - 1, self.selidx + 1) def get_value(self): return self.vals[self.selidx] @@ -590,11 +596,12 @@ 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 self.move_func = move_func - self.width = width + self.width = width self.additional_formatting = additional_formatting @@ -612,7 +619,7 @@ class TextInput(InputField): def get_height(self): return 2 + bool(self.message) - def render(self,screen,row,width,selected,col=1,cursor_offset=0): + def render(self, screen, row, width, selected, col=1, cursor_offset=0): if not self.value and not selected and len(self.default_value) != 0: self.value = self.default_value self.cursor = len(self.value) @@ -622,21 +629,21 @@ class TextInput(InputField): row += 1 if selected: if self.opts: - self.parent.add_string(row+1, self.opts[self.opt_off:], screen, col, False, True) - if self.cursor > (width-3): - self.move_func(row, width-2) + self.parent.add_string(row + 1, self.opts[self.opt_off:], screen, col, False, True) + if self.cursor > (width - 3): + self.move_func(row, width - 2) else: - self.move_func(row, self.cursor+1+cursor_offset) - slen = len(self.value)+3 + self.move_func(row, self.cursor + 1 + cursor_offset) + slen = len(self.value) + 3 if slen > width: - vstr = self.value[(slen-width):] + vstr = self.value[(slen - width):] else: - vstr = self.value.ljust(width-2) + vstr = self.value.ljust(width - 2) if self.additional_formatting and len(self.value) != 0 and self.value == self.default_value: - self.parent.add_string(row, "{!magenta,white!}%s"%vstr, screen, col, False, False) + self.parent.add_string(row, "{!magenta,white!}%s" % vstr, screen, col, False, False) else: - self.parent.add_string(row, "{!black,white,bold!}%s"%vstr, screen, col, False, False) + self.parent.add_string(row, "{!black,white,bold!}%s" % vstr, screen, col, False, False) if self.message: return 3 @@ -666,15 +673,15 @@ class TextInput(InputField): if self.cursor == len(self.value) or self.value[self.cursor] == " ": if self.opts: prev = self.opt_off - self.opt_off += self.width-3 + self.opt_off += self.width - 3 # now find previous double space, best guess at a split point # in future could keep opts unjoined to get this really right - self.opt_off = self.opts.rfind(" ", 0, self.opt_off)+2 - if second_hit and self.opt_off == prev: # double tap and we're at the end + self.opt_off = self.opts.rfind(" ", 0, self.opt_off) + 2 + if second_hit and self.opt_off == prev: # double tap and we're at the end self.opt_off = 0 else: opts = self.complete(self.value) - if len(opts) == 1: # only one option, just complete it + if len(opts) == 1: # only one option, just complete it self.value = opts[0] self.cursor = len(opts[0]) self.tab_count = 0 @@ -684,15 +691,15 @@ class TextInput(InputField): self.value = prefix self.cursor = len(prefix) - if len(opts) > 1 and second_hit: # display multiple options on second tab hit - sp = self.value.rfind(os.sep)+1 + if len(opts) > 1 and second_hit: # display multiple options on second tab hit + sp = self.value.rfind(os.sep) + 1 self.opts = " ".join([o[sp:] for o in opts]) # Cursor movement elif c == curses.KEY_LEFT: - self.cursor = max(0, self.cursor-1) + self.cursor = max(0, self.cursor - 1) elif c == curses.KEY_RIGHT: - self.cursor = min(len(self.value), self.cursor+1) + self.cursor = min(len(self.value), self.cursor + 1) elif c == curses.KEY_HOME: self.cursor = 0 elif c == curses.KEY_END: @@ -705,12 +712,12 @@ class TextInput(InputField): # Delete a character in the input string based on cursor position if c == curses.KEY_BACKSPACE or c == 127: - if self.value and self.cursor > 0: + if self.value and self.cursor > 0: self.value = self.value[:self.cursor - 1] + self.value[self.cursor:] - self.cursor-=1 + self.cursor -= 1 elif c == curses.KEY_DC: if self.value and self.cursor < len(self.value): - self.value = self.value[:self.cursor] + self.value[self.cursor+1:] + self.value = self.value[:self.cursor] + self.value[self.cursor + 1:] elif c > 31 and c < 256: # Emulate getwch stroke = chr(c) @@ -728,8 +735,7 @@ class TextInput(InputField): # Insert into string self.value = self.value[:self.cursor] + uchar + self.value[self.cursor:] # Move the cursor forward - self.cursor+=1 - + self.cursor += 1 def complete(self, line): line = os.path.abspath(os.path.expanduser(line)) @@ -752,7 +758,7 @@ class TextInput(InputField): # shares a common prefix. for f in os.listdir(os.path.dirname(line)): if f.startswith(os.path.split(line)[1]): - ret.append(os.path.join( os.path.dirname(line), f)) + ret.append(os.path.join(os.path.dirname(line), f)) else: # This path does not exist, so lets do a listdir on it's parent # and find any matches. @@ -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,17 +802,16 @@ 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, - additional_formatting = self.additional_formatting)) + self.inputs.append(TextInput(self, self.move, self.width, message, name, value, complete, + additional_formatting=self.additional_formatting)) def getmaxyx(self): return self.screen.getmaxyx() - def add_string(self, row, string, scr=None, col = 0, pad=True, trim=True): + def add_string(self, row, string, scr=None, col=0, pad=True, trim=True): if row <= 0: return False - elif row >= self.height -1: + elif row >= self.height - 1: return False self.parent.add_string(row, string, scr, col, pad, trim) return True @@ -820,26 +823,26 @@ class InputPopup(Popup): def add_text(self, string): lines = string.split("\n") for line in lines: - self.lines.append( (len(self.inputs), line) ) + self.lines.append((len(self.inputs), line)) def add_select_input(self, message, name, opts, vals, default_index=0): self.inputs.append(SelectInput(self, message, name, opts, vals, default_index, - additional_formatting = self.additional_formatting)) + additional_formatting=self.additional_formatting)) def add_checked_input(self, message, name, checked=False): self.inputs.append(CheckedInput(self, message, name, checked, - additional_formatting = self.additional_formatting)) + additional_formatting=self.additional_formatting)) #def add_checked_plus_input(self, message, name, child) - def add_float_spin_input(self, message, name, value=0.0, inc_amt = 1.0, precision = 1, min_val = None, max_val = None): + def add_float_spin_input(self, message, name, value=0.0, inc_amt=1.0, precision=1, min_val=None, max_val=None): i = FloatSpinInput(self, message, name, self.move, value, inc_amt, precision, min_val, max_val, - additional_formatting = self.additional_formatting) + additional_formatting=self.additional_formatting) self.inputs.append(i) - def add_int_spin_input(self, message, name, value = 0, min_val = None, max_val = None): + def add_int_spin_input(self, message, name, value=0, min_val=None, max_val=None): i = IntSpinInput(self, message, name, self.move, value, min_val, max_val, - additional_formatting = self.additional_formatting) + additional_formatting=self.additional_formatting) self.inputs.append(i) def _refresh_lines(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: @@ -859,37 +861,36 @@ class InputPopup(Popup): active = (i == self.current_input) if active: - if end_row + 1 >= self.height + self.lineoff : + if end_row + 1 >= self.height + self.lineoff: self.lineoff += ipt.get_height() elif start_row < self.lineoff: self.lineoff -= ipt.get_height() 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: self.add_string(crow, line[1], self.screen, 1, pad=False) crow += 1 - crow += ipt.render(self.screen, crow, self.width, i==self.current_input) + crow += ipt.render(self.screen, crow, self.width, i == self.current_input) - if (self.content_height > (self.height-2)): - lts = self.content_height-(self.height-3) - perc_sc = float(self.lineoff)/lts - sb_pos = int((self.height-2)*perc_sc)+1 + if (self.content_height > (self.height - 2)): + lts = self.content_height - (self.height - 3) + perc_sc = float(self.lineoff) / lts + sb_pos = int((self.height - 2) * perc_sc) + 1 if (sb_pos == 1) and (self.lineoff != 0): sb_pos += 1 - self.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width-1), pad=False, trim=False) + self.add_string(sb_pos, "{!red,black,bold!}#", self.screen, col=(self.width - 1), pad=False, trim=False) if self._cursor_row >= 0: curses.curs_set(2) self.screen.move(self._cursor_row, self._cursor_col) def handle_read(self, c): if c == curses.KEY_UP: - self.current_input = max(0, self.current_input-1) + self.current_input = max(0, self.current_input - 1) elif c == curses.KEY_DOWN: - self.current_input = min(len(self.inputs)-1, self.current_input+1) + self.current_input = min(len(self.inputs) - 1, self.current_input + 1) elif c == curses.KEY_ENTER or c == 10: if self.close_cb: vals = {} @@ -897,8 +898,8 @@ class InputPopup(Popup): vals[ipt.name] = ipt.get_value() curses.curs_set(0) self.close_cb(vals) - return True # close the popup - elif c == 27: # close on esc, no action + return True # close the popup + elif c == 27: # close on esc, no action return True elif self.inputs: self.inputs[self.current_input].handle_read(c) diff --git a/deluge/ui/console/modes/legacy.py b/deluge/ui/console/modes/legacy.py index 80d0b1895..75d50d61d 100644 --- a/deluge/ui/console/modes/legacy.py +++ b/deluge/ui/console/modes/legacy.py @@ -1,38 +1,11 @@ # -*- coding: utf-8 -*- # -# legacy.py -# # Copyright (C) 2008-2009 Ido Abramovich # Copyright (C) 2009 Andrew Resch # -# 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,13 +474,13 @@ 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) f.write(text) - f.write( os.linesep ) + f.write(os.linesep) #And increment line counter self._hf_lines[active_file] += 1 @@ -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,20 +501,20 @@ 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]) ) + chunks.append((line[:end_color + 2], line[end_color + 2:next_color])) line = line[next_color:] return chunks @@ -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. @@ -747,7 +722,7 @@ class Legacy(BaseMode, component.Component): new_line = format_utils.remove_formatting(new_line) return (new_line, len(new_line)) else: - if hits == 1: + if hits == 1: p = " ".join(split(line)[:-1]) try: @@ -755,7 +730,7 @@ class Legacy(BaseMode, component.Component): except IndexError: l_arg = "" - new_line = " ".join( [p, complete_line(l_arg, possible_matches)] ).lstrip() + new_line = " ".join([p, complete_line(l_arg, possible_matches)]).lstrip() if len(format_utils.remove_formatting(new_line)) > len(line): line = new_line @@ -763,8 +738,8 @@ class Legacy(BaseMode, component.Component): elif hits >= 2: max_list = self.console_config["torrents_per_tab_press"] match_count = len(possible_matches) - listed = (hits-2) * max_list - pages = (match_count-1) // max_list + 1 + listed = (hits - 2) * max_list + pages = (match_count - 1) // max_list + 1 left = match_count - listed if hits == 2: self.write(" ") @@ -777,7 +752,7 @@ 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. Press to list them" % (left - max_list) ) + self.write("{!error!}And %i more. Press to list them" % (left - max_list)) else: self.tab_count = 0 for match in possible_matches[listed:]: @@ -787,13 +762,14 @@ 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 to view more" % (left - max_list, hits-1, pages) ) + self.write("{!error!}And %i more (%i/%i). Press to view more" % ( + left - max_list, hits - 1, pages)) else: self.tab_count = 0 for match in possible_matches[listed:]: self.write(match.replace(r"\ ", " ")) if hits > 2: - self.write("{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits-1, pages) ) + self.write("{!green!}Finished listing %i torrents (%i/%i)" % (match_count, hits - 1, pages)) #We only want to print eventual colors or other control characters, not return them line = format_utils.remove_formatting(line) @@ -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 @@ -833,7 +809,7 @@ class Legacy(BaseMode, component.Component): # shares a common prefix. for f in os.listdir(os.path.dirname(line)): if f.startswith(os.path.split(line)[1]): - ret.append(os.path.join( os.path.dirname(line), f)) + ret.append(os.path.join(os.path.dirname(line), f)) except OSError: self.console.write("{!error!}Permission denied: {!info!}%s" % line) else: @@ -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: @@ -897,7 +873,7 @@ class Legacy(BaseMode, component.Component): possible_matches = [] possible_matches2 = [] - match_count = 0 + match_count = 0 match_count2 = 0 for torrent_id, torrent_name in self.torrents: if torrent_id.startswith(line): @@ -909,9 +885,10 @@ 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 we only matched one torrent, don't add the full name or it'll also get autocompleted if match_count == 1: if torrent_id.startswith(line): possible_matches.append(torrent_id) @@ -932,10 +909,12 @@ class Legacy(BaseMode, component.Component): text = "{!info!}%s{!input!}%s - '%s'" % (torrent_id[:l], torrent_id[l:], torrent_name) 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 diff --git a/deluge/ui/console/modes/popup.py b/deluge/ui/console/modes/popup.py index 153e47d43..dac60876f 100644 --- a/deluge/ui/console/modes/popup.py +++ b/deluge/ui/console/modes/popup.py @@ -1,42 +1,14 @@ # -*- coding: utf-8 -*- # -# popup.py -# # Copyright (C) 2011 Nick Lanham # -# 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,20 +18,23 @@ import format_utils log = logging.getLogger(__name__) + class ALIGN: - TOP_LEFT = 1 - TOP_CENTER = 2 - TOP_RIGHT = 3 - MIDDLE_LEFT = 4 - MIDDLE_CENTER= 5 + TOP_LEFT = 1 + TOP_CENTER = 2 + TOP_RIGHT = 3 + MIDDLE_LEFT = 4 + MIDDLE_CENTER = 5 MIDDLE_RIGHT = 6 - BOTTOM_LEFT = 7 - BOTTOM_CENTER= 8 + BOTTOM_LEFT = 7 + BOTTOM_CENTER = 8 BOTTOM_RIGHT = 9 - DEFAULT = MIDDLE_CENTER + 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. @@ -77,14 +52,14 @@ class Popup: refresh(self) - draw the popup window to screen. this default mode simply draws a bordered window with the supplied title to the screen - add_string(self, row, string) - add string at row. handles triming/ignoring if the string won't fit in the popup + add_string(self, row, string) - add string at row. handles triming/ignoring if the string won't fit in the popup _doRead(self) - handle user input to the popup. """ self.parent = parent_mode self.height_req = height_req - self.width_req = width_req + self.width_req = width_req self.align = align self.handle_resize() @@ -102,19 +77,19 @@ class Popup: def _refresh_lines(self): crow = 1 for line in self._lines[self.lineoff:]: - if (crow >= self.height-1): + if (crow >= self.height - 1): break self.parent.add_string(crow, line, self.screen, 1, False, True) - crow+=1 + crow += 1 def handle_resize(self): if isinstance(self.height_req, float) and 0.0 < self.height_req <= 1.0: - hr = int( (self.parent.rows - 2) * self.height_req ) + hr = int((self.parent.rows - 2) * self.height_req) else: hr = self.height_req if isinstance(self.width_req, float) and 0.0 < self.width_req <= 1.0: - wr = int( (self.parent.cols - 2) * self.width_req ) + wr = int((self.parent.cols - 2) * self.width_req) else: wr = self.width_req @@ -122,31 +97,31 @@ class Popup: #Height if hr == 0: - hr = int(self.parent.rows/2) + hr = int(self.parent.rows / 2) elif hr == -1: hr = self.parent.rows - 2 elif hr > self.parent.rows - 2: hr = self.parent.rows - 2 #Width - if wr == 0: - wr = int(self.parent.cols/2) + if wr == 0: + wr = int(self.parent.cols / 2) elif wr == -1: wr = self.parent.cols elif wr >= self.parent.cols: wr = self.parent.cols - if self.align in [ALIGN.TOP_CENTER, ALIGN.TOP_LEFT, ALIGN.TOP_RIGHT]: + if self.align in [ALIGN.TOP_CENTER, ALIGN.TOP_LEFT, ALIGN.TOP_RIGHT]: by = 1 elif self.align in [ALIGN.MIDDLE_CENTER, ALIGN.MIDDLE_LEFT, ALIGN.MIDDLE_RIGHT]: - by = (self.parent.rows/2)-(hr/2) + by = (self.parent.rows / 2) - (hr / 2) elif self.align in [ALIGN.BOTTOM_CENTER, ALIGN.BOTTOM_LEFT, ALIGN.BOTTOM_RIGHT]: by = self.parent.rows - hr - 1 - if self.align in [ALIGN.TOP_LEFT, ALIGN.MIDDLE_LEFT, ALIGN.BOTTOM_LEFT]: + if self.align in [ALIGN.TOP_LEFT, ALIGN.MIDDLE_LEFT, ALIGN.BOTTOM_LEFT]: bx = 0 elif self.align in [ALIGN.TOP_CENTER, ALIGN.MIDDLE_CENTER, ALIGN.BOTTOM_CENTER]: - bx = (self.parent.cols/2)-(wr/2) + bx = (self.parent.cols / 2) - (wr / 2) elif self.align in [ALIGN.TOP_RIGHT, ALIGN.MIDDLE_RIGHT, ALIGN.BOTTOM_RIGHT]: bx = self.parent.cols - wr - 1 @@ -155,21 +130,21 @@ 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) - toff = max(1, (self.width//2) - (len(self.title)//2)) - self.parent.add_string(0, "{!white,black,bold!}%s"%self.title, self.screen, toff, False, True) + toff = max(1, (self.width // 2) - (len(self.title) // 2)) + self.parent.add_string(0, "{!white,black,bold!}%s" % self.title, self.screen, toff, False, True) self._refresh_lines() - if (len(self._lines) > (self.height-2)): - lts = len(self._lines)-(self.height-3) - perc_sc = float(self.lineoff)/lts - sb_pos = int((self.height-2)*perc_sc)+1 + if (len(self._lines) > (self.height - 2)): + lts = len(self._lines) - (self.height - 3) + perc_sc = float(self.lineoff) / lts + 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() @@ -180,28 +155,28 @@ class Popup: def handle_read(self, c): p_off = self.height - 3 if c == curses.KEY_UP: - self.lineoff = max(0, self.lineoff -1) + self.lineoff = max(0, self.lineoff - 1) elif c == curses.KEY_PPAGE: self.lineoff = max(0, self.lineoff - p_off) elif c == curses.KEY_HOME: self.lineoff = 0 elif c == curses.KEY_DOWN: - if len(self._lines)-self.lineoff > (self.height-2): + if len(self._lines) - self.lineoff > (self.height - 2): self.lineoff += 1 elif c == curses.KEY_NPAGE: - self.lineoff = min(len(self._lines) - self.height+2, self.lineoff + p_off) + self.lineoff = min(len(self._lines) - self.height + 2, self.lineoff + p_off) elif c == curses.KEY_END: - self.lineoff = len(self._lines) - self.height+2 + self.lineoff = len(self._lines) - self.height + 2 - elif c == curses.KEY_ENTER or c == 10 or c == 27: # close on enter/esc + elif c == curses.KEY_ENTER or c == 10 or c == 27: # close on enter/esc if self.close_cb: self.close_cb() - return True # close the popup + return True # close the popup - if c > 31 and c < 256 and chr(c) == 'q': + if c > 31 and c < 256 and chr(c) == "q": if self.close_cb: self.close_cb() - return True # close the popup + return True # close the popup self.refresh() @@ -215,7 +190,7 @@ class Popup: def add_divider(self): if not self.divider: - self.divider = "-"*(self.width-2) + self.divider = "-" * (self.width - 2) self._lines.append(self.divider) @@ -224,7 +199,7 @@ class SelectablePopup(Popup): A popup which will let the user select from some of the lines that are added. """ - def __init__(self,parent_mode,title, selection_callback, args=(), align=ALIGN.DEFAULT, immediate_action=False): + def __init__(self, parent_mode, title, selection_callback, args=(), align=ALIGN.DEFAULT, immediate_action=False): Popup.__init__(self, parent_mode, title, align=align) self._selection_callback = selection_callback self._selection_args = args @@ -240,63 +215,67 @@ class SelectablePopup(Popup): def add_line(self, string, selectable=True, use_underline=True, data=None, foreground=None): 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 + string = string[:udx] + string[udx + 1:] + self._udxs[len(self._lines) + 1] = udx c = string[udx].lower() self._hotkeys[c] = len(self._lines) Popup.add_line(self, string) self._line_foregrounds.append(foreground) if selectable: - self._selectable_lines.append(len(self._lines)-1) + self._selectable_lines.append(len(self._lines) - 1) self._select_data.append(data) if self._selected < 0: - self._selected = (len(self._lines)-1) + self._selected = (len(self._lines) - 1) def _refresh_lines(self): crow = 1 for row, line in enumerate(self._lines): - if (crow >= self.height-1): + if (crow >= self.height - 1): break if (row < self.lineoff): continue fg = self._line_foregrounds[row] udx = self._udxs.get(crow) if row == self._selected: - if fg == None: fg = "black" - colorstr = "{!%s,white,bold!}"%fg + if fg is None: + fg = "black" + colorstr = "{!%s,white,bold!}" % fg if udx >= 0: - ustr = "{!%s,white,bold,underline!}"%fg + ustr = "{!%s,white,bold,underline!}" % fg else: - if fg == None: fg = "white" - colorstr = "{!%s,black!}"%fg + if fg is None: + fg = "white" + colorstr = "{!%s,black!}" % fg if udx >= 0: - ustr = "{!%s,black,underline!}"%fg + 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 + self.parent.add_string(crow, "- %s%s" % (colorstr, line), self.screen, 1, False, True) + crow += 1 def current_selection(self): "Returns a tuple of (selected index, selected data)" idx = self._selectable_lines.index(self._selected) return (idx, self._select_data[idx]) - def add_divider(self,color="white"): + def add_divider(self, color="white"): if not self.divider: - self.divider = "-"*(self.width-6)+" -" + self.divider = "-" * (self.width - 6) + " -" self._lines.append(self.divider) self._line_foregrounds.append(color) def _move_cursor_up(self, amount): if self._selectable_lines.index(self._selected) > amount: idx = self._selectable_lines.index(self._selected) - self._selected = self._selectable_lines[idx-amount] + self._selected = self._selectable_lines[idx - amount] else: self._selected = self._selectable_lines[0] @@ -306,7 +285,7 @@ class SelectablePopup(Popup): def _move_cursor_down(self, amount): idx = self._selectable_lines.index(self._selected) if (idx < len(self._selectable_lines) - amount): - self._selected = self._selectable_lines[idx+amount] + self._selected = self._selectable_lines[idx + amount] else: self._selected = self._selectable_lines[-1] @@ -329,7 +308,7 @@ class SelectablePopup(Popup): elif c == curses.KEY_END: self._move_cursor_down(len(self._selectable_lines)) - elif c == 27: # close on esc, no action + elif c == 27: # close on esc, no action return True elif c == curses.KEY_ENTER or c == 10: @@ -337,13 +316,13 @@ class SelectablePopup(Popup): return self._selection_callback(idx, self._select_data[idx], *self._selection_args) if c > 31 and c < 256: - if chr(c) == 'q': - return True # close the popup + if chr(c) == "q": + return True # close the popup uc = chr(c).lower() if uc in self._hotkeys: # exec hotkey action idx = self._selectable_lines.index(self._hotkeys[uc]) - return self._selection_callback(idx,self._select_data[idx],*self._selection_args) + return self._selection_callback(idx, self._select_data[idx], *self._selection_args) self.refresh() return False @@ -357,12 +336,12 @@ class MessagePopup(Popup): self.message = message #self.width= int(parent_mode.cols/2) Popup.__init__(self, parent_mode, title, align=align, width_req=width_req) - lns = format_utils.wrap_string(self.message, self.width-2, 3, True) - self.height_req = min(len(lns)+2, int(parent_mode.rows*2/3)) + lns = format_utils.wrap_string(self.message, self.width - 2, 3, True) + self.height_req = min(len(lns) + 2, int(parent_mode.rows * 2 / 3)) self.handle_resize() self._lines = lns def handle_resize(self): Popup.handle_resize(self) self.clear() - self._lines = format_utils.wrap_string(self.message, self.width-2, 3, True) + self._lines = format_utils.wrap_string(self.message, self.width - 2, 3, True) diff --git a/deluge/ui/console/modes/preference_panes.py b/deluge/ui/console/modes/preference_panes.py index 513aac218..9a085f601 100644 --- a/deluge/ui/console/modes/preference_panes.py +++ b/deluge/ui/console/modes/preference_panes.py @@ -1,36 +1,10 @@ -# -# preference_panes.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham # -# 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,10 +26,11 @@ class NoInput: def depend_skip(self): return False + class Header(NoInput): def __init__(self, parent, header, space_above, space_below): self.parent = parent - self.header = "{!white,black,bold!}%s"%header + self.header = "{!white,black,bold!}%s" % header self.space_above = space_above self.space_below = space_below self.name = header @@ -65,32 +40,35 @@ class Header(NoInput): if self.space_above: row += 1 rows += 1 - self.parent.add_string(row, self.header, screen, offset-1, False, True) - if self.space_below: rows += 1 + self.parent.add_string(row, self.header, screen, offset - 1, False, True) + if self.space_below: + rows += 1 return rows + class InfoField(NoInput): def __init__(self, parent, label, value, name): self.parent = parent self.label = label self.value = value - self.txt = "%s %s"%(label, value) + self.txt = "%s %s" % (label, value) self.name = name def render(self, screen, row, width, active, offset): - self.parent.add_string(row, self.txt, screen, offset-1, False, True) + self.parent.add_string(row, self.txt, screen, offset - 1, False, True) return 1 def set_value(self, v): self.value = v if type(v) == float: - self.txt = "%s %.2f"%(self.label, self.value) + self.txt = "%s %.2f" % (self.label, self.value) else: - self.txt = "%s %s"%(self.label, self.value) + self.txt = "%s %s" % (self.label, self.value) + class BasePane: def __init__(self, offset, parent, width): - self.offset = offset+1 + self.offset = offset + 1 self.parent = parent self.width = width self.inputs = [] @@ -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"): @@ -150,13 +126,13 @@ class BasePane: if not isinstance(ipt, NoInput): try: ipt.set_value(conf_dict[ipt.name]) - except KeyError: # just ignore if it's not in dict + except KeyError: # just ignore if it's not in dict pass if hasattr(ipt, "get_child"): try: c = ipt.get_child() c.set_value(conf_dict[c.name]) - except KeyError: # just ignore if it's not in dict + except KeyError: # just ignore if it's not in dict pass def render(self, mode, screen, width, active): @@ -169,26 +145,27 @@ class BasePane: drew_act = not active crow = 1 for i, ipt in enumerate(self.inputs): - if ipt.depend_skip() or i= (mode.prefs_height): break if not drew_act: - self.input_offset+=1 + self.input_offset += 1 mode.refresh() return 0 if active and self._cursor_row >= 0: curses.curs_set(2) - screen.move(self._cursor_row, self._cursor_col+self.offset-1) + screen.move(self._cursor_row, self._cursor_col + self.offset - 1) else: curses.curs_set(0) @@ -196,30 +173,30 @@ class BasePane: # just handles setting the active input def handle_read(self, c): - if not self.inputs: # no inputs added yet + if not self.inputs: # no inputs added yet return if c == curses.KEY_UP: - nc = max(0, self.active_input-1) + nc = max(0, self.active_input - 1) while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): - nc-=1 - if nc <= 0: break + nc -= 1 + 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: ilen = len(self.inputs) - nc = min(self.active_input+1, ilen-1) + nc = min(self.active_input + 1, ilen - 1) while isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): - nc+=1 + nc += 1 if nc >= ilen: - nc-=1 + nc -= 1 break if not isinstance(self.inputs[nc], NoInput) or self.inputs[nc].depend_skip(): self.active_input = nc 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): @@ -276,7 +262,7 @@ class ColumnsPane(BasePane): default_prefs = deluge.ui.console.modes.alltorrents.DEFAULT_PREFS for cpn in deluge.ui.console.modes.alltorrents.column_pref_names: - pn = "show_%s"%cpn + pn = "show_%s" % cpn #If there is no option for it, it's not togglable # We check in default_prefs because it might still exist in config files if pn not in default_prefs: @@ -286,7 +272,7 @@ class ColumnsPane(BasePane): parent.console_config[pn]) self.add_header("Column Widths (-1 = expand)", True) for cpn in deluge.ui.console.modes.alltorrents.column_pref_names: - pn = "%s_width"%cpn + pn = "%s_width" % cpn if pn not in default_prefs: continue self.add_int_spin_input(pn, @@ -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,12 +474,13 @@ 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") self.add_info_field(" Reads:", self.parent.status["reads"], "reads") - self.add_info_field(" Read Cache Hit Ratio:", "%.2f"%self.parent.status["read_hit_ratio"], "read_hit_ratio") + self.add_info_field(" Read Cache Hit Ratio:", "%.2f" % self.parent.status["read_hit_ratio"], "read_hit_ratio") self.add_header(" Size") self.add_info_field(" Cache Size:", self.parent.status["cache_size"], "cache_size") self.add_info_field(" Read Cache Size:", self.parent.status["read_cache_size"], "read_cache_size") diff --git a/deluge/ui/console/modes/preferences.py b/deluge/ui/console/modes/preferences.py index 84ba79f64..7f314ae44 100644 --- a/deluge/ui/console/modes/preferences.py +++ b/deluge/ui/console/modes/preferences.py @@ -1,37 +1,10 @@ # -*- coding: utf-8 -*- # -# preferences.py -# # Copyright (C) 2011 Nick Lanham # -# 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 @@ -132,39 +105,39 @@ class Preferences(BaseMode): self.refresh() def __calc_sizes(self): - self.prefs_width = self.cols-self.div_off-1 - self.prefs_height = self.rows-4 + self.prefs_width = self.cols - self.div_off - 1 + self.prefs_height = self.rows - 4 # Needs to be same order as self.categories self.panes = [ - InterfacePane(self.div_off+2, self, self.prefs_width), - ColumnsPane(self.div_off+2, self, self.prefs_width), - DownloadsPane(self.div_off+2, self, self.prefs_width), - NetworkPane(self.div_off+2, self, self.prefs_width), - BandwidthPane(self.div_off+2, self, self.prefs_width), - OtherPane(self.div_off+2, self, self.prefs_width), - DaemonPane(self.div_off+2, self, self.prefs_width), - QueuePane(self.div_off+2, self, self.prefs_width), - ProxyPane(self.div_off+2, self, self.prefs_width), - CachePane(self.div_off+2, self, self.prefs_width) + InterfacePane(self.div_off + 2, self, self.prefs_width), + ColumnsPane(self.div_off + 2, self, self.prefs_width), + DownloadsPane(self.div_off + 2, self, self.prefs_width), + NetworkPane(self.div_off + 2, self, self.prefs_width), + BandwidthPane(self.div_off + 2, self, self.prefs_width), + OtherPane(self.div_off + 2, self, self.prefs_width), + DaemonPane(self.div_off + 2, self, self.prefs_width), + QueuePane(self.div_off + 2, self, self.prefs_width), + ProxyPane(self.div_off + 2, self, self.prefs_width), + CachePane(self.div_off + 2, self, self.prefs_width) ] def __draw_catetories(self): for i, category in enumerate(self.categories): if i == self.cur_cat and self.active_zone == ZONE.CATEGORIES: - self.add_string(i+1, "- {!black,white,bold!}%s"%category, pad=False) + self.add_string(i + 1, "- {!black,white,bold!}%s" % category, pad=False) elif i == self.cur_cat: - self.add_string(i+1, "- {!black,white!}%s"%category, pad=False) + 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.add_string(i + 1, "- %s" % category) + 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) def __draw_actions(self): selected = self.active_zone == ZONE.ACTIONS - self.stdscr.hline(self.rows-3, self.div_off+1, "_", self.cols) - self.action_input.render(self.stdscr, self.rows-2, self.cols, selected, self.cols-22) + self.stdscr.hline(self.rows - 3, self.div_off + 1, "_", self.cols) + self.action_input.render(self.stdscr, self.rows - 2, self.cols, selected, self.cols - 22) def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args) @@ -177,14 +150,14 @@ 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) self.stdscr.erase() self.add_string(0, self.statusbars.topbar) - hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10)) - self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar, hstr)) + hstr = "%sPress [h] for help" % (" " * (self.cols - len(self.statusbars.bottombar) - 10)) + self.add_string(self.rows - 1, "%s%s" % (self.statusbars.bottombar, hstr)) self.__draw_catetories() self.__draw_actions() @@ -205,9 +178,9 @@ class Preferences(BaseMode): def __category_read(self, c): # Navigate prefs if c == curses.KEY_UP: - self.cur_cat = max(0, self.cur_cat-1) + self.cur_cat = max(0, self.cur_cat - 1) elif c == curses.KEY_DOWN: - self.cur_cat = min(len(self.categories)-1, self.cur_cat+1) + self.cur_cat = min(len(self.categories) - 1, self.cur_cat + 1) def __prefs_read(self, c): self.panes[self.cur_cat].handle_read(c) @@ -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 + 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) diff --git a/deluge/ui/console/modes/torrent_actions.py b/deluge/ui/console/modes/torrent_actions.py index 96416b9b4..61ad06898 100644 --- a/deluge/ui/console/modes/torrent_actions.py +++ b/deluge/ui/console/modes/torrent_actions.py @@ -1,35 +1,10 @@ -# torrent_actions.py +# -*- coding: utf-8 -*- # # Copyright (C) 2011 Nick Lanham # -# 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,38 +49,42 @@ torrent_options_to_names = { "move_on_completed_path": "Folder to move the torrent to" } + class ACTION: - PAUSE=0 - RESUME=1 - REANNOUNCE=2 - EDIT_TRACKERS=3 - RECHECK=4 - REMOVE=5 - REMOVE_DATA=6 - REMOVE_NODATA=7 - DETAILS=8 - MOVE_STORAGE=9 - QUEUE=10 - QUEUE_TOP=11 - QUEUE_UP=12 - QUEUE_DOWN=13 - QUEUE_BOTTOM=14 - TORRENT_OPTIONS=15 + PAUSE = 0 + RESUME = 1 + REANNOUNCE = 2 + EDIT_TRACKERS = 3 + RECHECK = 4 + REMOVE = 5 + REMOVE_DATA = 6 + REMOVE_NODATA = 7 + DETAILS = 8 + MOVE_STORAGE = 9 + QUEUE = 10 + QUEUE_TOP = 11 + QUEUE_UP = 12 + QUEUE_DOWN = 13 + 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: + if data == ACTION.PAUSE: log.debug("Pausing torrents: %s", ids) client.core.pause_torrent(ids).addErrback(action_error, mode) - elif data==ACTION.RESUME: + elif data == ACTION.RESUME: log.debug("Resuming torrents: %s", ids) client.core.resume_torrent(ids).addErrback(action_error, mode) - elif data==ACTION.QUEUE: + elif data == ACTION.QUEUE: def do_queue(idx, qact, mode, ids): def move_selection(r): if mode.config["move_selection"]: @@ -125,18 +104,18 @@ def torrent_action(idx, data, mode, ids): mode.marked = range(1, selected_num + 1) elif qact == ACTION.QUEUE_UP: mode.cursel = max(1, mode.cursel - 1) - mode.marked = map(lambda v: v-1, mode.marked) - mode.marked = filter(lambda v: v>0, mode.marked) + mode.marked = map(lambda v: v - 1, mode.marked) + mode.marked = filter(lambda v: v > 0, mode.marked) elif qact == ACTION.QUEUE_DOWN: mode.cursel = min(queue_length, mode.cursel + 1) - mode.marked = map(lambda v: v+1, mode.marked) - mode.marked = filter(lambda v: v<=queue_length, mode.marked) + mode.marked = map(lambda v: v + 1, mode.marked) + mode.marked = filter(lambda v: v <= queue_length, mode.marked) elif qact == ACTION.QUEUE_BOTTOM: if mode.marked: mode.cursel = queue_length - selected_num + 1 + sorted(mode.marked).index(mode.cursel) else: mode.cursel = queue_length - mode.marked = range(queue_length - selected_num + 1, queue_length+1) + mode.marked = range(queue_length - selected_num + 1, queue_length + 1) if qact == ACTION.QUEUE_TOP: log.debug("Queuing torrents top") @@ -161,9 +140,10 @@ def torrent_action(idx, data, mode, ids): popup.add_line("_Bottom", data=ACTION.QUEUE_BOTTOM) mode.set_popup(popup) return False - elif data==ACTION.REMOVE: + elif data == ACTION.REMOVE: def do_remove(data): - if not data: return + if not data: + return mode.clear_marks() wd = data["remove_files"] @@ -171,17 +151,13 @@ 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"]) callbacks = [] for tid in ids: d = client.core.get_torrent_status(tid, ["name", "state"]) - callbacks.append( d.addCallback(got_status) ) - + callbacks.append(d.addCallback(got_status)) def finish_up(status): status = map(lambda x: x[1], status) @@ -202,15 +178,17 @@ def torrent_action(idx, data, mode, ids): popup = InputPopup(mode, "(Esc to cancel, Enter to remove)", close_cb=do_remove) popup.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 - elif data==ACTION.MOVE_STORAGE: + elif data == ACTION.MOVE_STORAGE: 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) @@ -221,20 +199,20 @@ def torrent_action(idx, data, mode, ids): popup.add_text_input("Enter path to move to:", "path") mode.set_popup(popup) return False - elif data==ACTION.RECHECK: + elif data == ACTION.RECHECK: log.debug("Rechecking torrents: %s", ids) client.core.force_recheck(ids).addErrback(action_error, mode) - elif data==ACTION.REANNOUNCE: + elif data == ACTION.REANNOUNCE: log.debug("Reannouncing torrents: %s", ids) client.core.force_reannounce(ids).addErrback(action_error, mode) - elif data==ACTION.DETAILS: + elif data == ACTION.DETAILS: log.debug("Torrent details") tid = mode.current_torrent_id() if tid: mode.show_torrent_details(tid) else: log.error("No current torrent in _torrent_action, this is a bug") - elif data==ACTION.TORRENT_OPTIONS: + elif data == ACTION.TORRENT_OPTIONS: mode.popup = Popup(mode, "Torrent options") mode.popup.add_line("Querying core, please wait...") @@ -247,7 +225,7 @@ def torrent_action(idx, data, mode, ids): for opt in result: if result[opt] not in ["multiple", None]: options[opt] = result[opt] - client.core.set_torrent_options( ids, options ) + client.core.set_torrent_options(ids, options) for tid in ids: if "move_on_completed_path" in options: client.core.set_torrent_move_completed_path(tid, options["move_on_completed_path"]) @@ -262,7 +240,7 @@ def torrent_action(idx, data, mode, ids): def on_torrent_status(status): for key in status: - if key not in options: + if key not in options: options[key] = status[key] elif options[key] != status[key]: options[key] = "multiple" @@ -274,7 +252,7 @@ def torrent_action(idx, data, mode, ids): for (field, field_type) in torrent_options: caption = "{!info!}" + torrent_options_to_names[field] value = options[field] - if field_type == str: + if field_type == str: if not isinstance(value, basestring): value = str(value) option_popup.add_text_input(caption, field, value) @@ -293,9 +271,9 @@ def torrent_action(idx, data, mode, ids): ) option_popup.add_select_input(caption, field, choices[0], choices[1], choices[2]) elif field_type == float: - option_popup.add_float_spin_input(caption, field, value, min_val = -1) + option_popup.add_float_spin_input(caption, field, value, min_val=-1) elif field_type == int: - option_popup.add_int_spin_input(caption, field, value, min_val = -1) + option_popup.add_int_spin_input(caption, field, value, min_val=-1) mode.set_popup(option_popup) mode.refresh() @@ -306,7 +284,7 @@ def torrent_action(idx, data, mode, ids): for tid in torrents: deferred = component.get("SessionProxy").get_torrent_status(tid, field_list) - callbacks.append( deferred.addCallback(on_torrent_status) ) + callbacks.append(deferred.addCallback(on_torrent_status)) callbacks = defer.DeferredList(callbacks) callbacks.addCallback(create_popup) @@ -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: +def torrent_actions_popup(mode, tids, details=False, action=None): + if action is not None: torrent_action(-1, action, mode, tids) return popup = SelectablePopup(mode, "Torrent Actions", torrent_action, (mode, tids)) diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py index c00c8919d..b8b865624 100644 --- a/deluge/ui/console/modes/torrentdetail.py +++ b/deluge/ui/console/modes/torrentdetail.py @@ -1,53 +1,24 @@ # -*- coding: utf-8 -*- # -# torrentdetail.py -# # Copyright (C) 2011 Nick Lanham # -# 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,17 +120,19 @@ 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)) + self.files_sep = "{!green,black,bold,underline!}%s" % (("Files (File list unknown)").center(self.cols)) need_prio_update = True self.__fill_progress(self.file_list, state["file_progress"]) for i, prio in enumerate(state["file_priorities"]): @@ -194,13 +169,13 @@ class TorrentDetail(BaseMode, component.Component): cl = [] if p == fin: ent = [p, f["index"], f["size"], cl, False, - format_utils.format_progress(prog[f["index"]]*100), + format_utils.format_progress(prog[f["index"]] * 100), prio[f["index"]]] retdict[f["index"]] = ent else: ent = [p, diridx, -1, cl, False, 0, -1] retdict[diridx] = ent - diridx-=1 + diridx -= 1 cur.append(ent) cur = cl else: @@ -224,21 +199,22 @@ 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 + if f[3]: # dir, has some children bd = self.__fill_progress(f[3], progs) - f[5] = format_utils.format_progress((bd/f[2])*100) - else: # file, update own prog and add to total - bd = f[2]*progs[f[1]] - f[5] = format_utils.format_progress(progs[f[1]]*100) + f[5] = format_utils.format_progress((bd / f[2]) * 100) + else: # file, update own prog and add to total + bd = f[2] * progs[f[1]] + f[5] = format_utils.format_progress(progs[f[1]] * 100) tb += bd return tb def __fill_prio(self, fs): for f in fs: - if f[3]: # dir, so fill in children and compute our prio + if f[3]: # dir, so fill in children and compute our prio self.__fill_prio(f[3]) child_prios = [e[6] for e in f[3]] if len(child_prios) > 1: @@ -248,21 +224,21 @@ class TorrentDetail(BaseMode, component.Component): def __update_columns(self): self.column_widths = [-1, 15, 15, 20] - req = sum(filter(lambda x:x >= 0, self.column_widths)) - if (req > self.cols): # can't satisfy requests, just spread out evenly - cw = int(self.cols/len(self.column_names)) + req = sum(filter(lambda x: x >= 0, self.column_widths)) + if (req > self.cols): # can't satisfy requests, just spread out evenly + cw = int(self.cols / len(self.column_names)) for i in range(0, len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0, self.column_widths)) - vw = int(rem/var_cols) + vw = int(rem / var_cols) for i in range(0, len(self.column_widths)): 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): @@ -309,30 +287,27 @@ class TorrentDetail(BaseMode, component.Component): #from sys import stderr #print >> stderr, fl[6] # kick out if we're going to draw too low on the screen - if (off >= self.rows-1): + if (off >= self.rows - 1): self.more_to_draw = True return -1, -1 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 @@ -350,7 +325,7 @@ class TorrentDetail(BaseMode, component.Component): if fl[1] in self.marked: fg = color_selected if fl[3]: - if self.marked[fl[1]] < self.__get_contained_files_count(file_list = fl[3]): + if self.marked[fl[1]] < self.__get_contained_files_count(file_list=fl[3]): fg = color_partially_selected else: if fg == "white": @@ -358,30 +333,31 @@ class TorrentDetail(BaseMode, component.Component): attr = "bold" if attr: - color_string = "{!%s,%s,%s!}"%(fg, bg, attr) + color_string = "{!%s,%s,%s!}" % (fg, bg, attr) else: - color_string = "{!%s,%s!}"%(fg, bg) + color_string = "{!%s,%s!}" % (fg, bg) #actually draw the dir/file string - if fl[3] and fl[4]: # this is an expanded directory - xchar = 'v' - elif fl[3]: # collapsed directory - xchar = '>' - else: # file - xchar = '-' + if fl[3] and fl[4]: # this is an expanded directory + xchar = "v" + elif fl[3]: # collapsed directory + xchar = ">" + else: # file + xchar = "-" - r = format_utils.format_row(["%s%s %s"%(" "*depth, xchar, fl[0]), - deluge.common.fsize(fl[2]), fl[5], - format_utils.format_priority(fl[6])], + r = format_utils.format_row(["%s%s %s" % (" " * depth, xchar, fl[0]), + fsize(fl[2]), fl[5], + format_utils.format_priority(fl[6])], self.column_widths) - self.add_string(off, "%s%s"%(color_string, r), trim=False) + self.add_string(off, "%s%s" % (color_string, r), trim=False) off += 1 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) + off, idx = self.draw_files(fl[3], depth + 1, off, idx + 1) + 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: @@ -401,16 +377,16 @@ class TorrentDetail(BaseMode, component.Component): length += self.__get_file_list_length(element[3]) return length - def __get_contained_files_count(self, file_list=None, idx = None): + def __get_contained_files_count(self, file_list=None, idx=None): length = 0 - 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]) + if element[1] == idx: + return self.__get_contained_files_count(file_list=element[3]) elif element[3]: - c = self.__get_contained_files_count(file_list = element[3], idx=idx) + c = self.__get_contained_files_count(file_list=element[3], idx=idx) if c > 0: return c else: @@ -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+= "{!info!} ETA: {!input!}%s" % format_utils.format_time(status["eta"]) - self.add_string(off, s); off += 1 + 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 #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" - s+= " {!info!}Ratio: {!input!}%s" % ratio_str - self.add_string(off, s); off += 1 + if ratio_str == "-": + ratio_str = "inf" + s += " {!info!}Ratio: {!input!}%s" % ratio_str + 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 + s += " {!info!}Availability: {!input!}%s" % format_utils.format_float(status["distributed_copies"]) + 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"]: @@ -511,9 +500,9 @@ class TorrentDetail(BaseMode, component.Component): return off - def refresh(self,lines=None): + 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) @@ -527,7 +516,7 @@ class TorrentDetail(BaseMode, component.Component): string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help" - string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr + string += " " * (self.cols - len(rf(string)) - len(rf(hstr))) + hstr self.add_string(self.rows - 1, string) except: @@ -579,13 +568,13 @@ class TorrentDetail(BaseMode, component.Component): if self.current_file_idx > maxlen: self.current_file_idx = maxlen - if self.current_file_idx > self.file_off + (self._listing_space - 3): + if self.current_file_idx > self.file_off + (self._listing_space - 3): self.file_off = self.current_file_idx - (self._listing_space - 3) self.refresh() def file_list_up(self, rows=1): - self.current_file_idx = max(0, self.current_file_idx-rows) + self.current_file_idx = max(0, self.current_file_idx - rows) self.file_off = min(self.file_off, self.current_file_idx) self.refresh() @@ -605,7 +594,7 @@ class TorrentDetail(BaseMode, component.Component): #Do not set priorities for the whole dir, just selected contents if f[3]: self.build_prio_list(f[3], ret_list, parent_prio, selected_prio) - else: # file, need to add to list + else: # file, need to add to list if f[1] in self.marked or parent_prio >= 0: # selected (or parent selected), use requested priority ret_list.append((f[1], selected_prio)) @@ -631,10 +620,10 @@ class TorrentDetail(BaseMode, component.Component): func = lambda idx, data, we=was_empty: self.do_priority(idx, data, we) 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): @@ -652,7 +641,7 @@ class TorrentDetail(BaseMode, component.Component): #Selected, unselect it self.__unmark_tree(self.file_list, idx) - def __mark_tree(self, file_list, idx, mark_all = False): + def __mark_tree(self, file_list, idx, mark_all=False): """ Given file_list of TorrentDetail and index of file or folder, recursively selects all files contained @@ -687,7 +676,7 @@ class TorrentDetail(BaseMode, component.Component): return total_marked - def __get_file_by_num(self, num, file_list, idx = 0): + def __get_file_by_num(self, num, file_list, idx=0): for element in file_list: if idx == num: return element @@ -703,7 +692,7 @@ class TorrentDetail(BaseMode, component.Component): return idx - def __get_file_by_name(self, name, file_list, idx = 0): + def __get_file_by_name(self, name, file_list, idx=0): for element in file_list: if element[0].strip("/") == name.strip("/"): return element @@ -719,7 +708,7 @@ class TorrentDetail(BaseMode, component.Component): return idx - def __unmark_tree(self, file_list, idx, unmark_all = False): + def __unmark_tree(self, file_list, idx, unmark_all=False): """ Given file_list of TorrentDetail and index of file or folder, recursively deselects all files contained @@ -756,8 +745,9 @@ class TorrentDetail(BaseMode, component.Component): total_marked += marked 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 + 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 for element in file_list: if idx == self.current_file_idx: @@ -782,8 +772,9 @@ 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 + def _get_full_folder_path(self, num, file_list=None, path="", idx=0): + if not file_list: + file_list = self.file_list for element in file_list: if not element[3]: @@ -794,7 +785,7 @@ class TorrentDetail(BaseMode, component.Component): return "%s%s/" % (path, element[0]) if element[4]: - i = self._get_full_folder_path(num, element[3], path + element[0] + "/", idx + 1 ) + i = self._get_full_folder_path(num, element[3], path + element[0] + "/", idx + 1) if not isinstance(i, int): return i else: @@ -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 @@ -888,14 +879,14 @@ class TorrentDetail(BaseMode, component.Component): if c == curses.KEY_UP: self.file_list_up() elif c == curses.KEY_PPAGE: - self.file_list_up(self._listing_space-2) + self.file_list_up(self._listing_space - 2) elif c == curses.KEY_HOME: self.file_off = 0 self.current_file_idx = 0 elif c == curses.KEY_DOWN: self.file_list_down() elif c == curses.KEY_NPAGE: - self.file_list_down(self._listing_space-2) + self.file_list_down(self._listing_space - 2) elif c == curses.KEY_END: self.current_file_idx = self.__get_file_list_length() - 1 self.file_off = self.current_file_idx - (self._listing_space - 3) @@ -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() diff --git a/deluge/ui/console/statusbars.py b/deluge/ui/console/statusbars.py index 778cb8020..b027b8ad6 100644 --- a/deluge/ui/console/statusbars.py +++ b/deluge/ui/console/statusbars.py @@ -1,36 +1,10 @@ -# -# statusbars.py +# -*- coding: utf-8 -*- # # Copyright (C) 2009 Andrew Resch # -# 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