[Reportbug-commits] r641 - in trunk (11 files)

morph-guest at users.alioth.debian.org morph-guest at users.alioth.debian.org
Sun Aug 31 22:54:17 UTC 2008


    Date: Sunday, August 31, 2008 @ 22:54:15
  Author: morph-guest
Revision: 641

renamed ui files to let urwid work

Added:
  trunk/reportbug/ui/newt_ui.py
    (from rev 637, trunk/reportbug/ui/newt.py)
  trunk/reportbug/ui/text_ui.py
    (from rev 640, trunk/reportbug/ui/text.py)
  trunk/reportbug/ui/urwid_ui.py
    (from rev 640, trunk/reportbug/ui/urwid.py)
Modified:
  trunk/bin/querybts
  trunk/bin/reportbug
  trunk/debian/changelog
  trunk/reportbug/submit.py
  trunk/reportbug/utils.py
Deleted:
  trunk/reportbug/ui/newt.py
  trunk/reportbug/ui/text.py
  trunk/reportbug/ui/urwid.py

Modified: trunk/bin/querybts
===================================================================
--- trunk/bin/querybts	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/bin/querybts	2008-08-31 22:54:15 UTC (rev 641)
@@ -169,7 +169,7 @@
 
     if interface:
         global ui, ui_mode
-        iface = '%(interface)s' % vars()
+        iface = '%(interface)s_ui' % vars()
         try:
             lib_package = __import__('reportbug.ui', fromlist=[iface])
             ui = getattr(lib_package, iface)

Modified: trunk/bin/reportbug
===================================================================
--- trunk/bin/reportbug	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/bin/reportbug	2008-08-31 22:54:15 UTC (rev 641)
@@ -58,7 +58,7 @@
 from reportbug import checkversions
 from reportbug import debianbts
 from reportbug import checkbuildd
-import reportbug.ui.text as ui
+import reportbug.ui.text_ui as ui
 
 try:
     gettext.install('reportbug')
@@ -830,7 +830,7 @@
                             "indicate problems with this interface.\n",
                             interface)
 
-        iface = '%(interface)s' % vars()
+        iface = '%(interface)s_ui' % vars()
         try:
             lib_package = __import__('reportbug.ui', fromlist=[iface])
             ui = getattr(lib_package, iface)

Modified: trunk/debian/changelog
===================================================================
--- trunk/debian/changelog	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/debian/changelog	2008-08-31 22:54:15 UTC (rev 641)
@@ -18,8 +18,16 @@
       + replaced relative imports with absolute ones
       + removed reportbug.utils import to avoid circular import (and because
         it's not needed)
+    - reportbug/ui/{newt.py,text.py,urwid.py}
+      + renamed to <file>_ui.py because from <file>.py you cannot import <file>
+        (urwid UI had this problem)
+    - reportbug/{submit.py,utils.py}, bin/{querybts,reportbug}
+      + fixed for ui file rename
+    - reportbug/ui/text_ui.py
+      + disabled utils.NEWBIELINE check to avoid circular import between text
+        and utils ******** WE NEED TO FIND A SOLUTION TO THIS **********
 
- -- Sandro Tosi <matrixhasu at gmail.com>  Sun, 31 Aug 2008 22:38:39 +0200
+ -- Sandro Tosi <matrixhasu at gmail.com>  Mon, 01 Sep 2008 00:45:25 +0200
 
 reportbug (3.45) unstable; urgency=low
 

Modified: trunk/reportbug/submit.py
===================================================================
--- trunk/reportbug/submit.py	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/reportbug/submit.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -45,7 +45,7 @@
 from exceptions import (
     NoMessage,
     )
-import ui.text as ui
+import ui.text_ui as ui
 
 quietly = False
 

Deleted: trunk/reportbug/ui/newt.py
===================================================================
--- trunk/reportbug/ui/newt.py	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/reportbug/ui/newt.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -1,300 +0,0 @@
-# Newt user interface for reportbug
-#   Written by Chris Lawrence <lawrencc at debian.org>
-#   (C) 2001-06 Chris Lawrence
-#
-# This program is freely distributable per the following license:
-#
-##  Permission to use, copy, modify, and distribute this software and its
-##  documentation for any purpose and without fee is hereby granted,
-##  provided that the above copyright notice appears in all copies and that
-##  both that copyright notice and this permission notice appear in
-##  supporting documentation.
-##
-##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
-##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
-##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
-##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
-##  SOFTWARE.
-#
-# $Id: reportbug_ui_newt.py,v 1.5.2.4 2007-04-17 19:42:56 lawrencc Exp $
-
-import sys
-import commands
-import string
-import debianbts
-from reportbug_exceptions import (
-    UINotImportable,
-    NoPackage, NoBugs, NoNetwork,
-    )
-from urlutils import launch_browser
-
-try:
-    import snack
-except ImportError:
-    raise UINotImportable, 'Please install the python-newt package to use this interface.'
-
-ISATTY = sys.stdin.isatty()
-
-try:
-    r, c = string.split(commands.getoutput('stty size'))
-    rows, columns = int(r) or 24, int(c) or 79
-except:
-    rows, columns = 24, 79
-
-def ewrite(message, *args):
-    # ewrite shouldn't do anything on newt... maybe should log to a file
-    # if specified.
-    pass
-
-log_message = ewrite
-display_failure = ewrite
-
-# Utility functions for common newt dialogs
-def newt_screen():
-    "Start a newt windowing session."
-    return snack.SnackScreen()
-
-def newt_infobox(text, height=6, width=50, title="", screen=None):
-    "Display a message and go on."
-    if not screen:
-        s = snack.SnackScreen()
-    else:
-        s = screen
-
-    t = snack.TextboxReflowed(width, text, maxHeight = s.height - 12)
-    g = snack.GridForm(s, title[:width], 1, 2)
-    g.add(t, 0, 0, padding = (0, 0, 0, 1))
-    g.draw()
-    s.refresh()
-    if not screen:
-        s.finish()
-
-def newt_dialog(text, buttons=('Ok', 'Cancel'), width=50,
-                title="", screen=None):
-    "Display a message and wait for a response from the user."
-    if not screen:
-        s = snack.SnackScreen()
-    else:
-        s = screen
-
-    selected = snack.ButtonChoiceWindow(s, title[:width], text, buttons,
-                                        width=width)
-
-    if not screen:
-        s.finish()
-    return selected
-
-def newt_msgbox(text, width=50, title="", screen=None):
-    "Display a message and wait for an OK from the user."
-    return newt_dialog(text, ['Ok'], width, title, screen)
-
-def long_message(message, *args):
-    if args:
-        message = message % tuple(args)
-    newt_msgbox(message)
-
-final_message = long_message
-
-def newt_menu(text, height=20, width=60, menuheight=15, menu=None,
-         title="", scroll=0, screen=None, startpos=1):
-    "Display a menu of choices for the user."
-    if not menu: return None
-    if not screen:
-        s = snack.SnackScreen()
-    else:
-        s = screen
-    items = []
-    for item in menu:
-        if item[0]:
-            items.append(('%6s: %s' % item)[:width])
-        else:
-            items.append(item[1])
-
-    if len(items) > menuheight: scroll=1
-    res = startpos
-    while 1:
-        button, res = snack.ListboxChoiceWindow(s, title[:width], text, items,
-                                                width=width, height=menuheight,
-                                                scroll=scroll, default=res,
-                                                buttons=('View', 'Quit'))
-        if button == 'quit':
-            if not screen:
-                s.finish()
-            return None
-        elif menu[res][0]:
-            selected = menu[res][0]
-            break
-
-    if not screen:
-        s.finish()
-    return selected
-
-# XXX - From here on out needs to be rewritten for newt
-def select_options(msg, ok, help=None, allow_numbers=0):
-    return None
-
-def get_string(prompt, options=None, title=None, force_prompt=0):
-    return None
-
-def get_multiline(prompt, options=None, title=None, force_prompt=0):
-    return None
-
-def menu(par, options, prompt, default=None, title=None, any_ok=0, order=None):
-    return None
-
-# Things that are very UI dependent go here
-def show_report(number, system, mirrors, http_proxy, screen=None, queryonly=0,
-                title='', archived='no'):
-    s = screen
-    if not s:
-        s = newt_screen()
-
-    sysinfo = debianbts.SYSTEMS[system]
-    newt_infobox('Retrieving report #%d from %s bug tracking system...' % (
-        number, sysinfo['name']), title=title, screen=s)
-
-    width = columns-8
-    info = debianbts.get_report(number, system, mirrors=mirrors,
-                                http_proxy=http_proxy, archived=archived)
-    if not info:
-        s.popWindow()
-        newt_msgbox('Bug report #%d not found.' % number,
-                    screen=s, title=title)
-        if not screen:
-            s.finish()
-        return
-
-    buttons = ['Ok', 'More details (launch browser)', 'Quit']
-    if not queryonly:
-        buttons.append('Submit more information')
-
-    s.popWindow()
-    while 1:
-        (bugtitle, bodies) = info
-        body = bodies[0]
-
-        lines = string.split(body, '\n')
-        lines = map(lambda x, y=width: x[:y], lines)
-        body = string.join(lines, '\n')
-
-        r = newt_dialog(text=body, title=bugtitle, screen=s, width=width,
-                        buttons=buttons)
-        if not r or (r == 'ok'):
-            break
-        elif r == 'quit':
-            if not screen:
-                s.finish()
-            return -1
-        elif r == 'submit more information':
-            if not screen:
-                s.finish()
-            return number
-
-        s.suspend()
-        # print chr(27)+'c'
-        # os.system('stty sane; clear')
-        launch_browser(debianbts.get_report_url(system, number, archived))
-        s.resume()
-
-    if not screen:
-        s.finish()
-    return
-
-def handle_bts_query(package, bts, mirrors=None, http_proxy="",
-                     queryonly=0, screen=None, title="", archived='no',
-                     source=0):
-    sysinfo = debianbts.SYSTEMS[bts]
-    root = sysinfo.get('btsroot')
-    if not root:
-        ewrite("%s bug tracking system has no web URL; bypassing query.\n",
-               sysinfo['name'])
-        return
-
-    scr = screen
-    if not scr:
-        scr = newt_screen()
-
-    if isinstance(package, basestring):
-        if source:
-            newt_infobox('Querying %s bug tracking system for reports on'
-                         ' src:%s\n' % (debianbts.SYSTEMS[bts]['name'],
-                                        package),
-                         screen=scr, title=title)
-        else:
-            newt_infobox('Querying %s bug tracking system for reports on %s\n'%
-                         (debianbts.SYSTEMS[bts]['name'], package),
-                         screen=scr, title=title)
-    else:
-        newt_infobox('Querying %s bug tracking system for reports %s\n' %
-                     (debianbts.SYSTEMS[bts]['name'], ' '.join([str(x) for x in
-                                                                package])),
-                     screen=scr, title=title)
-
-    result = None
-    try:
-        (count, sectitle, hierarchy) = debianbts.get_reports(
-            package, bts, mirrors=mirrors,
-            http_proxy=http_proxy, archived=archived, source=source)
-
-        if not count:
-            scr.popWindow()
-            if hierarchy == None:
-                raise NoPackage
-            else:
-                raise NoBugs
-        else:
-            if count > 1:
-                sectitle = '%d bug reports found' % (count)
-            else:
-                sectitle = '%d bug report found' % (count)
-
-            list = []
-            for (t, bugs) in hierarchy:
-                bcount = len(bugs)
-                list.append( ('', t) )
-                for bug in bugs:
-                    bits = string.split(bug[1:], ':', 1)
-                    tag, info = bits
-                    info = string.strip(info)
-                    if not info:
-                        info = '(no subject)'
-                    list.append( (tag, info) )
-
-            p = 1
-            scr.popWindow()
-            while True:
-                info = newt_menu('Select a bug to read the report:', rows-6,
-                                 columns-10, rows-15, list, sectitle,
-                                 startpos=p, screen=scr)
-                if not info:
-                    break
-                else:
-                    p = i = 0
-                    for (number, subject) in list:
-                        if number == info: p = i
-			i += 1
-
-                    if ' ' in info:
-                        info, blah = info.split(' ', 1)
-
-                    res = show_report(int(info), bts, mirrors,
-                                      http_proxy, screen=scr,
-                                      queryonly=queryonly, title=title)
-                    if res:
-                        result = res
-                        break
-
-    except (IOError, NoNetwork):
-        scr.popWindow()
-        newt_msgbox('Unable to connect to %s BTS.' % sysinfo['name'],
-                    screen=scr, title=title)
-    except NoPackage:
-        #scr.popWindow()
-        newt_msgbox('No record of this package found.',
-                    screen=scr, title=title)
-
-    if not screen:
-        scr.finish()
-    return result

Copied: trunk/reportbug/ui/newt_ui.py (from rev 637, trunk/reportbug/ui/newt.py)
===================================================================
--- trunk/reportbug/ui/newt_ui.py	                        (rev 0)
+++ trunk/reportbug/ui/newt_ui.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -0,0 +1,300 @@
+# Newt user interface for reportbug
+#   Written by Chris Lawrence <lawrencc at debian.org>
+#   (C) 2001-06 Chris Lawrence
+#
+# This program is freely distributable per the following license:
+#
+##  Permission to use, copy, modify, and distribute this software and its
+##  documentation for any purpose and without fee is hereby granted,
+##  provided that the above copyright notice appears in all copies and that
+##  both that copyright notice and this permission notice appear in
+##  supporting documentation.
+##
+##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
+##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+##  SOFTWARE.
+#
+# $Id: reportbug_ui_newt.py,v 1.5.2.4 2007-04-17 19:42:56 lawrencc Exp $
+
+import sys
+import commands
+import string
+import debianbts
+from reportbug_exceptions import (
+    UINotImportable,
+    NoPackage, NoBugs, NoNetwork,
+    )
+from urlutils import launch_browser
+
+try:
+    import snack
+except ImportError:
+    raise UINotImportable, 'Please install the python-newt package to use this interface.'
+
+ISATTY = sys.stdin.isatty()
+
+try:
+    r, c = string.split(commands.getoutput('stty size'))
+    rows, columns = int(r) or 24, int(c) or 79
+except:
+    rows, columns = 24, 79
+
+def ewrite(message, *args):
+    # ewrite shouldn't do anything on newt... maybe should log to a file
+    # if specified.
+    pass
+
+log_message = ewrite
+display_failure = ewrite
+
+# Utility functions for common newt dialogs
+def newt_screen():
+    "Start a newt windowing session."
+    return snack.SnackScreen()
+
+def newt_infobox(text, height=6, width=50, title="", screen=None):
+    "Display a message and go on."
+    if not screen:
+        s = snack.SnackScreen()
+    else:
+        s = screen
+
+    t = snack.TextboxReflowed(width, text, maxHeight = s.height - 12)
+    g = snack.GridForm(s, title[:width], 1, 2)
+    g.add(t, 0, 0, padding = (0, 0, 0, 1))
+    g.draw()
+    s.refresh()
+    if not screen:
+        s.finish()
+
+def newt_dialog(text, buttons=('Ok', 'Cancel'), width=50,
+                title="", screen=None):
+    "Display a message and wait for a response from the user."
+    if not screen:
+        s = snack.SnackScreen()
+    else:
+        s = screen
+
+    selected = snack.ButtonChoiceWindow(s, title[:width], text, buttons,
+                                        width=width)
+
+    if not screen:
+        s.finish()
+    return selected
+
+def newt_msgbox(text, width=50, title="", screen=None):
+    "Display a message and wait for an OK from the user."
+    return newt_dialog(text, ['Ok'], width, title, screen)
+
+def long_message(message, *args):
+    if args:
+        message = message % tuple(args)
+    newt_msgbox(message)
+
+final_message = long_message
+
+def newt_menu(text, height=20, width=60, menuheight=15, menu=None,
+         title="", scroll=0, screen=None, startpos=1):
+    "Display a menu of choices for the user."
+    if not menu: return None
+    if not screen:
+        s = snack.SnackScreen()
+    else:
+        s = screen
+    items = []
+    for item in menu:
+        if item[0]:
+            items.append(('%6s: %s' % item)[:width])
+        else:
+            items.append(item[1])
+
+    if len(items) > menuheight: scroll=1
+    res = startpos
+    while 1:
+        button, res = snack.ListboxChoiceWindow(s, title[:width], text, items,
+                                                width=width, height=menuheight,
+                                                scroll=scroll, default=res,
+                                                buttons=('View', 'Quit'))
+        if button == 'quit':
+            if not screen:
+                s.finish()
+            return None
+        elif menu[res][0]:
+            selected = menu[res][0]
+            break
+
+    if not screen:
+        s.finish()
+    return selected
+
+# XXX - From here on out needs to be rewritten for newt
+def select_options(msg, ok, help=None, allow_numbers=0):
+    return None
+
+def get_string(prompt, options=None, title=None, force_prompt=0):
+    return None
+
+def get_multiline(prompt, options=None, title=None, force_prompt=0):
+    return None
+
+def menu(par, options, prompt, default=None, title=None, any_ok=0, order=None):
+    return None
+
+# Things that are very UI dependent go here
+def show_report(number, system, mirrors, http_proxy, screen=None, queryonly=0,
+                title='', archived='no'):
+    s = screen
+    if not s:
+        s = newt_screen()
+
+    sysinfo = debianbts.SYSTEMS[system]
+    newt_infobox('Retrieving report #%d from %s bug tracking system...' % (
+        number, sysinfo['name']), title=title, screen=s)
+
+    width = columns-8
+    info = debianbts.get_report(number, system, mirrors=mirrors,
+                                http_proxy=http_proxy, archived=archived)
+    if not info:
+        s.popWindow()
+        newt_msgbox('Bug report #%d not found.' % number,
+                    screen=s, title=title)
+        if not screen:
+            s.finish()
+        return
+
+    buttons = ['Ok', 'More details (launch browser)', 'Quit']
+    if not queryonly:
+        buttons.append('Submit more information')
+
+    s.popWindow()
+    while 1:
+        (bugtitle, bodies) = info
+        body = bodies[0]
+
+        lines = string.split(body, '\n')
+        lines = map(lambda x, y=width: x[:y], lines)
+        body = string.join(lines, '\n')
+
+        r = newt_dialog(text=body, title=bugtitle, screen=s, width=width,
+                        buttons=buttons)
+        if not r or (r == 'ok'):
+            break
+        elif r == 'quit':
+            if not screen:
+                s.finish()
+            return -1
+        elif r == 'submit more information':
+            if not screen:
+                s.finish()
+            return number
+
+        s.suspend()
+        # print chr(27)+'c'
+        # os.system('stty sane; clear')
+        launch_browser(debianbts.get_report_url(system, number, archived))
+        s.resume()
+
+    if not screen:
+        s.finish()
+    return
+
+def handle_bts_query(package, bts, mirrors=None, http_proxy="",
+                     queryonly=0, screen=None, title="", archived='no',
+                     source=0):
+    sysinfo = debianbts.SYSTEMS[bts]
+    root = sysinfo.get('btsroot')
+    if not root:
+        ewrite("%s bug tracking system has no web URL; bypassing query.\n",
+               sysinfo['name'])
+        return
+
+    scr = screen
+    if not scr:
+        scr = newt_screen()
+
+    if isinstance(package, basestring):
+        if source:
+            newt_infobox('Querying %s bug tracking system for reports on'
+                         ' src:%s\n' % (debianbts.SYSTEMS[bts]['name'],
+                                        package),
+                         screen=scr, title=title)
+        else:
+            newt_infobox('Querying %s bug tracking system for reports on %s\n'%
+                         (debianbts.SYSTEMS[bts]['name'], package),
+                         screen=scr, title=title)
+    else:
+        newt_infobox('Querying %s bug tracking system for reports %s\n' %
+                     (debianbts.SYSTEMS[bts]['name'], ' '.join([str(x) for x in
+                                                                package])),
+                     screen=scr, title=title)
+
+    result = None
+    try:
+        (count, sectitle, hierarchy) = debianbts.get_reports(
+            package, bts, mirrors=mirrors,
+            http_proxy=http_proxy, archived=archived, source=source)
+
+        if not count:
+            scr.popWindow()
+            if hierarchy == None:
+                raise NoPackage
+            else:
+                raise NoBugs
+        else:
+            if count > 1:
+                sectitle = '%d bug reports found' % (count)
+            else:
+                sectitle = '%d bug report found' % (count)
+
+            list = []
+            for (t, bugs) in hierarchy:
+                bcount = len(bugs)
+                list.append( ('', t) )
+                for bug in bugs:
+                    bits = string.split(bug[1:], ':', 1)
+                    tag, info = bits
+                    info = string.strip(info)
+                    if not info:
+                        info = '(no subject)'
+                    list.append( (tag, info) )
+
+            p = 1
+            scr.popWindow()
+            while True:
+                info = newt_menu('Select a bug to read the report:', rows-6,
+                                 columns-10, rows-15, list, sectitle,
+                                 startpos=p, screen=scr)
+                if not info:
+                    break
+                else:
+                    p = i = 0
+                    for (number, subject) in list:
+                        if number == info: p = i
+			i += 1
+
+                    if ' ' in info:
+                        info, blah = info.split(' ', 1)
+
+                    res = show_report(int(info), bts, mirrors,
+                                      http_proxy, screen=scr,
+                                      queryonly=queryonly, title=title)
+                    if res:
+                        result = res
+                        break
+
+    except (IOError, NoNetwork):
+        scr.popWindow()
+        newt_msgbox('Unable to connect to %s BTS.' % sysinfo['name'],
+                    screen=scr, title=title)
+    except NoPackage:
+        #scr.popWindow()
+        newt_msgbox('No record of this package found.',
+                    screen=scr, title=title)
+
+    if not screen:
+        scr.finish()
+    return result

Deleted: trunk/reportbug/ui/text.py
===================================================================
--- trunk/reportbug/ui/text.py	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/reportbug/ui/text.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -1,937 +0,0 @@
-# Text user interface for reportbug
-#   Written by Chris Lawrence <lawrencc at debian.org>
-#   (C) 2001-08 Chris Lawrence
-#
-# This program is freely distributable per the following license:
-#
-##  Permission to use, copy, modify, and distribute this software and its
-##  documentation for any purpose and without fee is hereby granted,
-##  provided that the above copyright notice appears in all copies and that
-##  both that copyright notice and this permission notice appear in
-##  supporting documentation.
-##
-##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
-##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
-##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
-##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
-##  SOFTWARE.
-#
-# $Id: reportbug_ui_text.py,v 1.19.2.9 2008-04-18 05:38:28 lawrencc Exp $
-
-import sys
-import os
-import commands
-import re
-import math
-import string
-import errno
-import glob
-import getpass
-import textwrap
-try:
-    import readline
-except ImportError:
-    readline = None
-
-from reportbug import debianbts, hiermatch
-from reportbug.exceptions import (
-    NoReport, NoPackage, NoBugs, NoNetwork,
-    InvalidRegex,
-    )
-from reportbug.urlutils import launch_browser
-
-ISATTY = sys.stdin.isatty()
-charset = 'us-ascii'
-
-try:
-    r, c = commands.getoutput('stty size').split()
-    rows, columns = int(r) or 24, int(c) or 79
-except:
-    rows, columns = 24, 79
-
-def ewrite(message, *args):
-    if not ISATTY:
-        return
-
-    if args:
-        message = message % args
-
-    if isinstance(message, unicode):
-        message = message.encode(charset, 'replace')
-
-    sys.stderr.write(message)
-    sys.stderr.flush()
-
-log_message = ewrite
-display_failure = ewrite
-
-def indent_wrap_text(text, starttext='', indent=0, linelen=None):
-    """Wrapper for textwrap.fill to the existing API."""
-    if not linelen:
-        linelen = columns-1
-
-    if indent:
-        si = ' '*indent
-    else:
-        si = ''
-
-    text = ' '.join(text.split())
-    if not text:
-        return starttext+'\n'
-
-    output = textwrap.fill(text, width=linelen, initial_indent=starttext,
-                           subsequent_indent=si)
-    if output.endswith('\n'):
-        return output
-    return output + '\n'
-
-# Readline support, if available
-if readline is not None:
-    readline.parse_and_bind("tab: complete")
-    try:
-        # minimize the word delimeter list if possible
-        readline.set_completer_delims(' ')
-    except:
-        pass
-
-class our_completer(object):
-    def __init__(self, completions=None):
-        self.completions = None
-        if completions:
-            self.completions = tuple(map(str, completions))
-
-    def complete(self, text, i):
-        if not self.completions: return None
-
-        matching = [x for x in self.completions if x.startswith(text)]
-        if i < len(matching):
-            return matching[i]
-        else:
-            return None
-
-def our_raw_input(prompt = None, completions=None, completer=None):
-    istty = sys.stdout.isatty()
-    if not istty:
-        sys.stderr.write(prompt)
-    sys.stderr.flush()
-    if readline:
-        if completions and not completer:
-            completer = our_completer(completions).complete
-        if completer:
-            readline.set_completer(completer)
-
-    try:
-        if istty:
-            ret = raw_input(prompt)
-        else:
-            ret = raw_input()
-    except EOFError:
-        ewrite('\nUser interrupt (^D).\n')
-        raise SystemExit
-
-    if readline:
-        readline.set_completer(None)
-    return ret.strip()
-
-def select_options(msg, ok, help, allow_numbers=None, nowrap=False):
-    err_message = ''
-    for option in ok:
-        if option in string.ascii_uppercase:
-            default=option
-            break
-
-    if not help: help = {}
-
-    if '?' not in ok: ok = ok+'?'
-
-    if nowrap:
-        longmsg = msg+' ['+'|'.join(ok)+']?'+' '
-    else:
-        longmsg = indent_wrap_text(msg+' ['+'|'.join(ok)+']?').strip()+' '
-    ch = our_raw_input(longmsg, allow_numbers)
-    # Allow entry of a bug number here
-    if allow_numbers:
-        while ch and ch[0] == '#': ch = ch[1:]
-        if type(allow_numbers) == type(1):
-            try:
-                return str(int(ch))
-            except ValueError:
-                pass
-        else:
-            try:
-                number = int(ch)
-                if number in allow_numbers:
-                    return str(number)
-                else:
-                    nums = list(allow_numbers)
-                    nums.sort()
-                    err_message = 'Only the following entries are allowed: '+\
-                                  ', '.join(map(str, nums))
-            except (ValueError, TypeError):
-                pass
-
-    if not ch: ch = default
-    ch = ch[0]
-    if ch=='?':
-        help['?'] = 'Display this help.'
-        for ch in ok:
-            if ch in string.ascii_uppercase:
-                desc = '(default) '
-            else:
-                desc = ''
-            desc += help.get(ch, help.get(ch.lower(),
-                                          'No help for this option.'))
-            ewrite(indent_wrap_text(desc+'\n', '%s - '% ch, 4))
-        return select_options(msg, ok, help, allow_numbers)
-    elif (ch.lower() in ok) or (ch.upper() in ok):
-        return ch.lower()
-    elif err_message:
-        ewrite(indent_wrap_text(err_message))
-    else:
-        ewrite('Invalid selection.\n')
-
-    return select_options(msg, ok, help, allow_numbers, nowrap)
-
-def yes_no(msg, yeshelp, nohelp, default=True, nowrap=False):
-    "Return True for yes, False for no."
-    if default:
-        ok = 'Ynq'
-    else:
-        ok = 'yNq'
-
-    res = select_options(msg, ok, {'y': yeshelp, 'n': nohelp, 'q' : 'Quit.'},
-                         nowrap=nowrap)
-    if res == 'q':
-        raise SystemExit
-    return (res == 'y')
-
-def long_message(text, *args):
-    if args:
-        ewrite(indent_wrap_text(text % args))
-    else:
-        ewrite(indent_wrap_text(text))
-
-final_message = long_message
-
-def get_string(prompt, options=None, title=None, force_prompt=False,
-               default='', completer=None):
-    if prompt and (len(prompt) < 2*columns/3) and not force_prompt:
-        if default:
-            prompt = '%s [%s]: ' % (prompt, default)
-            response = our_raw_input(prompt, options, completer) or default
-        else:
-            response = our_raw_input(prompt, options, completer)
-    else:
-        if prompt:
-            ewrite(indent_wrap_text(prompt))
-        if default:
-            response = our_raw_input('[%s]> ' % default, options, completer) or default
-        else:
-            response = our_raw_input('> ', options, completer)
-
-    # Translate the response into a Unicode string
-    if response is not None:
-        response = unicode(response, charset, 'replace')
-
-    return response
-
-def get_multiline(prompt):
-    ewrite('\n')
-    ewrite(indent_wrap_text(prompt + "  Press ENTER on a blank line to continue.\n"))
-    l = []
-    while 1:
-        entry = get_string('', force_prompt=True).strip()
-        if not entry:
-            break
-        l.append(entry)
-    ewrite('\n')
-    return l
-
-def get_password(prompt=None):
-    return getpass.getpass(prompt)
-
-def FilenameCompleter(text, i):
-    paths = glob.glob(text+'*')
-    if not paths: return None
-
-    if i < len(paths):
-        entry = paths[i]
-        if os.path.isdir(entry):
-            return entry+'/'
-        return entry
-    else:
-        return None
-
-def get_filename(prompt, title=None, force_prompt=False, default=''):
-    return get_string(prompt, title=title, force_prompt=force_prompt,
-                      default=default, completer=FilenameCompleter)
-
-def select_multiple(par, options, prompt, title=None, order=None, extras=None):
-    return menu(par, options, prompt, title=title, order=order, extras=extras,
-                multiple=True, empty_ok=False)
-
-def menu(par, options, prompt, default=None, title=None, any_ok=False,
-         order=None, extras=None, multiple=False, empty_ok=False):
-    selected = {}
-
-    if not extras:
-        extras = []
-    else:
-        extras = list(extras)
-
-    if title:
-        ewrite(title+'\n\n')
-
-    ewrite(indent_wrap_text(par, linelen=columns)+'\n')
-
-    if isinstance(options, dict):
-        options = options.copy()
-        # Convert to a list
-        if order:
-            olist = []
-            for key in order:
-                if options.has_key(key):
-                    olist.append( (key, options[key]) )
-                    del options[key]
-
-            # Append anything out of order
-            options = options.items()
-            options.sort()
-            for option in options:
-                olist.append( option )
-            options = olist
-        else:
-            options = options.items()
-            options.sort()
-
-    if multiple:
-        options.append( ('none', '') )
-        default = 'none'
-        extras += ['done']
-
-    allowed = map(lambda x: x[0], options)
-    allowed = allowed + extras
-
-    maxlen_name = min(max(map(len, allowed)), columns/3)
-    digits = int(math.ceil(math.log10(len(options)+1)))
-
-    i = 1
-    for name, desc in options:
-        text = indent_wrap_text(desc, indent=(maxlen_name+digits+3),
-                                starttext=('%*d %-*.*s  ' % (
-            digits, i, maxlen_name, maxlen_name, name)))
-        ewrite(text)
-        if len(options) < 5:
-            ewrite('\n')
-        i = i+1
-    if len(options) >= 5:
-        ewrite('\n')
-
-    if multiple:
-        prompt += '(one at a time) '
-
-    while 1:
-        if default:
-            aprompt = prompt + '[%s] ' % default
-        else:
-            aprompt = prompt
-
-        response = our_raw_input(aprompt, allowed)
-        if not response: response = default
-
-        try:
-            num = int(response)
-            if 1 <= num <= len(options):
-                response = options[num-1][0]
-        except (ValueError, TypeError):
-            pass
-
-        if response in allowed or (response == default and response):
-            if multiple:
-                if response == 'done':
-                    return selected.keys()
-                elif response == 'none':
-                    return []
-                elif selected.get(response):
-                    del selected[response]
-                else:
-                    selected[response]=1
-                ewrite('- selected: %s\n' % ', '.join(selected.keys()))
-                if len(selected):
-                    default = 'done'
-                else:
-                    default = 'none'
-                continue
-            else:
-                return response
-
-        if any_ok and response:
-            return response
-        elif empty_ok and not response:
-            return
-
-        ewrite('Invalid entry.\n')
-    return
-
-# Things that are very UI dependent go here
-def show_report(number, system, mirrors,
-                http_proxy, screen=None, queryonly=False, title='',
-                archived='no'):
-    sysinfo = debianbts.SYSTEMS[system]
-    ewrite('Retrieving report #%d from %s bug tracking system...\n',
-           number, sysinfo['name'])
-
-    try:
-        info = debianbts.get_report(number, system, mirrors=mirrors,
-                                    followups=1,
-                                    http_proxy=http_proxy, archived=archived)
-    except:
-        info = None
-
-    if not info:
-        ewrite('No report available: #%s\n', number)
-        return
-
-    (title, messages) = info
-    current_message = 0
-    skip_pager = False
-
-    while 1:
-        if current_message:
-            text = 'Followup %d - %s\n\n%s' % (current_message, title,
-                                                 messages[current_message])
-        else:
-            text = 'Original report - %s\n\n%s' % (title, messages[0])
-
-        if not skip_pager:
-            fd = os.popen('sensible-pager', 'w')
-            try:
-                fd.write(text)
-                fd.close()
-            except IOError, x:
-                if x.errno == errno.EPIPE:
-                    pass
-                else:
-                    raise
-        skip_pager = False
-
-        options = 'xOrbq'
-
-        if (current_message+1) < len(messages):
-            options = 'N'+options.lower()
-        if (current_message):
-            options = 'p'+options
-
-        x = select_options("What do you want to do now?", options,
-                           {'x' : 'Provide extra information.',
-                            'o' : 'Show other bug reports (return to '
-                            'bug listing).',
-                            'n' : 'Show next message (followup).',
-                            'p' : 'Show previous message (followup).',
-                            'r' : 'Redisplay this message.',
-                            'b' : 'Launch web browser to read '
-                            'full log.',
-                            'q' : "I'm bored; quit please."},
-                           allow_numbers = range(1, len(messages)+1))
-        if x == 'x':
-            return number
-        elif x == 'q':
-            raise NoReport
-        elif x == 'b':
-            launch_browser(debianbts.get_report_url(
-                system, number, mirrors, archived))
-            skip_pager = True
-        elif x == 'o':
-            break
-        elif x == 'n':
-            current_message += 1
-        elif x == 'p':
-            current_message -= 1
-    return
-
-def handle_bts_query(package, bts, mirrors=None, http_proxy="",
-                     queryonly=False, title="", screen=None, archived='no',
-                     source=False, version=None):
-    root = debianbts.SYSTEMS[bts].get('btsroot')
-    if not root:
-        ewrite('%s bug tracking system has no web URL; bypassing query\n',
-               debianbts.SYSTEMS[bts]['name'])
-        return
-
-    srcstr = ""
-    if source:
-        srcstr = " (source)"
-
-    if isinstance(package, basestring):
-        long_message('Querying %s BTS for reports on %s%s...\n',
-                     debianbts.SYSTEMS[bts]['name'], package, srcstr)
-    else:
-        long_message('Querying %s BTS for reports on %s%s...\n',
-                     debianbts.SYSTEMS[bts]['name'],
-                     ' '.join([str(x) for x in package]), srcstr)
-
-    bugs = []
-    try:
-        (count, title, hierarchy)=debianbts.get_reports(
-            package, bts, mirrors=mirrors, version=version,
-            source=source, http_proxy=http_proxy, archived=archived)
-        if debianbts.SYSTEMS[bts].has_key('namefmt'):
-            package2 = debianbts.SYSTEMS[bts]['namefmt'] % package
-            (count2, title2, hierarchy2) = \
-                     debianbts.get_reports(package2, bts,
-                                           mirrors=mirrors, source=source,
-                                           http_proxy=http_proxy,
-                                           version=version)
-            count = count+count2
-            for entry in hierarchy2:
-                hierarchy.append( (package2+' '+entry[0], entry[1]) )
-
-        exp = re.compile(r'#(\d+)[ :]')
-        for entry in hierarchy or []:
-            for bug in entry[1]:
-                match = exp.match(bug)
-                if match:
-                    bugs.append(int(match.group(1)))
-
-        if not count:
-            if hierarchy == None:
-                raise NoPackage
-            else:
-                raise NoBugs
-        elif count == 1:
-            ewrite('%d bug report found:\n\n', count)
-        else:
-            ewrite('%d bug reports found:\n\n', count)
-
-        return browse_bugs(hierarchy, count, bugs, bts, queryonly,
-                           mirrors, http_proxy, screen, title)
-
-    except (IOError, NoNetwork):
-        ewrite('Unable to connect to %s BTS; ', debianbts.SYSTEMS[bts]['name'])
-        res = select_options('continue', 'yN',
-                             {'y': 'Keep going.',
-                              'n': 'Abort.'})
-        if res == 'n':
-            raise NoNetwork
-
-def browse_bugs(hierarchy, count, bugs, bts, queryonly, mirrors,
-                http_proxy, screen, title):
-    endcount = catcount = 0
-    scount = startcount = 1
-    category = hierarchy[0]
-    lastpage = []
-    digits = len(str(len(bugs)))
-    linefmt = '  %'+str(digits)+'d) %s\n'
-    while category:
-        scount = scount + 1
-        catname, reports = category[0:2]
-        while catname.endswith(':'): catname = catname[:-1]
-        total = len(reports)
-
-        while len(reports):
-            these = reports[:rows-2]
-            reports = reports[rows-2:]
-            remain = len(reports)
-
-            tplural = rplural = 's'
-            if total == 1: tplural=''
-            if remain != 1: rplural=''
-
-            if remain:
-                lastpage.append(' %s: %d remain%s\n' %
-                                (catname, remain, rplural))
-            else:
-                lastpage.append(catname+'\n')
-
-            oldscount, oldecount = scount, endcount
-            for report in these:
-                scount = scount + 1
-                endcount = endcount + 1
-                lastpage.append(linefmt % (endcount,report[:columns-digits-5]))
-
-            if category == hierarchy[-1] or \
-               (scount >= (rows - len(hierarchy[catcount+1][1]) - 1)):
-                skipmsg = ' (s to skip rest)'
-                if endcount == count:
-                    skipmsg = ''
-
-                options = 'yNmrqsf'
-                if queryonly: options = 'Nmrqf'
-
-                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
-                pstr = rstr + "Is the bug you found listed above"
-                if queryonly:
-                    pstr = rstr + "What would you like to do next"
-                allowed = bugs+range(1, count+1)
-                helptext = {
-                    'y' : 'Problem already reported; optionally '
-                    'add extra information.',
-                    'n' : 'Problem not listed above; possibly '
-                    'check more.',
-                    'm' : 'Get more information about a bug (you '
-                    'can also enter a number\n'
-                    '     without selecting "m" first).',
-                    'r' : 'Redisplay the last bugs shown.',
-                    'q' : "I'm bored; quit please.",
-                    's' : 'Skip remaining problems; file a new '
-                    'report immediately.',
-                    'f' : 'Filter bug list using a pattern.'}
-                if skipmsg:
-                    helptext['n'] = helptext['n'][:-1]+' (skip to Next page).'
-
-                while 1:
-                    sys.stderr.writelines(lastpage)
-                    x = select_options(pstr, options, helptext,
-                                       allow_numbers=allowed)
-                    if x == 'n':
-                        lastpage = []
-                        break
-                    elif x == 'r':
-                        continue
-                    elif x == 'q':
-                        raise NoReport
-                    elif x == 's':
-                        return
-                    elif x == 'y':
-                        if queryonly:
-                            return
-
-                        if len(bugs) == 1:
-                            number = '1'
-                        else:
-                            number = our_raw_input(
-                                'Enter the number of the bug report '
-                                'you want to give more info on,\n'
-                                'or press ENTER to exit: #', allowed)
-                        while number and number[0] == '#':
-                            number=number[1:]
-                        if number:
-                            try:
-                                number = int(number)
-                                if number not in bugs and 1 <= number <= len(bugs):
-                                    number = bugs[number-1]
-                                return number
-                            except ValueError:
-                                ewrite('Invalid report number: %s\n',
-                                       number)
-                        else:
-                            raise NoReport
-		    elif x == 'f':
-			# Do filter. Recursive done.
-			retval = search_bugs(hierarchy,bts, queryonly, mirrors,
-                                             http_proxy, screen, title)
-			if retval in ["FilterEnd", "Top"]:
-			    continue
-			else:
-			    return retval
-                    else:
-                        if x == 'm' or x == 'i':
-                            if len(bugs) == 1:
-                                number = '1'
-                            else:
-                                number = our_raw_input(
-                                    'Please enter the number of the bug '
-                                    'you would like more info on: #',
-                                    allowed)
-                        else:
-                            number = x
-
-                        while number and number[0] == '#':
-                            number=number[1:]
-
-                        if number:
-                            try:
-                                number = int(number)
-                                if number not in bugs and 1 <= number <= len(bugs):
-                                    number = bugs[number-1]
-                                res = show_report(number, bts, mirrors,
-                                                  http_proxy,
-                                                  queryonly=queryonly,
-                                                  screen=screen,
-                                                  title=title)
-                                if res:
-                                    return res
-                            except ValueError:
-                                ewrite('Invalid report number: %s\n',
-                                       number)
-
-                startcount = endcount+1
-                scount = 0
-
-            # these now empty
-
-        if category == hierarchy[-1]: break
-
-        catcount = catcount+1
-        category = hierarchy[catcount]
-        if scount:
-            lastpage.append('\n')
-            scount = scount + 1
-
-def proc_hierarchy(hierarchy):
-    """Find out bug count and bug # in the hierarchy."""
-
-    lenlist = [len(i[1]) for i in hierarchy]
-    if lenlist:
-	count = reduce(lambda x, y: x+y, lenlist)
-    else:
-	return 0, 0
-
-    # Copy & paste from handle_bts_query()
-    bugs = []
-    exp = re.compile(r'\#(\d+)[ :]')
-    for entry in hierarchy or []:
-	for bug in entry[1]:
-	    match = exp.match(bug)
-	    if match:
-		bugs.append(int(match.group(1)))
-    return count, bugs
-
-def search_bugs(hierarchyfull, bts, queryonly, mirrors,
-                http_proxy, screen, title):
-    """Search for the bug list using a pattern."""
-    """Return string "FilterEnd" when we are done with search."""
-
-    pattern = our_raw_input(
-	'Enter the search pattern (a Perl-compatible regular expression)\n'
-	'or press ENTER to exit: ')
-    if not pattern:
-	return "FilterEnd"
-
-    " Create new hierarchy match the pattern."
-    try:
-        hierarchy = hiermatch.matched_hierarchy(hierarchyfull, pattern)
-    except InvalidRegex:
-	our_raw_input('Invalid regular expression, press ENTER to continue.')
-	return "FilterEnd"
-
-    count, bugs = proc_hierarchy(hierarchy)
-    exp = re.compile(r'\#(\d+):')
-
-    if not count:
-	our_raw_input('No match found, press ENTER to continue.')
-	return "FilterEnd"
-
-    endcount = catcount = 0
-    scount = startcount = 1
-    category = hierarchy[0]
-    lastpage = []
-    digits = len(str(len(bugs)))
-    linefmt = '  %'+str(digits)+'d) %s\n'
-    while category:
-        scount = scount + 1
-        catname, reports = category[0:2]
-        while catname.endswith(':'): catname = catname[:-1]
-        total = len(reports)
-
-        while len(reports):
-            these = reports[:rows-2]
-            reports = reports[rows-2:]
-            remain = len(reports)
-
-            tplural = rplural = 's'
-            if total == 1: tplural=''
-            if remain != 1: rplural=''
-
-            if remain:
-                lastpage.append(' %s: %d report%s (%d remain%s)\n' %
-                                (catname, total, tplural, remain, rplural))
-            else:
-                lastpage.append(' %s: %d report%s\n' %
-                                (catname, total, tplural))
-
-            oldscount, oldecount = scount, endcount
-            for report in these:
-                scount = scount + 1
-                endcount = endcount + 1
-                lastpage.append(linefmt % (endcount,report[:columns-digits-5]))
-
-            if category == hierarchy[-1] or \
-               (scount >= (rows - len(hierarchy[catcount+1][1]) - 1)):
-                skipmsg = ' (s to skip rest)'
-                if endcount == count:
-                    skipmsg = ''
-
-                options = 'yNmrqsfut'
-                if queryonly: options = 'Nmrqfut'
-
-                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
-                pstr = rstr + "Is the bug you found listed above"
-                if queryonly:
-                    pstr = rstr + "What would you like to do next"
-                allowed = bugs+range(1, count+1)
-                helptext = {
-                    'y' : 'Problem already reported; optionally '
-                    'add extra information.',
-                    'n' : 'Problem not listed above; possibly '
-                    'check more.',
-                    'm' : 'Get more information about a bug (you '
-                    'can also enter a number\n'
-                    '     without selecting "m" first).',
-                    'r' : 'Redisplay the last bugs shown.',
-                    'q' : "I'm bored; quit please.",
-                    's' : 'Skip remaining problems; file a new '
-                    'report immediately.',
-		    'f' : 'Filter (search) bug list using a pattern.',
-		    'u' : 'Up one level of filter.',
-		    't' : 'Top of the bug list (remove all filters).'}
-                if skipmsg:
-                    helptext['n'] = helptext['n'][:-1]+' (skip to Next page).'
-
-                while 1:
-                    sys.stderr.writelines(lastpage)
-                    x = select_options(pstr, options, helptext,
-                                       allow_numbers=allowed)
-                    if x == 'n':
-                        lastpage = []
-                        break
-                    elif x == 'r':
-                        continue
-                    elif x == 'q':
-                        raise NoReport
-                    elif x == 's':
-                        return
-                    elif x == 'y':
-                        if queryonly:
-                            return
-
-                        number = our_raw_input(
-                            'Enter the number of the bug report '
-                            'you want to give more info on,\n'
-                            'or press ENTER to exit: #', allowed)
-                        while number and number[0] == '#':
-                            number=number[1:]
-                        if number:
-                            try:
-                                number = int(number)
-                                if number not in bugs and 1 <= number <= len(bugs):
-                                    number = bugs[number-1]
-                                return number
-                            except ValueError:
-                                ewrite('Invalid report number: %s\n',
-                                       number)
-                        else:
-                            raise NoReport
-		    elif x == 'f':
-			# Do filter. Recursive done.
-			retval = search_bugs(hierarchy, bts, queryonly, mirrors,
-			    http_proxy, screen, title)
-			if retval == "FilterEnd":
-			    continue
-			else:
-			    return retval
-		    elif x == 'u':
-			# Up a level
-			return "FilterEnd"
-		    elif x == 't':
-			# go back to the Top level.
-			return "Top"
-                    else:
-                        if x == 'm' or x == 'i':
-                            number = our_raw_input(
-                                'Please enter the number of the bug '
-                                'you would like more info on: #',
-                                allowed)
-                        else:
-                            number = x
-
-                        while number and number[0] == '#':
-                            number=number[1:]
-
-                        if number:
-                            try:
-                                number = int(number)
-                                if number not in bugs and 1 <= number <= len(bugs):
-                                    number = bugs[number-1]
-                                res = show_report(number, bts, mirrors,
-                                                  http_proxy,
-                                                  queryonly=queryonly,
-                                                  screen=screen,
-                                                  title=title)
-                                if res:
-                                    return res
-                            except ValueError:
-                                ewrite('Invalid report number: %s\n',
-                                       number)
-
-                startcount = endcount+1
-                scount = 0
-
-            # these now empty
-
-        if category == hierarchy[-1]: break
-
-        catcount = catcount+1
-        category = hierarchy[catcount]
-        if scount:
-            lastpage.append('\n')
-            scount = scount + 1
-    return "FilterEnd"
-
-def display_report(text, use_pager=True):
-    if not use_pager:
-        ewrite(text)
-        return
-
-    pager = os.environ.get('PAGER', 'sensible-pager')
-    try:
-        os.popen(pager, 'w').write(text)
-    except IOError:
-        pass
-
-def spawn_editor(message, filename, editor, charset='utf-8'):
-    if not editor:
-        ewrite('No editor found!\n')
-        return (message, 0)
-
-    edname = os.path.basename(editor.split()[0])
-
-    # Move the cursor for lazy buggers like me; add your editor here...
-    ourline = 0
-    for (lineno, line) in enumerate(file(filename)):
-        if line == '\n' and not ourline:
-            ourline = lineno + 2
-        elif line.strip() == utils.NEWBIELINE:
-            ourline = lineno + 2
-
-    opts = ''
-    if 'vim' in edname:
-        # Force *vim to edit the file in the foreground, instead of forking
-        opts = '-f '
-
-    if ourline:
-        if edname in ('vi', 'nvi', 'vim', 'elvis', 'gvim', 'kvim'):
-            opts = '-c :%d' % ourline
-        elif (edname in ('elvis-tiny', 'gnuclient', 'ee', 'pico', 'nano', 'zile') or
-              'emacs' in edname):
-            opts = '+%d' % ourline
-        elif edname in ('jed', 'xjed'):
-            opts = '-g %d' % ourline
-        elif edname == 'kate':
-            opts = '--line %d' % ourline
-
-    if '&' in editor or edname == 'kate':
-        ewrite("Spawning %s in background; please press Enter when done "
-               "editing.\n", edname)
-    else:
-        ewrite("Spawning %s...\n", edname)
-
-    result = os.system("%s %s '%s'" % (editor, opts, filename))
-
-    if result:
-        ewrite('Warning: possible error exit from %s: %d\n', edname, result)
-
-    if not os.path.exists(filename):
-        ewrite('Bug report file %s removed!', filename)
-        sys.exit(1)
-
-    if '&' in editor: return (None, 1)
-
-    newmessage = file(filename).read().decode(charset, 'replace')
-
-    if newmessage == message:
-        ewrite('No changes were made in the editor.\n')
-
-    return (newmessage, newmessage != message)

Copied: trunk/reportbug/ui/text_ui.py (from rev 640, trunk/reportbug/ui/text.py)
===================================================================
--- trunk/reportbug/ui/text_ui.py	                        (rev 0)
+++ trunk/reportbug/ui/text_ui.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -0,0 +1,938 @@
+# Text user interface for reportbug
+#   Written by Chris Lawrence <lawrencc at debian.org>
+#   (C) 2001-08 Chris Lawrence
+#
+# This program is freely distributable per the following license:
+#
+##  Permission to use, copy, modify, and distribute this software and its
+##  documentation for any purpose and without fee is hereby granted,
+##  provided that the above copyright notice appears in all copies and that
+##  both that copyright notice and this permission notice appear in
+##  supporting documentation.
+##
+##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
+##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+##  SOFTWARE.
+#
+# $Id: reportbug_ui_text.py,v 1.19.2.9 2008-04-18 05:38:28 lawrencc Exp $
+
+import sys
+import os
+import commands
+import re
+import math
+import string
+import errno
+import glob
+import getpass
+import textwrap
+try:
+    import readline
+except ImportError:
+    readline = None
+
+from reportbug import debianbts, hiermatch
+from reportbug.exceptions import (
+    NoReport, NoPackage, NoBugs, NoNetwork,
+    InvalidRegex,
+    )
+from reportbug.urlutils import launch_browser
+
+ISATTY = sys.stdin.isatty()
+charset = 'us-ascii'
+
+try:
+    r, c = commands.getoutput('stty size').split()
+    rows, columns = int(r) or 24, int(c) or 79
+except:
+    rows, columns = 24, 79
+
+def ewrite(message, *args):
+    if not ISATTY:
+        return
+
+    if args:
+        message = message % args
+
+    if isinstance(message, unicode):
+        message = message.encode(charset, 'replace')
+
+    sys.stderr.write(message)
+    sys.stderr.flush()
+
+log_message = ewrite
+display_failure = ewrite
+
+def indent_wrap_text(text, starttext='', indent=0, linelen=None):
+    """Wrapper for textwrap.fill to the existing API."""
+    if not linelen:
+        linelen = columns-1
+
+    if indent:
+        si = ' '*indent
+    else:
+        si = ''
+
+    text = ' '.join(text.split())
+    if not text:
+        return starttext+'\n'
+
+    output = textwrap.fill(text, width=linelen, initial_indent=starttext,
+                           subsequent_indent=si)
+    if output.endswith('\n'):
+        return output
+    return output + '\n'
+
+# Readline support, if available
+if readline is not None:
+    readline.parse_and_bind("tab: complete")
+    try:
+        # minimize the word delimeter list if possible
+        readline.set_completer_delims(' ')
+    except:
+        pass
+
+class our_completer(object):
+    def __init__(self, completions=None):
+        self.completions = None
+        if completions:
+            self.completions = tuple(map(str, completions))
+
+    def complete(self, text, i):
+        if not self.completions: return None
+
+        matching = [x for x in self.completions if x.startswith(text)]
+        if i < len(matching):
+            return matching[i]
+        else:
+            return None
+
+def our_raw_input(prompt = None, completions=None, completer=None):
+    istty = sys.stdout.isatty()
+    if not istty:
+        sys.stderr.write(prompt)
+    sys.stderr.flush()
+    if readline:
+        if completions and not completer:
+            completer = our_completer(completions).complete
+        if completer:
+            readline.set_completer(completer)
+
+    try:
+        if istty:
+            ret = raw_input(prompt)
+        else:
+            ret = raw_input()
+    except EOFError:
+        ewrite('\nUser interrupt (^D).\n')
+        raise SystemExit
+
+    if readline:
+        readline.set_completer(None)
+    return ret.strip()
+
+def select_options(msg, ok, help, allow_numbers=None, nowrap=False):
+    err_message = ''
+    for option in ok:
+        if option in string.ascii_uppercase:
+            default=option
+            break
+
+    if not help: help = {}
+
+    if '?' not in ok: ok = ok+'?'
+
+    if nowrap:
+        longmsg = msg+' ['+'|'.join(ok)+']?'+' '
+    else:
+        longmsg = indent_wrap_text(msg+' ['+'|'.join(ok)+']?').strip()+' '
+    ch = our_raw_input(longmsg, allow_numbers)
+    # Allow entry of a bug number here
+    if allow_numbers:
+        while ch and ch[0] == '#': ch = ch[1:]
+        if type(allow_numbers) == type(1):
+            try:
+                return str(int(ch))
+            except ValueError:
+                pass
+        else:
+            try:
+                number = int(ch)
+                if number in allow_numbers:
+                    return str(number)
+                else:
+                    nums = list(allow_numbers)
+                    nums.sort()
+                    err_message = 'Only the following entries are allowed: '+\
+                                  ', '.join(map(str, nums))
+            except (ValueError, TypeError):
+                pass
+
+    if not ch: ch = default
+    ch = ch[0]
+    if ch=='?':
+        help['?'] = 'Display this help.'
+        for ch in ok:
+            if ch in string.ascii_uppercase:
+                desc = '(default) '
+            else:
+                desc = ''
+            desc += help.get(ch, help.get(ch.lower(),
+                                          'No help for this option.'))
+            ewrite(indent_wrap_text(desc+'\n', '%s - '% ch, 4))
+        return select_options(msg, ok, help, allow_numbers)
+    elif (ch.lower() in ok) or (ch.upper() in ok):
+        return ch.lower()
+    elif err_message:
+        ewrite(indent_wrap_text(err_message))
+    else:
+        ewrite('Invalid selection.\n')
+
+    return select_options(msg, ok, help, allow_numbers, nowrap)
+
+def yes_no(msg, yeshelp, nohelp, default=True, nowrap=False):
+    "Return True for yes, False for no."
+    if default:
+        ok = 'Ynq'
+    else:
+        ok = 'yNq'
+
+    res = select_options(msg, ok, {'y': yeshelp, 'n': nohelp, 'q' : 'Quit.'},
+                         nowrap=nowrap)
+    if res == 'q':
+        raise SystemExit
+    return (res == 'y')
+
+def long_message(text, *args):
+    if args:
+        ewrite(indent_wrap_text(text % args))
+    else:
+        ewrite(indent_wrap_text(text))
+
+final_message = long_message
+
+def get_string(prompt, options=None, title=None, force_prompt=False,
+               default='', completer=None):
+    if prompt and (len(prompt) < 2*columns/3) and not force_prompt:
+        if default:
+            prompt = '%s [%s]: ' % (prompt, default)
+            response = our_raw_input(prompt, options, completer) or default
+        else:
+            response = our_raw_input(prompt, options, completer)
+    else:
+        if prompt:
+            ewrite(indent_wrap_text(prompt))
+        if default:
+            response = our_raw_input('[%s]> ' % default, options, completer) or default
+        else:
+            response = our_raw_input('> ', options, completer)
+
+    # Translate the response into a Unicode string
+    if response is not None:
+        response = unicode(response, charset, 'replace')
+
+    return response
+
+def get_multiline(prompt):
+    ewrite('\n')
+    ewrite(indent_wrap_text(prompt + "  Press ENTER on a blank line to continue.\n"))
+    l = []
+    while 1:
+        entry = get_string('', force_prompt=True).strip()
+        if not entry:
+            break
+        l.append(entry)
+    ewrite('\n')
+    return l
+
+def get_password(prompt=None):
+    return getpass.getpass(prompt)
+
+def FilenameCompleter(text, i):
+    paths = glob.glob(text+'*')
+    if not paths: return None
+
+    if i < len(paths):
+        entry = paths[i]
+        if os.path.isdir(entry):
+            return entry+'/'
+        return entry
+    else:
+        return None
+
+def get_filename(prompt, title=None, force_prompt=False, default=''):
+    return get_string(prompt, title=title, force_prompt=force_prompt,
+                      default=default, completer=FilenameCompleter)
+
+def select_multiple(par, options, prompt, title=None, order=None, extras=None):
+    return menu(par, options, prompt, title=title, order=order, extras=extras,
+                multiple=True, empty_ok=False)
+
+def menu(par, options, prompt, default=None, title=None, any_ok=False,
+         order=None, extras=None, multiple=False, empty_ok=False):
+    selected = {}
+
+    if not extras:
+        extras = []
+    else:
+        extras = list(extras)
+
+    if title:
+        ewrite(title+'\n\n')
+
+    ewrite(indent_wrap_text(par, linelen=columns)+'\n')
+
+    if isinstance(options, dict):
+        options = options.copy()
+        # Convert to a list
+        if order:
+            olist = []
+            for key in order:
+                if options.has_key(key):
+                    olist.append( (key, options[key]) )
+                    del options[key]
+
+            # Append anything out of order
+            options = options.items()
+            options.sort()
+            for option in options:
+                olist.append( option )
+            options = olist
+        else:
+            options = options.items()
+            options.sort()
+
+    if multiple:
+        options.append( ('none', '') )
+        default = 'none'
+        extras += ['done']
+
+    allowed = map(lambda x: x[0], options)
+    allowed = allowed + extras
+
+    maxlen_name = min(max(map(len, allowed)), columns/3)
+    digits = int(math.ceil(math.log10(len(options)+1)))
+
+    i = 1
+    for name, desc in options:
+        text = indent_wrap_text(desc, indent=(maxlen_name+digits+3),
+                                starttext=('%*d %-*.*s  ' % (
+            digits, i, maxlen_name, maxlen_name, name)))
+        ewrite(text)
+        if len(options) < 5:
+            ewrite('\n')
+        i = i+1
+    if len(options) >= 5:
+        ewrite('\n')
+
+    if multiple:
+        prompt += '(one at a time) '
+
+    while 1:
+        if default:
+            aprompt = prompt + '[%s] ' % default
+        else:
+            aprompt = prompt
+
+        response = our_raw_input(aprompt, allowed)
+        if not response: response = default
+
+        try:
+            num = int(response)
+            if 1 <= num <= len(options):
+                response = options[num-1][0]
+        except (ValueError, TypeError):
+            pass
+
+        if response in allowed or (response == default and response):
+            if multiple:
+                if response == 'done':
+                    return selected.keys()
+                elif response == 'none':
+                    return []
+                elif selected.get(response):
+                    del selected[response]
+                else:
+                    selected[response]=1
+                ewrite('- selected: %s\n' % ', '.join(selected.keys()))
+                if len(selected):
+                    default = 'done'
+                else:
+                    default = 'none'
+                continue
+            else:
+                return response
+
+        if any_ok and response:
+            return response
+        elif empty_ok and not response:
+            return
+
+        ewrite('Invalid entry.\n')
+    return
+
+# Things that are very UI dependent go here
+def show_report(number, system, mirrors,
+                http_proxy, screen=None, queryonly=False, title='',
+                archived='no'):
+    sysinfo = debianbts.SYSTEMS[system]
+    ewrite('Retrieving report #%d from %s bug tracking system...\n',
+           number, sysinfo['name'])
+
+    try:
+        info = debianbts.get_report(number, system, mirrors=mirrors,
+                                    followups=1,
+                                    http_proxy=http_proxy, archived=archived)
+    except:
+        info = None
+
+    if not info:
+        ewrite('No report available: #%s\n', number)
+        return
+
+    (title, messages) = info
+    current_message = 0
+    skip_pager = False
+
+    while 1:
+        if current_message:
+            text = 'Followup %d - %s\n\n%s' % (current_message, title,
+                                                 messages[current_message])
+        else:
+            text = 'Original report - %s\n\n%s' % (title, messages[0])
+
+        if not skip_pager:
+            fd = os.popen('sensible-pager', 'w')
+            try:
+                fd.write(text)
+                fd.close()
+            except IOError, x:
+                if x.errno == errno.EPIPE:
+                    pass
+                else:
+                    raise
+        skip_pager = False
+
+        options = 'xOrbq'
+
+        if (current_message+1) < len(messages):
+            options = 'N'+options.lower()
+        if (current_message):
+            options = 'p'+options
+
+        x = select_options("What do you want to do now?", options,
+                           {'x' : 'Provide extra information.',
+                            'o' : 'Show other bug reports (return to '
+                            'bug listing).',
+                            'n' : 'Show next message (followup).',
+                            'p' : 'Show previous message (followup).',
+                            'r' : 'Redisplay this message.',
+                            'b' : 'Launch web browser to read '
+                            'full log.',
+                            'q' : "I'm bored; quit please."},
+                           allow_numbers = range(1, len(messages)+1))
+        if x == 'x':
+            return number
+        elif x == 'q':
+            raise NoReport
+        elif x == 'b':
+            launch_browser(debianbts.get_report_url(
+                system, number, mirrors, archived))
+            skip_pager = True
+        elif x == 'o':
+            break
+        elif x == 'n':
+            current_message += 1
+        elif x == 'p':
+            current_message -= 1
+    return
+
+def handle_bts_query(package, bts, mirrors=None, http_proxy="",
+                     queryonly=False, title="", screen=None, archived='no',
+                     source=False, version=None):
+    root = debianbts.SYSTEMS[bts].get('btsroot')
+    if not root:
+        ewrite('%s bug tracking system has no web URL; bypassing query\n',
+               debianbts.SYSTEMS[bts]['name'])
+        return
+
+    srcstr = ""
+    if source:
+        srcstr = " (source)"
+
+    if isinstance(package, basestring):
+        long_message('Querying %s BTS for reports on %s%s...\n',
+                     debianbts.SYSTEMS[bts]['name'], package, srcstr)
+    else:
+        long_message('Querying %s BTS for reports on %s%s...\n',
+                     debianbts.SYSTEMS[bts]['name'],
+                     ' '.join([str(x) for x in package]), srcstr)
+
+    bugs = []
+    try:
+        (count, title, hierarchy)=debianbts.get_reports(
+            package, bts, mirrors=mirrors, version=version,
+            source=source, http_proxy=http_proxy, archived=archived)
+        if debianbts.SYSTEMS[bts].has_key('namefmt'):
+            package2 = debianbts.SYSTEMS[bts]['namefmt'] % package
+            (count2, title2, hierarchy2) = \
+                     debianbts.get_reports(package2, bts,
+                                           mirrors=mirrors, source=source,
+                                           http_proxy=http_proxy,
+                                           version=version)
+            count = count+count2
+            for entry in hierarchy2:
+                hierarchy.append( (package2+' '+entry[0], entry[1]) )
+
+        exp = re.compile(r'#(\d+)[ :]')
+        for entry in hierarchy or []:
+            for bug in entry[1]:
+                match = exp.match(bug)
+                if match:
+                    bugs.append(int(match.group(1)))
+
+        if not count:
+            if hierarchy == None:
+                raise NoPackage
+            else:
+                raise NoBugs
+        elif count == 1:
+            ewrite('%d bug report found:\n\n', count)
+        else:
+            ewrite('%d bug reports found:\n\n', count)
+
+        return browse_bugs(hierarchy, count, bugs, bts, queryonly,
+                           mirrors, http_proxy, screen, title)
+
+    except (IOError, NoNetwork):
+        ewrite('Unable to connect to %s BTS; ', debianbts.SYSTEMS[bts]['name'])
+        res = select_options('continue', 'yN',
+                             {'y': 'Keep going.',
+                              'n': 'Abort.'})
+        if res == 'n':
+            raise NoNetwork
+
+def browse_bugs(hierarchy, count, bugs, bts, queryonly, mirrors,
+                http_proxy, screen, title):
+    endcount = catcount = 0
+    scount = startcount = 1
+    category = hierarchy[0]
+    lastpage = []
+    digits = len(str(len(bugs)))
+    linefmt = '  %'+str(digits)+'d) %s\n'
+    while category:
+        scount = scount + 1
+        catname, reports = category[0:2]
+        while catname.endswith(':'): catname = catname[:-1]
+        total = len(reports)
+
+        while len(reports):
+            these = reports[:rows-2]
+            reports = reports[rows-2:]
+            remain = len(reports)
+
+            tplural = rplural = 's'
+            if total == 1: tplural=''
+            if remain != 1: rplural=''
+
+            if remain:
+                lastpage.append(' %s: %d remain%s\n' %
+                                (catname, remain, rplural))
+            else:
+                lastpage.append(catname+'\n')
+
+            oldscount, oldecount = scount, endcount
+            for report in these:
+                scount = scount + 1
+                endcount = endcount + 1
+                lastpage.append(linefmt % (endcount,report[:columns-digits-5]))
+
+            if category == hierarchy[-1] or \
+               (scount >= (rows - len(hierarchy[catcount+1][1]) - 1)):
+                skipmsg = ' (s to skip rest)'
+                if endcount == count:
+                    skipmsg = ''
+
+                options = 'yNmrqsf'
+                if queryonly: options = 'Nmrqf'
+
+                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
+                pstr = rstr + "Is the bug you found listed above"
+                if queryonly:
+                    pstr = rstr + "What would you like to do next"
+                allowed = bugs+range(1, count+1)
+                helptext = {
+                    'y' : 'Problem already reported; optionally '
+                    'add extra information.',
+                    'n' : 'Problem not listed above; possibly '
+                    'check more.',
+                    'm' : 'Get more information about a bug (you '
+                    'can also enter a number\n'
+                    '     without selecting "m" first).',
+                    'r' : 'Redisplay the last bugs shown.',
+                    'q' : "I'm bored; quit please.",
+                    's' : 'Skip remaining problems; file a new '
+                    'report immediately.',
+                    'f' : 'Filter bug list using a pattern.'}
+                if skipmsg:
+                    helptext['n'] = helptext['n'][:-1]+' (skip to Next page).'
+
+                while 1:
+                    sys.stderr.writelines(lastpage)
+                    x = select_options(pstr, options, helptext,
+                                       allow_numbers=allowed)
+                    if x == 'n':
+                        lastpage = []
+                        break
+                    elif x == 'r':
+                        continue
+                    elif x == 'q':
+                        raise NoReport
+                    elif x == 's':
+                        return
+                    elif x == 'y':
+                        if queryonly:
+                            return
+
+                        if len(bugs) == 1:
+                            number = '1'
+                        else:
+                            number = our_raw_input(
+                                'Enter the number of the bug report '
+                                'you want to give more info on,\n'
+                                'or press ENTER to exit: #', allowed)
+                        while number and number[0] == '#':
+                            number=number[1:]
+                        if number:
+                            try:
+                                number = int(number)
+                                if number not in bugs and 1 <= number <= len(bugs):
+                                    number = bugs[number-1]
+                                return number
+                            except ValueError:
+                                ewrite('Invalid report number: %s\n',
+                                       number)
+                        else:
+                            raise NoReport
+		    elif x == 'f':
+			# Do filter. Recursive done.
+			retval = search_bugs(hierarchy,bts, queryonly, mirrors,
+                                             http_proxy, screen, title)
+			if retval in ["FilterEnd", "Top"]:
+			    continue
+			else:
+			    return retval
+                    else:
+                        if x == 'm' or x == 'i':
+                            if len(bugs) == 1:
+                                number = '1'
+                            else:
+                                number = our_raw_input(
+                                    'Please enter the number of the bug '
+                                    'you would like more info on: #',
+                                    allowed)
+                        else:
+                            number = x
+
+                        while number and number[0] == '#':
+                            number=number[1:]
+
+                        if number:
+                            try:
+                                number = int(number)
+                                if number not in bugs and 1 <= number <= len(bugs):
+                                    number = bugs[number-1]
+                                res = show_report(number, bts, mirrors,
+                                                  http_proxy,
+                                                  queryonly=queryonly,
+                                                  screen=screen,
+                                                  title=title)
+                                if res:
+                                    return res
+                            except ValueError:
+                                ewrite('Invalid report number: %s\n',
+                                       number)
+
+                startcount = endcount+1
+                scount = 0
+
+            # these now empty
+
+        if category == hierarchy[-1]: break
+
+        catcount = catcount+1
+        category = hierarchy[catcount]
+        if scount:
+            lastpage.append('\n')
+            scount = scount + 1
+
+def proc_hierarchy(hierarchy):
+    """Find out bug count and bug # in the hierarchy."""
+
+    lenlist = [len(i[1]) for i in hierarchy]
+    if lenlist:
+	count = reduce(lambda x, y: x+y, lenlist)
+    else:
+	return 0, 0
+
+    # Copy & paste from handle_bts_query()
+    bugs = []
+    exp = re.compile(r'\#(\d+)[ :]')
+    for entry in hierarchy or []:
+	for bug in entry[1]:
+	    match = exp.match(bug)
+	    if match:
+		bugs.append(int(match.group(1)))
+    return count, bugs
+
+def search_bugs(hierarchyfull, bts, queryonly, mirrors,
+                http_proxy, screen, title):
+    """Search for the bug list using a pattern."""
+    """Return string "FilterEnd" when we are done with search."""
+
+    pattern = our_raw_input(
+	'Enter the search pattern (a Perl-compatible regular expression)\n'
+	'or press ENTER to exit: ')
+    if not pattern:
+	return "FilterEnd"
+
+    " Create new hierarchy match the pattern."
+    try:
+        hierarchy = hiermatch.matched_hierarchy(hierarchyfull, pattern)
+    except InvalidRegex:
+	our_raw_input('Invalid regular expression, press ENTER to continue.')
+	return "FilterEnd"
+
+    count, bugs = proc_hierarchy(hierarchy)
+    exp = re.compile(r'\#(\d+):')
+
+    if not count:
+	our_raw_input('No match found, press ENTER to continue.')
+	return "FilterEnd"
+
+    endcount = catcount = 0
+    scount = startcount = 1
+    category = hierarchy[0]
+    lastpage = []
+    digits = len(str(len(bugs)))
+    linefmt = '  %'+str(digits)+'d) %s\n'
+    while category:
+        scount = scount + 1
+        catname, reports = category[0:2]
+        while catname.endswith(':'): catname = catname[:-1]
+        total = len(reports)
+
+        while len(reports):
+            these = reports[:rows-2]
+            reports = reports[rows-2:]
+            remain = len(reports)
+
+            tplural = rplural = 's'
+            if total == 1: tplural=''
+            if remain != 1: rplural=''
+
+            if remain:
+                lastpage.append(' %s: %d report%s (%d remain%s)\n' %
+                                (catname, total, tplural, remain, rplural))
+            else:
+                lastpage.append(' %s: %d report%s\n' %
+                                (catname, total, tplural))
+
+            oldscount, oldecount = scount, endcount
+            for report in these:
+                scount = scount + 1
+                endcount = endcount + 1
+                lastpage.append(linefmt % (endcount,report[:columns-digits-5]))
+
+            if category == hierarchy[-1] or \
+               (scount >= (rows - len(hierarchy[catcount+1][1]) - 1)):
+                skipmsg = ' (s to skip rest)'
+                if endcount == count:
+                    skipmsg = ''
+
+                options = 'yNmrqsfut'
+                if queryonly: options = 'Nmrqfut'
+
+                rstr = "(%d-%d/%d) " % (startcount, endcount, count)
+                pstr = rstr + "Is the bug you found listed above"
+                if queryonly:
+                    pstr = rstr + "What would you like to do next"
+                allowed = bugs+range(1, count+1)
+                helptext = {
+                    'y' : 'Problem already reported; optionally '
+                    'add extra information.',
+                    'n' : 'Problem not listed above; possibly '
+                    'check more.',
+                    'm' : 'Get more information about a bug (you '
+                    'can also enter a number\n'
+                    '     without selecting "m" first).',
+                    'r' : 'Redisplay the last bugs shown.',
+                    'q' : "I'm bored; quit please.",
+                    's' : 'Skip remaining problems; file a new '
+                    'report immediately.',
+		    'f' : 'Filter (search) bug list using a pattern.',
+		    'u' : 'Up one level of filter.',
+		    't' : 'Top of the bug list (remove all filters).'}
+                if skipmsg:
+                    helptext['n'] = helptext['n'][:-1]+' (skip to Next page).'
+
+                while 1:
+                    sys.stderr.writelines(lastpage)
+                    x = select_options(pstr, options, helptext,
+                                       allow_numbers=allowed)
+                    if x == 'n':
+                        lastpage = []
+                        break
+                    elif x == 'r':
+                        continue
+                    elif x == 'q':
+                        raise NoReport
+                    elif x == 's':
+                        return
+                    elif x == 'y':
+                        if queryonly:
+                            return
+
+                        number = our_raw_input(
+                            'Enter the number of the bug report '
+                            'you want to give more info on,\n'
+                            'or press ENTER to exit: #', allowed)
+                        while number and number[0] == '#':
+                            number=number[1:]
+                        if number:
+                            try:
+                                number = int(number)
+                                if number not in bugs and 1 <= number <= len(bugs):
+                                    number = bugs[number-1]
+                                return number
+                            except ValueError:
+                                ewrite('Invalid report number: %s\n',
+                                       number)
+                        else:
+                            raise NoReport
+		    elif x == 'f':
+			# Do filter. Recursive done.
+			retval = search_bugs(hierarchy, bts, queryonly, mirrors,
+			    http_proxy, screen, title)
+			if retval == "FilterEnd":
+			    continue
+			else:
+			    return retval
+		    elif x == 'u':
+			# Up a level
+			return "FilterEnd"
+		    elif x == 't':
+			# go back to the Top level.
+			return "Top"
+                    else:
+                        if x == 'm' or x == 'i':
+                            number = our_raw_input(
+                                'Please enter the number of the bug '
+                                'you would like more info on: #',
+                                allowed)
+                        else:
+                            number = x
+
+                        while number and number[0] == '#':
+                            number=number[1:]
+
+                        if number:
+                            try:
+                                number = int(number)
+                                if number not in bugs and 1 <= number <= len(bugs):
+                                    number = bugs[number-1]
+                                res = show_report(number, bts, mirrors,
+                                                  http_proxy,
+                                                  queryonly=queryonly,
+                                                  screen=screen,
+                                                  title=title)
+                                if res:
+                                    return res
+                            except ValueError:
+                                ewrite('Invalid report number: %s\n',
+                                       number)
+
+                startcount = endcount+1
+                scount = 0
+
+            # these now empty
+
+        if category == hierarchy[-1]: break
+
+        catcount = catcount+1
+        category = hierarchy[catcount]
+        if scount:
+            lastpage.append('\n')
+            scount = scount + 1
+    return "FilterEnd"
+
+def display_report(text, use_pager=True):
+    if not use_pager:
+        ewrite(text)
+        return
+
+    pager = os.environ.get('PAGER', 'sensible-pager')
+    try:
+        os.popen(pager, 'w').write(text)
+    except IOError:
+        pass
+
+def spawn_editor(message, filename, editor, charset='utf-8'):
+    if not editor:
+        ewrite('No editor found!\n')
+        return (message, 0)
+
+    edname = os.path.basename(editor.split()[0])
+
+    # Move the cursor for lazy buggers like me; add your editor here...
+    ourline = 0
+    for (lineno, line) in enumerate(file(filename)):
+        if line == '\n' and not ourline:
+            ourline = lineno + 2
+        # Morph @ 2008-08-31
+        #elif line.strip() == utils.NEWBIELINE:
+        #    ourline = lineno + 2
+
+    opts = ''
+    if 'vim' in edname:
+        # Force *vim to edit the file in the foreground, instead of forking
+        opts = '-f '
+
+    if ourline:
+        if edname in ('vi', 'nvi', 'vim', 'elvis', 'gvim', 'kvim'):
+            opts = '-c :%d' % ourline
+        elif (edname in ('elvis-tiny', 'gnuclient', 'ee', 'pico', 'nano', 'zile') or
+              'emacs' in edname):
+            opts = '+%d' % ourline
+        elif edname in ('jed', 'xjed'):
+            opts = '-g %d' % ourline
+        elif edname == 'kate':
+            opts = '--line %d' % ourline
+
+    if '&' in editor or edname == 'kate':
+        ewrite("Spawning %s in background; please press Enter when done "
+               "editing.\n", edname)
+    else:
+        ewrite("Spawning %s...\n", edname)
+
+    result = os.system("%s %s '%s'" % (editor, opts, filename))
+
+    if result:
+        ewrite('Warning: possible error exit from %s: %d\n', edname, result)
+
+    if not os.path.exists(filename):
+        ewrite('Bug report file %s removed!', filename)
+        sys.exit(1)
+
+    if '&' in editor: return (None, 1)
+
+    newmessage = file(filename).read().decode(charset, 'replace')
+
+    if newmessage == message:
+        ewrite('No changes were made in the editor.\n')
+
+    return (newmessage, newmessage != message)

Deleted: trunk/reportbug/ui/urwid.py
===================================================================
--- trunk/reportbug/ui/urwid.py	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/reportbug/ui/urwid.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -1,685 +0,0 @@
-# urwid user interface for reportbug
-#   Written by Chris Lawrence <lawrencc at debian.org>
-#   (C) 2006-08 Chris Lawrence
-#
-# This program is freely distributable per the following license:
-#
-##  Permission to use, copy, modify, and distribute this software and its
-##  documentation for any purpose and without fee is hereby granted,
-##  provided that the above copyright notice appears in all copies and that
-##  both that copyright notice and this permission notice appear in
-##  supporting documentation.
-##
-##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
-##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
-##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
-##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
-##  SOFTWARE.
-#
-# Portions of this file are licensed under the Lesser GNU Public License
-# (LGPL) Version 2.1 or later.  On Debian systems, this license is available
-# in /usr/share/common-licenses/LGPL
-#
-# $Id: reportbug_ui_urwid.py,v 1.3.2.19 2007-04-17 20:02:41 lawrencc Exp $
-
-import sys
-import re
-import getpass
-from reportbug.exceptions import (
-    UINotImportable,
-    NoPackage, NoBugs, NoNetwork, NoReport,
-    )
-from reportbug.urlutils import launch_browser
-from text import (
-    ewrite,
-    spawn_editor,
-    )
-from reportbug import VERSION
-
-try:
-    import urwid.raw_display
-    import urwid
-except ImportError:
-    raise UINotImportable, 'Please install the python-urwid package to use this interface.'
-
-ISATTY = sys.stdin.isatty()
-
-log_message = ewrite
-display_failure = ewrite
-
-# Start a urwid session
-def initialize_urwid_ui():
-    ui = urwid.raw_display.Screen()
-    ui.register_palette(palette)
-    # Improve responsiveness of UI
-    ui.set_input_timeouts(max_wait=0.1)
-    return ui
-
-# Empty function to satisfy ui.run_wrapper()
-def nullfunc():
-    pass
-
-# Widgets ripped mercilessly from urwid examples (dialog.py)
-class buttonpush(Exception):
-    pass
-
-def button_press(button):
-    raise buttonpush, button.exitcode
-
-class SelectableText(urwid.Edit):
-    def valid_char(self, ch):
-        return False
-
-class dialog(object):
-    def __init__(self, message, body=None, width=None, height=None,
-                 title='', long_message=''):
-        self.body = body
-
-        self.scrollmode=False
-        if not body:
-            if long_message:
-                box = SelectableText(edit_text=long_message)
-                box.set_edit_pos(0)
-                self.body = body = urwid.ListBox([box])
-                self.scrollmode=True
-            else:
-                self.body = body = urwid.Filler(urwid.Divider(), 'top')
-
-        if not width:
-            width = ('relative', 80)
-
-        if not height:
-            height = ('relative', 80)
-
-        self.frame = urwid.Frame(body, focus_part='footer')
-        if message:
-            self.frame.header = urwid.Pile([urwid.Text(message),
-                                            urwid.Divider()])
-
-        w = self.frame
-        # pad area around listbox
-        w = urwid.Padding(w, ('fixed left',2), ('fixed right',2))
-        w = urwid.Filler(w, ('fixed top',1), ('fixed bottom',1))
-        w = urwid.AttrWrap(w, 'body')
-
-        if title:
-            w = urwid.Frame(w)
-            w.header = urwid.Text( ('title', title) )
-
-        # "shadow" effect
-        w = urwid.Columns( [w, ('fixed', 1, urwid.AttrWrap( urwid.Filler(urwid.Text(('border',' ')), "top") ,'shadow'))])
-        w = urwid.Frame( w, footer = urwid.AttrWrap(urwid.Text(('border',' ')),'shadow'))
-        # outermost border area
-        w = urwid.Padding(w, 'center', width )
-        w = urwid.Filler(w, 'middle', height )
-        w = urwid.AttrWrap( w, 'border' )
-        self.view = w
-
-    def add_buttons(self, buttons, default=0, vertical=False):
-        l = []
-        for name, exitcode in buttons:
-            if exitcode == '---':
-                # Separator is just a text label
-                b = urwid.Text(name)
-                b = urwid.AttrWrap( b, 'scrolllabel' )
-            else:
-                b = urwid.Button( name, self.button_press )
-                b.exitcode = exitcode
-                b = urwid.AttrWrap( b, 'selectable','focus' )
-            l.append( b )
-
-        if vertical:
-            box = urwid.ListBox(l)
-            box.set_focus(default or 0)
-            self.buttons = urwid.Frame(urwid.AttrWrap(box, 'selectable'))
-            self.frame.footer = urwid.BoxAdapter(self.buttons, min(len(l), 10))
-        else:
-            self.buttons = urwid.GridFlow(l, 12, 3, 1, 'center')
-            self.buttons.set_focus(default or 0)
-            self.frame.footer = urwid.Pile( [ urwid.Divider(), self.buttons ],
-                                            focus_item = 1 )
-
-    def button_press(self, button):
-        raise buttonpush, button.exitcode
-
-    def run(self):
-        #self.ui.set_mouse_tracking()
-        size = self.ui.get_cols_rows()
-        try:
-            while True:
-                canvas = self.view.render( size, focus=True )
-                self.ui.draw_screen( size, canvas )
-                keys = None
-                while not keys:
-                    keys = self.ui.get_input()
-                for k in keys:
-                    if urwid.is_mouse_event(k):
-                        event, button, col, row = k
-                        self.view.mouse_event( size,
-                                               event, button, col, row,
-                                               focus=True)
-                    if k == 'window resize':
-                        size = self.ui.get_cols_rows()
-                    k = self.view.keypress( size, k )
-                    if k:
-                        self.unhandled_key( size, k)
-        except buttonpush, e:
-            return self.on_exit( e.args[0] )
-
-    def on_exit(self, exitcode):
-        return exitcode
-
-    def unhandled_key(self, size, k):
-        if k in ('tab', 'shift tab'):
-            focus = self.frame.focus_part
-            if focus == 'footer':
-                self.frame.set_focus('body')
-            else:
-                self.frame.set_focus('footer')
-
-        if k in ('up','page up', 'down', 'page down'):
-            if self.scrollmode:
-                self.frame.set_focus('body')
-                self.body.keypress(size, k)
-            elif k in ('up', 'page up'):
-                self.frame.set_focus('body')
-            else:
-                self.frame.set_focus('footer')
-
-        if k == 'enter':
-            # pass enter to the "ok" button
-            self.frame.set_focus('footer')
-            self.view.keypress( size, k )
-
-    def main(self, ui=None):
-        if ui:
-            self.ui = ui
-        else:
-            self.ui = initialize_urwid_ui()
-        return self.ui.run_wrapper(self.run)
-
-class displaybox(dialog):
-    def show(self, ui=None):
-        if ui:
-            self.ui = ui
-        else:
-            self.ui = initialize_urwid_ui()
-        size = self.ui.get_cols_rows()
-        canvas = self.view.render( size, focus=True )
-        self.ui.start()
-        self.ui.draw_screen( size, canvas )
-        self.ui.stop()
-
-class textentry(dialog):
-    def __init__(self, text, width=None, height=None, multiline=False,
-                 title=''):
-        self.edit = urwid.Edit(multiline=multiline)
-        body = urwid.ListBox([self.edit])
-        body = urwid.AttrWrap(body, 'selectable', 'focustext')
-        if not multiline:
-            body = urwid.Pile( [('fixed', 1, body), urwid.Divider()] )
-            body = urwid.Filler(body)
-
-        dialog.__init__(self, text, body, width, height, title)
-
-        self.frame.set_focus('body')
-
-    def on_exit(self, exitcode):
-        return exitcode, self.edit.get_edit_text()
-
-class listdialog(dialog):
-    def __init__(self, text, widgets, has_default=False, width=None,
-                 height=None, title='', buttonwidth=12):
-        l = []
-        self.items = []
-        for (w, label) in widgets:
-            self.items.append(w)
-            if label:
-                w = urwid.Columns( [('fixed', buttonwidth, w),
-                                    urwid.Text(label)], 2 )
-            w = urwid.AttrWrap(w, 'selectable','focus')
-            l.append(w)
-
-        lb = urwid.ListBox(l)
-        lb = urwid.AttrWrap( lb, "selectable" )
-        dialog.__init__(self, text, height=height, width=width, body=lb,
-                        title=title)
-
-        self.frame.set_focus('body')
-
-    def on_exit(self, exitcode):
-        """Print the tag of the item selected."""
-        if exitcode:
-            return exitcode, None
-
-        for i in self.items:
-            if hasattr(i, 'get_state') and i.get_state():
-                return exitcode, i.get_label()
-        return exitcode, None
-
-class checklistdialog(listdialog):
-    def on_exit(self, exitcode):
-        """
-        Mimick dialog(1)'s --checklist exit.
-        Put each checked item in double quotes with a trailing space.
-        """
-        if exitcode:
-            return exitcode, []
-
-        l = []
-        for i in self.items:
-            if i.get_state():
-                l.append(i.get_label())
-        return exitcode, l
-
-def display_message(message, *args, **kwargs):
-    if args:
-        message = message % tuple(args)
-
-    if 'title' in kwargs:
-        title = kwargs['title']
-    else:
-        title = ''
-
-    if 'ui' in kwargs:
-        ui = kwargs['ui']
-    else:
-        ui = None
-
-    # Rewrap the message
-    chunks = re.split('\n\n+', message)
-    chunks = [re.sub(r'\s+', ' ', x).strip() for x in chunks]
-    message = '\n\n'.join(chunks).strip()
-
-    box = displaybox('', long_message=message, title=title or VERSION)
-    box.show(ui)
-
-def long_message(message, *args, **kwargs):
-    if args:
-        message = message % tuple(args)
-
-    if 'title' in kwargs:
-        title = kwargs['title']
-    else:
-        title = ''
-
-    if 'ui' in kwargs:
-        ui = kwargs['ui']
-    else:
-        ui = None
-
-    # Rewrap the message
-    chunks = re.split('\n\n+', message)
-    chunks = [re.sub(r'\s+', ' ', x).strip() for x in chunks]
-    message = '\n\n'.join(chunks).strip()
-
-    box = dialog('', long_message=message, title=title or VERSION)
-    box.add_buttons([ ("OK", 0) ])
-    box.main(ui)
-
-final_message = long_message
-display_report = long_message
-
-def select_options(msg, ok, help=None, allow_numbers=False, nowrap=False,
-                   ui=None, title=None):
-    box = dialog('', long_message=msg, height=('relative', 80),
-                 title=title or VERSION)
-    if not help:
-        help = {}
-
-    buttons = []
-    default = None
-    for i, option in enumerate(ok):
-        if option.isupper():
-            default = i
-            option = option.lower()
-        buttons.append( (help.get(option, option), option) )
-
-    box.add_buttons(buttons, default, vertical=True)
-    result = box.main(ui)
-    return result
-
-def yes_no(msg, yeshelp, nohelp, default=True, nowrap=False, ui=None):
-    box = dialog('', long_message=msg+"?", title=VERSION)
-    box.add_buttons([ ('Yes', True), ('No', False) ], default=1-int(default))
-    result = box.main(ui)
-    return result
-
-def get_string(prompt, options=None, title=None, force_prompt=False,
-               default='', ui=None):
-    if title:
-        title = '%s: %s' % (VERSION, title)
-    else:
-        title = VERSION
-
-    box = textentry(prompt, title=title)
-    box.add_buttons([ ("OK", 0) ])
-    code, text = box.main(ui)
-    return text or default
-
-def get_multiline(prompt, options=None, title=None, force_prompt=False,
-                  ui=None):
-    if title:
-        title = '%s: %s' % (VERSION, title)
-    else:
-        title = VERSION
-
-    box = textentry(prompt, multiline=True)
-    box.add_buttons([ ("OK", 0) ])
-    code, text = box.main(ui)
-    l = text.split('\n')
-    return l
-
-def get_password(prompt=None):
-    return getpass.getpass(prompt)
-
-def menu(par, options, prompt, default=None, title=None, any_ok=False,
-         order=None, extras=None, multiple=False, empty_ok=False, ui=None,
-         oklabel='Ok', cancellabel='Cancel', quitlabel=None):
-    if not extras:
-        extras = []
-    else:
-        extras = list(extras)
-
-    if not default:
-        default = ''
-
-    if title:
-        title = '%s: %s' % (VERSION, title)
-    else:
-        title = VERSION
-
-    if isinstance(options, dict):
-        options = options.copy()
-        # Convert to a list
-        if order:
-            olist = []
-            for key in order:
-                if options.has_key(key):
-                    olist.append( (key, options[key]) )
-                    del options[key]
-
-            # Append anything out of order
-            options = options.items()
-            options.sort()
-            for option in options:
-                olist.append( option )
-            options = olist
-        else:
-            options = options.items()
-            options.sort()
-
-    opts = []
-    for option, desc in options:
-        if desc:
-            opts.append((option, re.sub(r'\s+', ' ', desc)))
-        else:
-            opts.append((option, desc))
-    options = opts
-
-    if multiple:
-        widgets = [(urwid.CheckBox(option, state=(option == default)),
-                    desc or '') for (option, desc) in options]
-        box = checklistdialog(par, widgets, height=('relative', 80),
-                              title=title)
-        if quitlabel:
-            box.add_buttons( [(oklabel, 0), (cancellabel, -1),
-                              (quitlabel, -2)] )
-        else:
-            box.add_buttons( [(oklabel, 0), (cancellabel, -1)] )
-        result, chosen = box.main(ui)
-        if result < 0:
-            return []
-        return chosen
-
-    # Single menu option only
-    def label_button(option, desc):
-        return option
-
-    widgets = []
-    rlist = []
-    for option, desc in options:
-        if option == '---':
-            # Separator is just a text label
-            b = urwid.Text(desc)
-            b = urwid.AttrWrap( b, 'scrolllabel' )
-            desc = ''
-        else:
-            b = urwid.RadioButton( rlist, label_button(option, desc) )
-            b.exitcode = option
-            b = urwid.AttrWrap( b, 'selectable','focus' )
-        widgets.append((b, desc))
-
-##     if any_ok:
-##         editbox = urwid.Edit(multiline=False)
-##         e = urwid.ListBox([editbox])
-##         e = urwid.AttrWrap(e, 'selectable', 'focustext')
-##         e = urwid.Pile( [('fixed', 1, e)] )
-##         e = urwid.Filler(e)
-##         widgets.append((e, None))
-
-    box = listdialog(par, widgets, height=('relative', 80),
-                     title=title, buttonwidth=12)
-    if quitlabel:
-        box.add_buttons( [(oklabel, 0), (cancellabel, -1), (quitlabel, -2)] )
-    else:
-        box.add_buttons( [(oklabel, 0), (cancellabel, -1)] )
-    focus = 0
-    if default:
-        for i, opt in enumerate(options):
-            if opt[0] == default:
-                focus = i
-                break
-
-    result, chosen = box.main(ui)
-    if result < 0:
-        return result
-
-    return chosen
-
-# A real file dialog would be nice here
-def get_filename(prompt, title=None, force_prompt=False, default=''):
-    return get_string(prompt, title=title, force_prompt=force_prompt,
-                      default=default)
-
-def select_multiple(par, options, prompt, title=None, order=None, extras=None):
-    return menu(par, options, prompt, title=title, order=order, extras=extras,
-                multiple=True, empty_ok=False)
-
-# Things that are very UI dependent go here
-def show_report(number, system, mirrors,
-                http_proxy, screen=None, queryonly=False, title='',
-                archived='no'):
-    import debianbts
-
-    ui = screen
-    if not ui:
-        ui = initialize_urwid_ui()
-
-    sysinfo = debianbts.SYSTEMS[system]
-    display_message('Retrieving report #%d from %s bug tracking system...',
-        number, sysinfo['name'], title=title, ui=ui)
-
-    info = debianbts.get_report(number, system, mirrors=mirrors,
-                                http_proxy=http_proxy, archived=archived)
-    if not info:
-        long_message('Bug report #%d not found.', number, title=title, ui=ui)
-        return
-
-    options = dict(o='Ok', d='More details (launch browser)',
-                   m='Submit more information', q='Quit')
-    valid = 'Odmq'
-
-    while 1:
-        (bugtitle, bodies) = info
-        body = bodies[0]
-
-        r = select_options(body, valid, title=bugtitle, ui=ui, help=options)
-        ui = None
-        if not r or (r == 'o'):
-            break
-        elif r == 'q':
-            return -1
-        elif r == 'm':
-            return number
-
-        launch_browser(debianbts.get_report_url(system, number, archived))
-    return
-
-def handle_bts_query(package, bts, mirrors=None, http_proxy="",
-                     queryonly=False, screen=None, title="", archived='no',
-                     source=False, version=None):
-    import debianbts
-
-    sysinfo = debianbts.SYSTEMS[bts]
-    root = sysinfo.get('btsroot')
-    if not root:
-        ewrite("%s bug tracking system has no web URL; bypassing query.\n",
-               sysinfo['name'])
-        return
-
-    ui = screen
-    if not ui:
-        ui = initialize_urwid_ui()
-
-    if isinstance(package, basestring):
-        pkgname = package
-        if source:
-            pkgname += ' (source)'
-
-        display_message('Querying %s bug tracking system for reports on %s',
-                        debianbts.SYSTEMS[bts]['name'], pkgname,
-                        ui=ui, title=title)
-    else:
-        display_message('Querying %s bug tracking system for reports %s',
-                        debianbts.SYSTEMS[bts]['name'],
-                        ' '.join([str(x) for x in package]), ui=ui,title=title)
-
-    result = None
-    try:
-        (count, sectitle, hierarchy) = debianbts.get_reports(
-            package, bts, mirrors=mirrors, version=version,
-            http_proxy=http_proxy, archived=archived, source=source)
-
-        if not count:
-            ui.run_wrapper(nullfunc)
-            if hierarchy == None:
-                raise NoPackage
-            else:
-                raise NoBugs
-        else:
-            if count > 1:
-                sectitle = '%d bug reports found' % (count,)
-            else:
-                sectitle = '%d bug report found' % (count,)
-
-            buglist = []
-            for (t, bugs) in hierarchy:
-                bcount = len(bugs)
-                buglist.append( ('---', t) )
-                for bug in bugs:
-                    bits = re.split(r'[: ]', bug[1:], 1)
-                    if len(bits) > 1:
-                        tag, info = bits
-                        info = info.strip()
-                        if not info:
-                            info = '(no subject)'
-                    else:
-                        tag = bug[1:]
-                        info = '(no subject)'
-                    buglist.append( (tag, info) )
-
-            p = buglist[1][0]
-            #scr.popWindow()
-            if queryonly:
-                cancellabel = 'Exit'
-                quitlabel = None
-            else:
-                cancellabel = 'Continue'
-                quitlabel='Quit'
-
-            while True:
-                info = menu('Select a bug to read the report:', buglist,
-                            '', ui=ui, title=sectitle, default=p,
-                            oklabel='Get info',
-                            cancellabel=cancellabel,
-                            quitlabel=quitlabel)
-                ui = None
-                if info < 0:
-                    if info == -1:
-                        result = None
-                    else:
-                        result = info
-                    break
-                else:
-                    p = info
-                    res = show_report(int(p), bts, mirrors, http_proxy,
-                                      queryonly=queryonly)
-                    if res:
-                        result = res
-                        break
-
-    except (IOError, NoNetwork):
-        ui.run_wrapper(nullfunc)
-        long_message('Unable to connect to %s BTS.', sysinfo['name'],
-                     title=title)
-    except NoPackage:
-        ui.run_wrapper(nullfunc)
-        long_message('No record of this package found.', title=title)
-        raise NoPackage
-
-    if result and result < 0:
-        raise NoReport
-
-    return result
-
-palette = [
-    ('body','black','light gray', 'standout'),
-    ('border','black','dark blue'),
-    ('shadow','white','black'),
-    ('selectable','black', 'dark cyan'),
-    ('focus','white','dark blue','bold'),
-    ('focustext','light gray','dark blue'),
-    ('title', 'dark red', 'light gray'),
-    ('scrolllabel', 'white', 'dark cyan'),
-    ]
-
-def test():
-    import time
-
-    fp = sys.stdout
-
-    long_message('This is a test.  This is only a test.\nPlease do not adjust your set.')
-    time.sleep(1)
-##     output = get_string('Tell me your name, biatch.')
-##     print >> fp, output
-##     output = get_multiline('List all of your aliases now.')
-##     print >> fp, output
-##     result = select_options('This is really lame', 'ynM', {
-##         'y' : 'You bet', 'n' : 'Never!', 'm' : 'Maybe'})
-##     print >> fp, result
-##     yn = yes_no('Do you like green eggs and ham?', 'Yes sireee', 'No way!')
-##     print >> fp, yn
-
-    mailers = [(x, '') for x in reportbug.MUA.keys()]
-    mailers.sort()
-    mailer = menu('Choose a mailer for your report', mailers,
-                  'Select mailer: ', default='mutt', empty_ok=True)
-    print >> fp, mailer
-
-    import debianbts
-
-    tags = debianbts.TAGS.copy()
-    tags.update(debianbts.CRITICAL_TAGS)
-    tagorder = debianbts.TAGLIST + debianbts.CRITICAL_TAGLIST
-
-    taglist = select_multiple(
-        'Do any of the following apply to this report?', tags,
-        'Please select tags: ', order=tagorder,
-        extras=debianbts.EXTRA_TAGS)
-    print >> fp, taglist
-
-if __name__ == '__main__':
-    test()

Copied: trunk/reportbug/ui/urwid_ui.py (from rev 640, trunk/reportbug/ui/urwid.py)
===================================================================
--- trunk/reportbug/ui/urwid_ui.py	                        (rev 0)
+++ trunk/reportbug/ui/urwid_ui.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -0,0 +1,685 @@
+# urwid user interface for reportbug
+#   Written by Chris Lawrence <lawrencc at debian.org>
+#   (C) 2006-08 Chris Lawrence
+#
+# This program is freely distributable per the following license:
+#
+##  Permission to use, copy, modify, and distribute this software and its
+##  documentation for any purpose and without fee is hereby granted,
+##  provided that the above copyright notice appears in all copies and that
+##  both that copyright notice and this permission notice appear in
+##  supporting documentation.
+##
+##  I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+##  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL I
+##  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+##  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+##  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+##  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+##  SOFTWARE.
+#
+# Portions of this file are licensed under the Lesser GNU Public License
+# (LGPL) Version 2.1 or later.  On Debian systems, this license is available
+# in /usr/share/common-licenses/LGPL
+#
+# $Id: reportbug_ui_urwid.py,v 1.3.2.19 2007-04-17 20:02:41 lawrencc Exp $
+
+import sys
+import re
+import getpass
+from reportbug.exceptions import (
+    UINotImportable,
+    NoPackage, NoBugs, NoNetwork, NoReport,
+    )
+from reportbug.urlutils import launch_browser
+from text_ui import (
+    ewrite,
+    spawn_editor,
+    )
+from reportbug import VERSION
+
+try:
+    import urwid.raw_display
+    import urwid
+except ImportError:
+    raise UINotImportable, 'Please install the python-urwid package to use this interface.'
+
+ISATTY = sys.stdin.isatty()
+
+log_message = ewrite
+display_failure = ewrite
+
+# Start a urwid session
+def initialize_urwid_ui():
+    ui = urwid.raw_display.Screen()
+    ui.register_palette(palette)
+    # Improve responsiveness of UI
+    ui.set_input_timeouts(max_wait=0.1)
+    return ui
+
+# Empty function to satisfy ui.run_wrapper()
+def nullfunc():
+    pass
+
+# Widgets ripped mercilessly from urwid examples (dialog.py)
+class buttonpush(Exception):
+    pass
+
+def button_press(button):
+    raise buttonpush, button.exitcode
+
+class SelectableText(urwid.Edit):
+    def valid_char(self, ch):
+        return False
+
+class dialog(object):
+    def __init__(self, message, body=None, width=None, height=None,
+                 title='', long_message=''):
+        self.body = body
+
+        self.scrollmode=False
+        if not body:
+            if long_message:
+                box = SelectableText(edit_text=long_message)
+                box.set_edit_pos(0)
+                self.body = body = urwid.ListBox([box])
+                self.scrollmode=True
+            else:
+                self.body = body = urwid.Filler(urwid.Divider(), 'top')
+
+        if not width:
+            width = ('relative', 80)
+
+        if not height:
+            height = ('relative', 80)
+
+        self.frame = urwid.Frame(body, focus_part='footer')
+        if message:
+            self.frame.header = urwid.Pile([urwid.Text(message),
+                                            urwid.Divider()])
+
+        w = self.frame
+        # pad area around listbox
+        w = urwid.Padding(w, ('fixed left',2), ('fixed right',2))
+        w = urwid.Filler(w, ('fixed top',1), ('fixed bottom',1))
+        w = urwid.AttrWrap(w, 'body')
+
+        if title:
+            w = urwid.Frame(w)
+            w.header = urwid.Text( ('title', title) )
+
+        # "shadow" effect
+        w = urwid.Columns( [w, ('fixed', 1, urwid.AttrWrap( urwid.Filler(urwid.Text(('border',' ')), "top") ,'shadow'))])
+        w = urwid.Frame( w, footer = urwid.AttrWrap(urwid.Text(('border',' ')),'shadow'))
+        # outermost border area
+        w = urwid.Padding(w, 'center', width )
+        w = urwid.Filler(w, 'middle', height )
+        w = urwid.AttrWrap( w, 'border' )
+        self.view = w
+
+    def add_buttons(self, buttons, default=0, vertical=False):
+        l = []
+        for name, exitcode in buttons:
+            if exitcode == '---':
+                # Separator is just a text label
+                b = urwid.Text(name)
+                b = urwid.AttrWrap( b, 'scrolllabel' )
+            else:
+                b = urwid.Button( name, self.button_press )
+                b.exitcode = exitcode
+                b = urwid.AttrWrap( b, 'selectable','focus' )
+            l.append( b )
+
+        if vertical:
+            box = urwid.ListBox(l)
+            box.set_focus(default or 0)
+            self.buttons = urwid.Frame(urwid.AttrWrap(box, 'selectable'))
+            self.frame.footer = urwid.BoxAdapter(self.buttons, min(len(l), 10))
+        else:
+            self.buttons = urwid.GridFlow(l, 12, 3, 1, 'center')
+            self.buttons.set_focus(default or 0)
+            self.frame.footer = urwid.Pile( [ urwid.Divider(), self.buttons ],
+                                            focus_item = 1 )
+
+    def button_press(self, button):
+        raise buttonpush, button.exitcode
+
+    def run(self):
+        #self.ui.set_mouse_tracking()
+        size = self.ui.get_cols_rows()
+        try:
+            while True:
+                canvas = self.view.render( size, focus=True )
+                self.ui.draw_screen( size, canvas )
+                keys = None
+                while not keys:
+                    keys = self.ui.get_input()
+                for k in keys:
+                    if urwid.is_mouse_event(k):
+                        event, button, col, row = k
+                        self.view.mouse_event( size,
+                                               event, button, col, row,
+                                               focus=True)
+                    if k == 'window resize':
+                        size = self.ui.get_cols_rows()
+                    k = self.view.keypress( size, k )
+                    if k:
+                        self.unhandled_key( size, k)
+        except buttonpush, e:
+            return self.on_exit( e.args[0] )
+
+    def on_exit(self, exitcode):
+        return exitcode
+
+    def unhandled_key(self, size, k):
+        if k in ('tab', 'shift tab'):
+            focus = self.frame.focus_part
+            if focus == 'footer':
+                self.frame.set_focus('body')
+            else:
+                self.frame.set_focus('footer')
+
+        if k in ('up','page up', 'down', 'page down'):
+            if self.scrollmode:
+                self.frame.set_focus('body')
+                self.body.keypress(size, k)
+            elif k in ('up', 'page up'):
+                self.frame.set_focus('body')
+            else:
+                self.frame.set_focus('footer')
+
+        if k == 'enter':
+            # pass enter to the "ok" button
+            self.frame.set_focus('footer')
+            self.view.keypress( size, k )
+
+    def main(self, ui=None):
+        if ui:
+            self.ui = ui
+        else:
+            self.ui = initialize_urwid_ui()
+        return self.ui.run_wrapper(self.run)
+
+class displaybox(dialog):
+    def show(self, ui=None):
+        if ui:
+            self.ui = ui
+        else:
+            self.ui = initialize_urwid_ui()
+        size = self.ui.get_cols_rows()
+        canvas = self.view.render( size, focus=True )
+        self.ui.start()
+        self.ui.draw_screen( size, canvas )
+        self.ui.stop()
+
+class textentry(dialog):
+    def __init__(self, text, width=None, height=None, multiline=False,
+                 title=''):
+        self.edit = urwid.Edit(multiline=multiline)
+        body = urwid.ListBox([self.edit])
+        body = urwid.AttrWrap(body, 'selectable', 'focustext')
+        if not multiline:
+            body = urwid.Pile( [('fixed', 1, body), urwid.Divider()] )
+            body = urwid.Filler(body)
+
+        dialog.__init__(self, text, body, width, height, title)
+
+        self.frame.set_focus('body')
+
+    def on_exit(self, exitcode):
+        return exitcode, self.edit.get_edit_text()
+
+class listdialog(dialog):
+    def __init__(self, text, widgets, has_default=False, width=None,
+                 height=None, title='', buttonwidth=12):
+        l = []
+        self.items = []
+        for (w, label) in widgets:
+            self.items.append(w)
+            if label:
+                w = urwid.Columns( [('fixed', buttonwidth, w),
+                                    urwid.Text(label)], 2 )
+            w = urwid.AttrWrap(w, 'selectable','focus')
+            l.append(w)
+
+        lb = urwid.ListBox(l)
+        lb = urwid.AttrWrap( lb, "selectable" )
+        dialog.__init__(self, text, height=height, width=width, body=lb,
+                        title=title)
+
+        self.frame.set_focus('body')
+
+    def on_exit(self, exitcode):
+        """Print the tag of the item selected."""
+        if exitcode:
+            return exitcode, None
+
+        for i in self.items:
+            if hasattr(i, 'get_state') and i.get_state():
+                return exitcode, i.get_label()
+        return exitcode, None
+
+class checklistdialog(listdialog):
+    def on_exit(self, exitcode):
+        """
+        Mimick dialog(1)'s --checklist exit.
+        Put each checked item in double quotes with a trailing space.
+        """
+        if exitcode:
+            return exitcode, []
+
+        l = []
+        for i in self.items:
+            if i.get_state():
+                l.append(i.get_label())
+        return exitcode, l
+
+def display_message(message, *args, **kwargs):
+    if args:
+        message = message % tuple(args)
+
+    if 'title' in kwargs:
+        title = kwargs['title']
+    else:
+        title = ''
+
+    if 'ui' in kwargs:
+        ui = kwargs['ui']
+    else:
+        ui = None
+
+    # Rewrap the message
+    chunks = re.split('\n\n+', message)
+    chunks = [re.sub(r'\s+', ' ', x).strip() for x in chunks]
+    message = '\n\n'.join(chunks).strip()
+
+    box = displaybox('', long_message=message, title=title or VERSION)
+    box.show(ui)
+
+def long_message(message, *args, **kwargs):
+    if args:
+        message = message % tuple(args)
+
+    if 'title' in kwargs:
+        title = kwargs['title']
+    else:
+        title = ''
+
+    if 'ui' in kwargs:
+        ui = kwargs['ui']
+    else:
+        ui = None
+
+    # Rewrap the message
+    chunks = re.split('\n\n+', message)
+    chunks = [re.sub(r'\s+', ' ', x).strip() for x in chunks]
+    message = '\n\n'.join(chunks).strip()
+
+    box = dialog('', long_message=message, title=title or VERSION)
+    box.add_buttons([ ("OK", 0) ])
+    box.main(ui)
+
+final_message = long_message
+display_report = long_message
+
+def select_options(msg, ok, help=None, allow_numbers=False, nowrap=False,
+                   ui=None, title=None):
+    box = dialog('', long_message=msg, height=('relative', 80),
+                 title=title or VERSION)
+    if not help:
+        help = {}
+
+    buttons = []
+    default = None
+    for i, option in enumerate(ok):
+        if option.isupper():
+            default = i
+            option = option.lower()
+        buttons.append( (help.get(option, option), option) )
+
+    box.add_buttons(buttons, default, vertical=True)
+    result = box.main(ui)
+    return result
+
+def yes_no(msg, yeshelp, nohelp, default=True, nowrap=False, ui=None):
+    box = dialog('', long_message=msg+"?", title=VERSION)
+    box.add_buttons([ ('Yes', True), ('No', False) ], default=1-int(default))
+    result = box.main(ui)
+    return result
+
+def get_string(prompt, options=None, title=None, force_prompt=False,
+               default='', ui=None):
+    if title:
+        title = '%s: %s' % (VERSION, title)
+    else:
+        title = VERSION
+
+    box = textentry(prompt, title=title)
+    box.add_buttons([ ("OK", 0) ])
+    code, text = box.main(ui)
+    return text or default
+
+def get_multiline(prompt, options=None, title=None, force_prompt=False,
+                  ui=None):
+    if title:
+        title = '%s: %s' % (VERSION, title)
+    else:
+        title = VERSION
+
+    box = textentry(prompt, multiline=True)
+    box.add_buttons([ ("OK", 0) ])
+    code, text = box.main(ui)
+    l = text.split('\n')
+    return l
+
+def get_password(prompt=None):
+    return getpass.getpass(prompt)
+
+def menu(par, options, prompt, default=None, title=None, any_ok=False,
+         order=None, extras=None, multiple=False, empty_ok=False, ui=None,
+         oklabel='Ok', cancellabel='Cancel', quitlabel=None):
+    if not extras:
+        extras = []
+    else:
+        extras = list(extras)
+
+    if not default:
+        default = ''
+
+    if title:
+        title = '%s: %s' % (VERSION, title)
+    else:
+        title = VERSION
+
+    if isinstance(options, dict):
+        options = options.copy()
+        # Convert to a list
+        if order:
+            olist = []
+            for key in order:
+                if options.has_key(key):
+                    olist.append( (key, options[key]) )
+                    del options[key]
+
+            # Append anything out of order
+            options = options.items()
+            options.sort()
+            for option in options:
+                olist.append( option )
+            options = olist
+        else:
+            options = options.items()
+            options.sort()
+
+    opts = []
+    for option, desc in options:
+        if desc:
+            opts.append((option, re.sub(r'\s+', ' ', desc)))
+        else:
+            opts.append((option, desc))
+    options = opts
+
+    if multiple:
+        widgets = [(urwid.CheckBox(option, state=(option == default)),
+                    desc or '') for (option, desc) in options]
+        box = checklistdialog(par, widgets, height=('relative', 80),
+                              title=title)
+        if quitlabel:
+            box.add_buttons( [(oklabel, 0), (cancellabel, -1),
+                              (quitlabel, -2)] )
+        else:
+            box.add_buttons( [(oklabel, 0), (cancellabel, -1)] )
+        result, chosen = box.main(ui)
+        if result < 0:
+            return []
+        return chosen
+
+    # Single menu option only
+    def label_button(option, desc):
+        return option
+
+    widgets = []
+    rlist = []
+    for option, desc in options:
+        if option == '---':
+            # Separator is just a text label
+            b = urwid.Text(desc)
+            b = urwid.AttrWrap( b, 'scrolllabel' )
+            desc = ''
+        else:
+            b = urwid.RadioButton( rlist, label_button(option, desc) )
+            b.exitcode = option
+            b = urwid.AttrWrap( b, 'selectable','focus' )
+        widgets.append((b, desc))
+
+##     if any_ok:
+##         editbox = urwid.Edit(multiline=False)
+##         e = urwid.ListBox([editbox])
+##         e = urwid.AttrWrap(e, 'selectable', 'focustext')
+##         e = urwid.Pile( [('fixed', 1, e)] )
+##         e = urwid.Filler(e)
+##         widgets.append((e, None))
+
+    box = listdialog(par, widgets, height=('relative', 80),
+                     title=title, buttonwidth=12)
+    if quitlabel:
+        box.add_buttons( [(oklabel, 0), (cancellabel, -1), (quitlabel, -2)] )
+    else:
+        box.add_buttons( [(oklabel, 0), (cancellabel, -1)] )
+    focus = 0
+    if default:
+        for i, opt in enumerate(options):
+            if opt[0] == default:
+                focus = i
+                break
+
+    result, chosen = box.main(ui)
+    if result < 0:
+        return result
+
+    return chosen
+
+# A real file dialog would be nice here
+def get_filename(prompt, title=None, force_prompt=False, default=''):
+    return get_string(prompt, title=title, force_prompt=force_prompt,
+                      default=default)
+
+def select_multiple(par, options, prompt, title=None, order=None, extras=None):
+    return menu(par, options, prompt, title=title, order=order, extras=extras,
+                multiple=True, empty_ok=False)
+
+# Things that are very UI dependent go here
+def show_report(number, system, mirrors,
+                http_proxy, screen=None, queryonly=False, title='',
+                archived='no'):
+    import debianbts
+
+    ui = screen
+    if not ui:
+        ui = initialize_urwid_ui()
+
+    sysinfo = debianbts.SYSTEMS[system]
+    display_message('Retrieving report #%d from %s bug tracking system...',
+        number, sysinfo['name'], title=title, ui=ui)
+
+    info = debianbts.get_report(number, system, mirrors=mirrors,
+                                http_proxy=http_proxy, archived=archived)
+    if not info:
+        long_message('Bug report #%d not found.', number, title=title, ui=ui)
+        return
+
+    options = dict(o='Ok', d='More details (launch browser)',
+                   m='Submit more information', q='Quit')
+    valid = 'Odmq'
+
+    while 1:
+        (bugtitle, bodies) = info
+        body = bodies[0]
+
+        r = select_options(body, valid, title=bugtitle, ui=ui, help=options)
+        ui = None
+        if not r or (r == 'o'):
+            break
+        elif r == 'q':
+            return -1
+        elif r == 'm':
+            return number
+
+        launch_browser(debianbts.get_report_url(system, number, archived))
+    return
+
+def handle_bts_query(package, bts, mirrors=None, http_proxy="",
+                     queryonly=False, screen=None, title="", archived='no',
+                     source=False, version=None):
+    import debianbts
+
+    sysinfo = debianbts.SYSTEMS[bts]
+    root = sysinfo.get('btsroot')
+    if not root:
+        ewrite("%s bug tracking system has no web URL; bypassing query.\n",
+               sysinfo['name'])
+        return
+
+    ui = screen
+    if not ui:
+        ui = initialize_urwid_ui()
+
+    if isinstance(package, basestring):
+        pkgname = package
+        if source:
+            pkgname += ' (source)'
+
+        display_message('Querying %s bug tracking system for reports on %s',
+                        debianbts.SYSTEMS[bts]['name'], pkgname,
+                        ui=ui, title=title)
+    else:
+        display_message('Querying %s bug tracking system for reports %s',
+                        debianbts.SYSTEMS[bts]['name'],
+                        ' '.join([str(x) for x in package]), ui=ui,title=title)
+
+    result = None
+    try:
+        (count, sectitle, hierarchy) = debianbts.get_reports(
+            package, bts, mirrors=mirrors, version=version,
+            http_proxy=http_proxy, archived=archived, source=source)
+
+        if not count:
+            ui.run_wrapper(nullfunc)
+            if hierarchy == None:
+                raise NoPackage
+            else:
+                raise NoBugs
+        else:
+            if count > 1:
+                sectitle = '%d bug reports found' % (count,)
+            else:
+                sectitle = '%d bug report found' % (count,)
+
+            buglist = []
+            for (t, bugs) in hierarchy:
+                bcount = len(bugs)
+                buglist.append( ('---', t) )
+                for bug in bugs:
+                    bits = re.split(r'[: ]', bug[1:], 1)
+                    if len(bits) > 1:
+                        tag, info = bits
+                        info = info.strip()
+                        if not info:
+                            info = '(no subject)'
+                    else:
+                        tag = bug[1:]
+                        info = '(no subject)'
+                    buglist.append( (tag, info) )
+
+            p = buglist[1][0]
+            #scr.popWindow()
+            if queryonly:
+                cancellabel = 'Exit'
+                quitlabel = None
+            else:
+                cancellabel = 'Continue'
+                quitlabel='Quit'
+
+            while True:
+                info = menu('Select a bug to read the report:', buglist,
+                            '', ui=ui, title=sectitle, default=p,
+                            oklabel='Get info',
+                            cancellabel=cancellabel,
+                            quitlabel=quitlabel)
+                ui = None
+                if info < 0:
+                    if info == -1:
+                        result = None
+                    else:
+                        result = info
+                    break
+                else:
+                    p = info
+                    res = show_report(int(p), bts, mirrors, http_proxy,
+                                      queryonly=queryonly)
+                    if res:
+                        result = res
+                        break
+
+    except (IOError, NoNetwork):
+        ui.run_wrapper(nullfunc)
+        long_message('Unable to connect to %s BTS.', sysinfo['name'],
+                     title=title)
+    except NoPackage:
+        ui.run_wrapper(nullfunc)
+        long_message('No record of this package found.', title=title)
+        raise NoPackage
+
+    if result and result < 0:
+        raise NoReport
+
+    return result
+
+palette = [
+    ('body','black','light gray', 'standout'),
+    ('border','black','dark blue'),
+    ('shadow','white','black'),
+    ('selectable','black', 'dark cyan'),
+    ('focus','white','dark blue','bold'),
+    ('focustext','light gray','dark blue'),
+    ('title', 'dark red', 'light gray'),
+    ('scrolllabel', 'white', 'dark cyan'),
+    ]
+
+def test():
+    import time
+
+    fp = sys.stdout
+
+    long_message('This is a test.  This is only a test.\nPlease do not adjust your set.')
+    time.sleep(1)
+##     output = get_string('Tell me your name, biatch.')
+##     print >> fp, output
+##     output = get_multiline('List all of your aliases now.')
+##     print >> fp, output
+##     result = select_options('This is really lame', 'ynM', {
+##         'y' : 'You bet', 'n' : 'Never!', 'm' : 'Maybe'})
+##     print >> fp, result
+##     yn = yes_no('Do you like green eggs and ham?', 'Yes sireee', 'No way!')
+##     print >> fp, yn
+
+    mailers = [(x, '') for x in reportbug.MUA.keys()]
+    mailers.sort()
+    mailer = menu('Choose a mailer for your report', mailers,
+                  'Select mailer: ', default='mutt', empty_ok=True)
+    print >> fp, mailer
+
+    import debianbts
+
+    tags = debianbts.TAGS.copy()
+    tags.update(debianbts.CRITICAL_TAGS)
+    tagorder = debianbts.TAGLIST + debianbts.CRITICAL_TAGLIST
+
+    taglist = select_multiple(
+        'Do any of the following apply to this report?', tags,
+        'Please select tags: ', order=tagorder,
+        extras=debianbts.EXTRA_TAGS)
+    print >> fp, taglist
+
+if __name__ == '__main__':
+    test()

Modified: trunk/reportbug/utils.py
===================================================================
--- trunk/reportbug/utils.py	2008-08-31 20:45:18 UTC (rev 640)
+++ trunk/reportbug/utils.py	2008-08-31 22:54:15 UTC (rev 641)
@@ -49,7 +49,7 @@
 AVAILABLE_UIS = ['text']
 
 try:
-    import ui.urwid
+    import ui.urwid_ui
     AVAILABLE_UIS.append('urwid')
 except:
     pass




More information about the Reportbug-commits mailing list