Author: andar

Revision: 5073

Log:
        Start work on new Console UI .. Don't expect anything to work, cause 
most of it doesn't.

Diff:
Modified: trunk/deluge/ui/console/colors.py
===================================================================
--- trunk/deluge/ui/console/colors.py   2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/colors.py   2009-04-18 05:35:03 UTC (rev 5073)
@@ -1,8 +1,7 @@
-#!/usr/bin/env python
 #
 # colors.py
 #
-# Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
 #
 # Deluge is free software.
 #
@@ -22,87 +21,108 @@
 #      51 Franklin Street, Fifth Floor
 #      Boston, MA    02110-1301, USA.
 #
-import re, sys
 
-def color(string, fg=None, attrs=[], bg=None, keep_open=False, input=False):
-    if isinstance(attrs, basestring):
-        attrs = [attrs]
-    attrs = map(str.lower, attrs)
-    ansi_reset = "\x1b[0m"
-    if input:
-        ansi_reset = '\001'+ansi_reset+'\002'
-    if len(attrs) == 1 and 'reset' in attrs:
-        return ansi_reset
-    colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 
'white']
-    attributes = ['reset', 'bright', 'dim', None, 'underscore', 'blink', 
'reverse', 'hidden']
-    _fg = 30 + colors.index(fg.lower()) if fg and fg.lower() in colors else 
None
-    _bg = 40 + colors.index(bg.lower()) if bg and bg.lower() in colors else 
None
-    _attrs = [ str(attributes.index(a)) for a in attrs if a in attributes]
-    color_vals = map(str, filter(lambda x: x is not None, [_fg, _bg]))
-    color_vals.extend(_attrs)
-    reset_cmd = ansi_reset if not keep_open else ''
-    color_code = '\x1b['+';'.join(color_vals)+'m'
-    if input:
-        color_code = '\001'+color_code+'\002'
-    return color_code+string+reset_cmd
+import curses
 
-def make_style(*args, **kwargs):
-    return lambda text: color(text, *args, **kwargs)
+colors = [
+    'COLOR_BLACK',
+    'COLOR_BLUE',
+    'COLOR_CYAN',
+    'COLOR_GREEN',
+    'COLOR_MAGENTA',
+    'COLOR_RED',
+    'COLOR_WHITE',
+    'COLOR_YELLOW'
+]
 
-default_style = {
-    'black' : make_style(fg='black'),
-    'red' : make_style(fg='red'),
-    'green' : make_style(fg='green'),
-    'yellow' : make_style(fg='yellow'),
-    'blue' : make_style(fg='blue'),
-    'magenta' : make_style(fg='magenta'),
-    'cyan' : make_style(fg='cyan'),
-    'white' : make_style(fg='white'),
+# {(fg, bg): pair_number, ...}
+color_pairs = {
+    ("white", "black"): 0 # Special case, can't be changed
+}
 
-    'bold_black' : make_style(fg='black', attrs='bright'),
-    'bold_red' : make_style(fg='red', attrs='bright'),
-    'bold_green' : make_style(fg='green', attrs='bright'),
-    'bold_yellow' : make_style(fg='yellow', attrs='bright'),
-    'bold_blue' : make_style(fg='blue', attrs='bright'),
-    'bold_magenta' : make_style(fg='magenta', attrs='bright'),
-    'bold_cyan' : make_style(fg='cyan', attrs='bright'),
-    'bold_white' : make_style(fg='white', attrs='bright'),
+# Some default color schemes
+schemes = {
+    "input": ("white", "black"),
+    "status": ("yellow", "blue", "bold"),
+    "info": ("white", "black", "bold"),
+    "error": ("red", "black", "bold"),
+    "success": ("green", "black", "bold")
 }
 
-class Template(str):
-    regex = re.compile(r'{{\s*(?P<style>.*?)\((?P<arg>.*?)\)\s*}}')
-    style = default_style
-    def __new__(self, text):
-        return str.__new__(self, Template.regex.sub(lambda mo: 
Template.style[mo.group('style')](mo.group('arg')), text))
 
-    def __call__(self, *args, **kwargs):
-        if kwargs:
-            return str(self) % kwargs
+def init_colors():
+    # Create the color_pairs dict
+    counter = 1
+    for fg in colors:
+        for bg in colors:
+            if fg == "COLOR_WHITE" and bg == "COLOR_BLACK":
+                continue
+            color_pairs[(fg[6:].lower(), bg[6:].lower())] = counter
+            curses.init_pair(counter, getattr(curses, fg), getattr(curses, bg))
+            counter += 1
+
+class BadColorString(Exception):
+    pass
+
+def parse_color_string(s):
+    """
+    Parses a string and returns a list of 2-tuples (color, string).
+
+    :param s:, string to parse
+
+    """
+    ret = []
+    # Keep track of where the strings
+    col_index = 0
+    while s.find("{{") != -1:
+        begin = s.find("{{")
+        end = s.find("}}")
+        if end == -1:
+            raise BadColorString("Missing closing '}}'")
+
+        # Get a list of attributes in the bracketed section
+        attrs = s[begin+2:end].split(",")
+
+        if len(attrs) == 1 and not attrs:
+            raise BadColorString("No description in {{ }}")
+
+        def apply_attrs(cp, a):
+            # This function applies any additional attributes as necessary
+            if len(a) > 2:
+                for attr in a[2:]:
+                    cp |= getattr(curses, "A_" + attr.upper())
+            return cp
+
+        # Check for a builtin type first
+        if attrs[0] in schemes:
+            # Get the color pair number
+            color_pair = curses.color_pair(color_pairs[(schemes[attrs[0]][0], 
schemes[attrs[0]][1])])
+            color_pair = apply_attrs(color_pair, schemes[attrs[0]])
+
         else:
-            return str(self) % args
+            # This is a custom color scheme
+            fg = attrs[0]
+            if len(attrs) > 1:
+                bg = attrs[1]
+            else:
+                # Default to 'black' if no bg is chosen
+                bg = "black"
 
-class InputTemplate(Template):
-    """This class is similar to Template, but the escapes are wrapped in \001
-       and \002 so that readline can properly know the length of each line and
-       can wrap lines accordingly.  Use this class for any colored text which
-       needs to be used in input prompts, such as in calls to raw_input()."""
-    input_codes = re.compile('(\x1b\[.*?m)')
-    def __new__(self, text):
-        regular_string = InputTemplate.regex.sub(lambda mo: 
InputTemplate.style[mo.group('style')](mo.group('arg')) , text)
-        return str.__new__(self, InputTemplate.input_codes.sub(r'\001\1\002', 
regular_string))
+            color_pair = curses.color_pair(color_pairs[(fg, bg)])
+            # Check for additional attributes and OR them to the color_pair
+            color_pair = apply_attrs(color_pair, attrs)
 
-class struct(object):
-    pass
+        # We need to find the text now, so lets try to find another {{ and if
+        # there isn't one, then it's the rest of the string
+        next_begin = s.find("{{", end)
+        if next_begin == -1:
+            ret.append((color_pair, s[end+2:]))
+            break
+        else:
+            ret.append((color_pair, s[end+2:next_begin]))
+            s = s[next_begin:]
 
-templates = struct()
-templates.prompt = InputTemplate('{{bold_white(%s)}}')
-templates.ERROR = Template('{{bold_red( * %s)}}')
-templates.SUCCESS = Template('{{bold_green( * %s)}}')
-templates.help = Template(' * {{bold_blue(%-*s)}} %s')
-templates.info_general = Template('{{bold_blue(*** %s:)}} %s')
-templates.info_transfers = Template('{{bold_green(*** %s:)}} %s')
-templates.info_network = Template('{{bold_white(*** %s:)}} %s')
-templates.info_files_header = Template('{{bold_cyan(*** %s:)}}')
-templates.info_peers_header = Template('{{bold_magenta(*** %s:)}}')
-templates.info_peers = Template('\t * {{bold_blue(%-22s)}} 
{{bold_green(%-25s)}} {{bold_cyan(Up: %-12s)}} {{bold_magenta(Down: %-12s)}}')
-templates.config_display = Template(' * {{bold_blue(%s)}}: %s')
+    if not ret:
+        # There was no color scheme so we add it with a 0 for white on black
+        ret = [(0, s)]
+    return ret

Modified: trunk/deluge/ui/console/commands/add.py
===================================================================
--- trunk/deluge/ui/console/commands/add.py     2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/add.py     2009-04-18 05:35:03 UTC (rev 
5073)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
 #
 # add.py
 #
 # Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
 #
 # Deluge is free software.
 #
@@ -24,10 +24,11 @@
 #
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
 from optparse import make_option
 import os
+import base64
 
 class Command(BaseCommand):
     """Add a torrent"""
@@ -39,19 +40,18 @@
     usage = "Usage: add [-p <save-location>] <torrent-file> [<torrent-file> 
...]"
 
     def handle(self, *args, **options):
-        if options['path'] is None:
-            def _got_config(configs):
-                global save_path
-                save_path = configs['download_location']
-            client.get_config(_got_config)
-            client.force_call()
-            options['path'] = save_path
-        else:
-            client.set_config({'download_location': options['path']})
-        if not options['path']:
-            print templates.ERROR("There's no save-path specified. You must 
specify a path to save the downloaded files.")
-            return
-        try:
-            client.add_torrent_file(args)
-        except Exception, msg:
-            print templates.ERROR("Error: %s" % str(msg))
+        t_options = {}
+        if options["path"]:
+            t_options["download_location"] = options["path"]
+
+        for arg in args:
+            self.write("{{info}}Attempting to add torrent: %s" % arg)
+            filename = os.path.split(arg)[-1]
+            filedump = base64.encodestring(open(arg).read())
+
+            def on_success(result):
+                self.write("{{success}}Torrent added!")
+            def on_fail(result):
+                self.write("{{error}}Torrent was not added! %s" % result)
+
+            client.core.add_torrent_file(filename, filedump, 
t_options).addCallback(on_success).addErrback(on_fail)

Modified: trunk/deluge/ui/console/commands/config.py
===================================================================
--- trunk/deluge/ui/console/commands/config.py  2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/config.py  2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,8 +24,8 @@
 #
 
 from deluge.ui.console.main import BaseCommand, match_torrents
-from deluge.ui.console.colors import templates, default_style as style
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
 from optparse import make_option
 import re
 
@@ -136,4 +136,3 @@
 
     def split(self, text):
         return str.split(text)
-

Modified: trunk/deluge/ui/console/commands/connect.py
===================================================================
--- trunk/deluge/ui/console/commands/connect.py 2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/connect.py 2009-04-18 05:35:03 UTC (rev 
5073)
@@ -23,14 +23,19 @@
 #      Boston, MA    02110-1301, USA.
 #
 from deluge.ui.console.main import BaseCommand
-from deluge.ui.console.colors import templates, default_style as style
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
 
 class Command(BaseCommand):
     """Connect to a new deluge server."""
-    def handle(self, host='localhost', port='58846', **options):
+    def handle(self, host='localhost', port='58846', username="", password="", 
**options):
         port = int(port)
-        if host[:7] != "http://":
-            host = "http://"; + host
-        client.set_core_uri("%s:%d" % (host, port))
-        print templates.SUCCESS('connected to %s:%d' % (host, port))
+        d = client.connect(host, port, username, password)
+        def on_connect(result):
+            print templates.SUCCESS('Connected to %s:%d!' % (host, port))
+
+        def on_connect_fail(result):
+            print templates.ERROR("Failed to connect to %s:%d!" % (host, port))
+
+        d.addCallback(on_connect)
+        d.addErrback(on_connect_fail)

Modified: trunk/deluge/ui/console/commands/debug.py
===================================================================
--- trunk/deluge/ui/console/commands/debug.py   2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/debug.py   2009-04-18 05:35:03 UTC (rev 
5073)
@@ -23,8 +23,8 @@
 #      Boston, MA    02110-1301, USA.
 #
 from deluge.ui.console.main import BaseCommand
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
 import logging
 
 class Command(BaseCommand):

Modified: trunk/deluge/ui/console/commands/halt.py
===================================================================
--- trunk/deluge/ui/console/commands/halt.py    2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/halt.py    2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,8 +24,8 @@
 #
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
 
 class Command(BaseCommand):
     "Shutdown the deluge server."

Modified: trunk/deluge/ui/console/commands/help.py
===================================================================
--- trunk/deluge/ui/console/commands/help.py    2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/help.py    2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,7 +24,8 @@
 #
 from deluge.ui.console import UI_PATH
 from deluge.ui.console.main import BaseCommand, load_commands
-from deluge.ui.console.colors import templates
+import deluge.ui.console.colors as colors
+#from deluge.ui.console.colors import templates
 import os
 
 class Command(BaseCommand):
@@ -35,30 +36,32 @@
     def __init__(self):
         BaseCommand.__init__(self)
         # get a list of commands, exclude 'help' so we won't run into a 
recursive loop.
-        self._commands = load_commands(os.path.join(UI_PATH,'commands'), 
exclude=['help'])
+        self._commands = load_commands(os.path.join(UI_PATH,'commands'), None, 
exclude=['help'])
         self._commands['help'] = self
 
     def handle(self, *args, **options):
         if args:
             if len(args) > 1:
-                print usage
+                #print usage
+                self.write(usage)
                 return
             try:
                 cmd = self._commands[args[0]]
             except KeyError:
-                print templates.ERROR('unknown command %r' % args[0])
+                #print templates.ERROR('unknown command %r' % args[0])
+                self.write("{{error}}Unknown command %r" % args[0])
                 return
             try:
                 parser = cmd.create_parser()
-                print parser.format_help()
+                self.write(parser.format_help())
             except AttributeError, e:
-                print cmd.__doc__ or 'No help for this command'
+                self.write(cmd.__doc__ or 'No help for this command')
         else:
             max_length = max( len(k) for k in self._commands)
             for cmd in sorted(self._commands):
-                print templates.help(max_length, cmd, 
self._commands[cmd].__doc__ or '')
-            print
-            print 'for help on a specific command, use "<command> --help"'
+                self.write("{{info}}" + cmd + "{{input}} - " + 
self._commands[cmd].__doc__ or '')
+            self.write("")
+            self.write('For help on a specific command, use "<command> 
--help"')
 
     def complete(self, text, *args):
         return [ x for x in self._commands.keys() if x.startswith(text) ]

Modified: trunk/deluge/ui/console/commands/info.py
===================================================================
--- trunk/deluge/ui/console/commands/info.py    2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/info.py    2009-04-18 05:35:03 UTC (rev 
5073)
@@ -25,8 +25,9 @@
 
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+#from deluge.ui.console.colors import templates
+from deluge.ui.client import client
 import deluge.common as common
 from optparse import make_option
 
@@ -72,16 +73,19 @@
 
 
     def handle(self, *args, **options):
-        args = mapping.to_ids(args)
-        self.torrents = match_torrents(args)
-        for tor in self.torrents:
-            self.show_info(tor, options.get('verbose'))
+        def on_to_ids(result):
+            def on_match_torrents(torrents):
+                for torrent in torrents:
+                    self.show_info(torrent, options.get("verbose"))
 
-    def complete(self, text, *args):
-        torrents = match_torrents()
-        names = mapping.get_names(torrents)
-        return [ x[1] for x in names if x[1].startswith(text) ]
+            match_torrents(result).addCallback(on_match_torrents)
+        mapping.to_ids(args).addCallback(on_to_ids)
 
+#    def complete(self, text, *args):
+#        torrents = match_torrents()
+#        names = mapping.get_names(torrents)
+#        return [ x[1] for x in names if x[1].startswith(text) ]
+
     def show_info(self, torrent, verbose):
         def _got_torrent_status(state):
             print templates.info_general('ID', torrent)
@@ -124,4 +128,4 @@
                     print templates.info_peers(str(peer['ip']), 
unicode(client_str),
                                         str(common.fspeed(peer['up_speed'])), 
str(common.fspeed(peer['down_speed'])))
             print ""
-        client.get_torrent_status(_got_torrent_status, torrent, status_keys)
+        client.core.get_torrent_status(torrent, 
status_keys).addCallback(_got_torrent_status)

Modified: trunk/deluge/ui/console/commands/pause.py
===================================================================
--- trunk/deluge/ui/console/commands/pause.py   2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/pause.py   2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,8 +24,8 @@
 #
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
 
 class Command(BaseCommand):
     """Pause a torrent"""

Modified: trunk/deluge/ui/console/commands/quit.py
===================================================================
--- trunk/deluge/ui/console/commands/quit.py    2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/quit.py    2009-04-18 05:35:03 UTC (rev 
5073)
@@ -22,7 +22,7 @@
 #      51 Franklin Street, Fifth Floor
 #      Boston, MA    02110-1301, USA.
 #
-from deluge.ui.client import aclient as client
+from deluge.ui.client import client
 from deluge.ui.console.main import BaseCommand
 
 class Command(BaseCommand):
@@ -31,4 +31,3 @@
     def handle(self, *args, **options):
         print "Thanks!"
         raise StopIteration
-

Modified: trunk/deluge/ui/console/commands/resume.py
===================================================================
--- trunk/deluge/ui/console/commands/resume.py  2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/resume.py  2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,8 +24,8 @@
 #
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
 
 class Command(BaseCommand):
     """Resume a torrent"""
@@ -49,4 +49,3 @@
         torrents = match_torrents()
         names = mapping.get_names(torrents)
         return [ x[1] for x in names if x[1].startswith(text) ]
-

Modified: trunk/deluge/ui/console/commands/rm.py
===================================================================
--- trunk/deluge/ui/console/commands/rm.py      2009-04-17 21:24:49 UTC (rev 
5072)
+++ trunk/deluge/ui/console/commands/rm.py      2009-04-18 05:35:03 UTC (rev 
5073)
@@ -24,8 +24,8 @@
 #
 from deluge.ui.console.main import BaseCommand, match_torrents
 from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
 from optparse import make_option
 import os
 

Modified: trunk/deluge/ui/console/main.py
===================================================================
--- trunk/deluge/ui/console/main.py     2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/main.py     2009-04-18 05:35:03 UTC (rev 5073)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
 #
 # main.py
 #
 # Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
 #
 # Deluge is free software.
 #
@@ -22,16 +22,23 @@
 #      51 Franklin Street, Fifth Floor
 #      Boston, MA    02110-1301, USA.
 #
-import logging
-logging.disable(logging.ERROR)
+
 import os, sys
 import optparse
 from deluge.ui.console import UI_PATH
-from deluge.ui.console.colors import Template, make_style, templates, 
default_style as style
-from deluge.ui.client import aclient as client
-from deluge.ui.common import get_localhost_auth_uri
+#from deluge.ui.console.colors import Template, make_style, templates, 
default_style as style
+import deluge.component as component
+from deluge.ui.client import client
+import deluge.common
+from deluge.ui.coreconfig import CoreConfig
+from deluge.ui.console.statusbars import StatusBars
+
+from twisted.internet import defer, reactor
 import shlex
+import screen
+import colors
 
+from deluge.log import LOG as log
 
 class OptionParser(optparse.OptionParser):
     """subclass from optparse.OptionParser so exit() won't exit."""
@@ -49,7 +56,6 @@
         """
         raise
 
-
 class BaseCommand(object):
 
     usage = 'usage'
@@ -79,24 +85,24 @@
                             epilog = self.epilog,
                             option_list = self.option_list)
 
-def match_torrents(array=None):
-    global torrents
-    if array is None:
-        array = list()
-    torrents = []
+
+def match_torrents(array=[]):
+    # Make sure we don't have any duplicates
     array = set(array)
+    # We return this defer and it will be fired once we received the session
+    # state and intersect the data.
+    d = defer.Deferred()
+
     def _got_session_state(tors):
         if not array:
-            torrents.extend(tors)
-            return
-        tors = set(tors)
-        torrents.extend(list(tors.intersection(array)))
-        return
-    client.get_session_state(_got_session_state)
-    client.force_call()
-    return torrents
+            d.callback(tors)
+        d.callback(list(tors.intersection(array)))
 
-def load_commands(command_dir, exclude=[]):
+    client.core.get_session_state().addCallback(_got_session_state)
+
+    return d
+
+def load_commands(command_dir, write_func, exclude=[]):
     def get_command(name):
         return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, 
{}, ['Command']), 'Command')()
 
@@ -106,6 +112,8 @@
             if filename.split('.')[0] in exclude or filename.startswith('_') 
or not filename.endswith('.py'):
                 continue
             cmd = get_command(filename[:-3])
+            # Hack to give the commands a write function
+            cmd.write = write_func
             aliases = [ filename[:-3] ]
             aliases.extend(cmd.aliases)
             for a in aliases:
@@ -114,80 +122,103 @@
     except OSError, e:
         return {}
 
-class ConsoleUI(object):
-    prompt = '>>> '
-
+class ConsoleUI(component.Component):
     def __init__(self, args=None):
-        client.set_core_uri(get_localhost_auth_uri("http://localhost:58846";))
-        self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
+        component.Component.__init__(self, "ConsoleUI", 2)
+        # Load all the commands
+        self._commands = load_commands(os.path.join(UI_PATH, 'commands'), 
self.write)
+
+        # Try to connect to the localhost daemon
+        def on_connect(result):
+            component.start()
+        client.connect().addCallback(on_connect)
+
+        # Set the interactive flag to indicate where we should print the output
+        self.interactive = True
         if args:
-            self.precmd()
+            self.interactive = False
+            # If we have args, lets process them and quit
             #allow multiple commands split by ";"
             for arg in args.split(";"):
-                self.onecmd(arg)
-                self.postcmd()
+                self.do_command(arg)
             sys.exit(0)
 
-    def completedefault(self, *ignored):
-        """Method called to complete an input line when no command-specific
-        method is available.
+        self.coreconfig = CoreConfig()
 
-        By default, it returns an empty list.
+        # We use the curses.wrapper function to prevent the console from 
getting
+        # messed up if an uncaught exception is experienced.
+        import curses.wrapper
+        curses.wrapper(self.run)
 
+    def run(self, stdscr):
         """
-        return []
+        This method is called by the curses.wrapper to start the mainloop and
+        screen.
 
-    def completenames(self, text, *ignored):
-        return [n for n in self._commands.keys() if n.startswith(text)]
+        :param stdscr: curses screen passed in from curses.wrapper
 
-    def complete(self, text, state):
-        """Return the next possible completion for 'text'.
-        If a command has not been entered, then complete against command list.
-        Otherwise try to call complete_<command> to get list of completions.
         """
-        if state == 0:
-            import readline
-            origline = readline.get_line_buffer()
-            line = origline.lstrip()
-            stripped = len(origline) - len(line)
-            begidx = readline.get_begidx() - stripped
-            endidx = readline.get_endidx() - stripped
-            if begidx>0:
-                cmd = line.split()[0]
-                if cmd == '':
-                    compfunc = self.completedefault
-                else:
-                    try:
-                        compfunc = getattr(self._commands[cmd], 'complete')
-                    except AttributeError:
-                        compfunc = self.completedefault
-            else:
-                compfunc = self.completenames
-            self.completion_matches = compfunc(text, line, begidx, endidx)
-        try:
-            return self.completion_matches[state]
-        except IndexError:
-            return None
+        # We want to do an interactive session, so start up the curses screen 
and
+        # pass it the function that handles commands
+        colors.init_colors()
+        self.screen = screen.Screen(stdscr, self.do_command)
+        self.statusbars = StatusBars()
 
-    def preloop(self):
-        pass
+        self.screen.topbar = "{{status}}Deluge " + deluge.common.get_version() 
+ " Console"
+        self.screen.bottombar = "{{status}}"
+        self.screen.refresh()
 
-    def postloop(self):
+        # The Screen object is designed to run as a twisted reader so that it
+        # can use twisted's select poll for non-blocking user input.
+        reactor.addReader(self.screen)
+
+        # Start the twisted mainloop
+        reactor.run()
+
+    def start(self):
         pass
 
-    def precmd(self):
+    def update(self):
         pass
 
-    def onecmd(self, line):
-        if not line:
+    def write(self, line):
+        """
+        Writes a line out depending on if we're in interactive mode or not.
+
+        :param line: str, the line to print
+
+        """
+        if self.interactive:
+            self.screen.add_line(line)
+        else:
+            print(line)
+
+    def do_command(self, cmd):
+        """
+        Processes a command.
+
+        :param cmd: str, the command string
+
+        """
+        if not cmd:
             return
-        cmd, _, line = line.partition(' ')
+        cmd, _, line = cmd.partition(' ')
         try:
             parser = self._commands[cmd].create_parser()
         except KeyError:
-            print templates.ERROR('unknown command: %s' % cmd)
+            self.write("{{error}}Unknown command: %s" % cmd)
             return
         args = self._commands[cmd].split(line)
+
+        # Do a little hack here to print 'command --help' properly
+        parser._print_help = parser.print_help
+        def print_help(f=None):
+            if self.interactive:
+                self.write(parser.format_help())
+            else:
+                parser._print_help(f)
+        parser.print_help = print_help
+
         options, args = parser.parse_args(args)
         if not getattr(options, '_exit', False):
             try:
@@ -195,41 +226,4 @@
             except StopIteration, e:
                 raise
             except Exception, e:
-                print templates.ERROR(str(e))
-
-    def postcmd(self):
-        client.force_call()
-
-    def cmdloop(self):
-        self.preloop()
-        try:
-            import readline
-            self.old_completer = readline.get_completer()
-            readline.set_completer(self.complete)
-            readline.parse_and_bind("tab: complete")
-        except ImportError:
-            pass
-
-        while True:
-            try:
-                line = raw_input(templates.prompt(self.prompt)).strip()
-            except EOFError:
-                break
-            except Exception, e:
-                print e
-                continue
-            try:
-                self.precmd()
-                self.onecmd(line)
-                self.postcmd()
-            except StopIteration:
-                break
-        self.postloop()
-        print
-    run = cmdloop
-
-if __name__ == '__main__':
-    ui = ConsoleUI()
-    ui.precmd()
-    ui.onecmd(' '.join(sys.argv[1:]))
-    ui.postcmd()
+                self.write("{{error}}" + str(e))

Modified: trunk/deluge/ui/console/mapping.py
===================================================================
--- trunk/deluge/ui/console/mapping.py  2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/mapping.py  2009-04-18 05:35:03 UTC (rev 5073)
@@ -22,10 +22,11 @@
 #      51 Franklin Street, Fifth Floor
 #      Boston, MA    02110-1301, USA.
 #
-from deluge.ui.client import aclient as client
+from deluge.ui.client import client
 from deluge.ui.console.main import match_torrents
 import re
 import logging
+from twisted.internet import defer
 
 _idregex = re.compile(r'^[0-9a-f]{40}$')
 
@@ -35,40 +36,42 @@
     return bool(_idregex.match(arg))
 
 def get_names(torrents):
-    global names
-    names = []
+    d = defer.Deferred()
     def _got_torrents_status(states):
         try:
-            names.extend(list([ (tid, state['name']) for (tid, state) in 
states.items() ]))
+            d.callback(list([ (tid, state['name']) for (tid, state) in 
states.items() ]))
         except Exception, e:
             print e
+            d.errback(e)
 
-    client.get_torrents_status(_got_torrents_status, {'id':torrents}, ['name'])
-    client.force_call()
-    return names
+    client.core.get_torrents_status({'id':torrents}, 
['name']).addCallback(_got_torrents_status)
+    return d
 
 def rehash():
     global _mapping
-    torrents = match_torrents()
-    names = get_names(torrents)
-    _mapping = dict([(x[1],x[0]) for x in names])
-    logging.debug('rehashed torrent name->id mapping')
+    d = defer.Deferred()
+    def on_match_torrents(torrents):
+        def on_get_names(names):
+            _mapping = dict([(x[1],x[0]) for x in names])
+            d.callback()
+        get_names(torrents).addCallback(on_get_names)
+    match_torrents().addCallback(on_match_torrents)
+    return d
 
 def to_ids(args):
-    res = []
-    rehashed = False
-    for i in args:
-        if _arg_is_id(i):
-            res.append(i)
-        else:
-            if i in _mapping:
-                res.append(_mapping[i])
-            elif not rehashed:
-                rehash()
+    d = defer.Deferred()
+    def on_rehash(result):
+        res = []
+        for i in args:
+            if _arg_is_id(i):
+                res.append(i)
+            else:
                 if i in _mapping:
                     res.append(_mapping[i])
-                rehashed = True
-    return res
+        d.callback(res)
+    rehash().addCallback(on_rehash)
 
+    return d
+
 def names():
     return _mapping.keys()



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"deluge-commit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/deluge-commit?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to