commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r7544 - trunk/gnue-forms/src/uidrivers/curses


From: johannes
Subject: [gnue] r7544 - trunk/gnue-forms/src/uidrivers/curses
Date: Wed, 18 May 2005 05:13:28 -0500 (CDT)

Author: johannes
Date: 2005-05-18 05:13:26 -0500 (Wed, 18 May 2005)
New Revision: 7544

Modified:
   trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py
   trunk/gnue-forms/src/uidrivers/curses/dialogs.py
Log:
Added versatile input dialog, updated login handler


Modified: trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py     2005-05-17 
15:31:08 UTC (rev 7543)
+++ trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py     2005-05-18 
10:13:26 UTC (rev 7544)
@@ -24,6 +24,7 @@
 import curses
 
 from gnue.common.datasources import GLoginHandler
+from gnue.forms.uidrivers.curses import dialogs
 
 # =============================================================================
 # This class implements a login handler for curses
@@ -35,10 +36,11 @@
   # Get input for all fields listed in loginData
   # ---------------------------------------------------------------------------
 
-  def getLogin (self, loginData, error = None):
+  def _askLogin (self, title, fields):
 
-    (connection, description, fields) = loginData
+    dlg = dialogs.InputDialog (title, fields, self.uiDriver.attr)
 
-    text = u_('Login required for "%s"') % (description or connection)
+    dlg.run ()
+    return dlg.inputData
 
-    return self.uiDriver.askLogin (text, fields, error)
+

Modified: trunk/gnue-forms/src/uidrivers/curses/dialogs.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/dialogs.py    2005-05-17 15:31:08 UTC 
(rev 7543)
+++ trunk/gnue-forms/src/uidrivers/curses/dialogs.py    2005-05-18 10:13:26 UTC 
(rev 7544)
@@ -22,7 +22,11 @@
 # $Id$
 
 import curses
+import curses.ascii
+import sys
 
+from gnue.common.apps import i18n
+
 # =============================================================================
 # Dialog box for selection an option from a given options dictionary
 # =============================================================================
@@ -34,7 +38,7 @@
   # ---------------------------------------------------------------------------
 
   def __init__ (self, title, options, attrs, quit = None, left = 0, top = 0,
-      right = 80, bottom = 24):
+      right = 80, bottom = 24, returnKeys = False):
     
     curses.noecho ()
     curses.cbreak ()
@@ -44,10 +48,10 @@
     except:
       self.__oldcursor = 1
 
-    self.__data = ["%s" % item for item in options.values ()]
+    self.__data = [("%s" % val, key) for (key, val) in options.items ()]
     self.__data.sort ()
 
-    maxString = max ([len (item) for item in self.__data])
+    maxString = max ([len (item) for (item, key) in self.__data])
 
     width  = min (right - left, max ((maxString + 4), len (title) + 4))
     height = min (bottom - top, len (options) + 4)
@@ -75,6 +79,7 @@
     self.__index  = 0
     self.__pIndex = 1
     self.__page   = height - 4
+    self.__returnKeys = returnKeys
 
     self.__refresh ()
 
@@ -106,7 +111,7 @@
           self.__move (+1)
 
         elif key == 10:
-          return self.__data [self.__index]
+          return self.__data [self.__index][self.__returnKeys]
 
     finally:
       try:
@@ -148,7 +153,7 @@
   def __refresh (self):
 
     lines = self.__data [self.__offset:self.__offset + self.__page]
-    for (line, value) in enumerate (lines):
+    for (line, (value, key)) in enumerate (lines):
       text = " %s " % value.ljust (self.__dwidth) [:self.__dwidth]
       if line == self.__pIndex - 1:
         attr = self.__reverse
@@ -160,33 +165,342 @@
     self.__window.move (3 + self.__pIndex - 1, 1)
 
 
+
 # =============================================================================
+# Class implementing a versatile input dialog
+# =============================================================================
+
+class InputDialog:
+
+  # ---------------------------------------------------------------------------
+  # Create a new input dialog
+  # ---------------------------------------------------------------------------
+
+  def __init__ (self, title, fields, attrs, cancel = True, left = 0, top = 0,
+      right = 80, bottom = 24):
+
+    curses.noecho ()
+    curses.cbreak ()
+
+    self.attrs  = attrs
+    self.fields = fields
+    self.__buildWindow (title, left, top, right, bottom)
+
+    self.inputData  = {}
+
+    # A dictionary with the fieldname as key and a triple (master, allowed,
+    # current) as data items
+    self.lookups    = {}
+
+    y = 2
+    self.__entries = []
+
+    for (label, name, ftype, default, master, elements) in fields:
+      if ftype == 'image':
+        continue
+
+      y += 1
+
+      if ftype in ['label', 'warning']:
+        attr = [attrs ['background'], attrs ['warnmsg']][ftype == 'warning']
+        cw   = self.__width - 2
+        lh   = label.count ('\n') + 1
+
+        for item in label.splitlines ():
+          self.__window.addnstr (y, 1, o(item.center (cw)), cw, attr)
+          y += 1
+
+        if lh > 1: y -= 1
+
+      elif ftype in ['string', 'password', 'dropdown']:
+        self.__window.addnstr (y, 2, o(label), self.__leftcol)
+
+        win = curses.newwin (1, self.__rightcol,
+                      self.__wtop + y, self.__wleft + self.__leftcol + 2)
+        self.__entries.append ((win, name, default, ftype == 'password'))
+
+        if ftype == 'dropdown':
+          values = elements [0][1]
+          self.lookups [name] = (master, values,
+                                 self.__getCurrent (master, values))
+          dispValue = self.lookups [name][2].get (default, '')
+        else:
+          dispValue = default
+
+        self.__display (win, dispValue, ftype == 'password')
+
+
+
+  # ---------------------------------------------------------------------------
+  # Run the dialog
+  # ---------------------------------------------------------------------------
+
+  def run (self):
+
+    self.__window.refresh ()
+
+    index = 0
+    while index < len (self.__entries):
+      (win, name, default, hidden) = self.__entries [index]
+      cvalue = self.inputData.get (name, default)
+      (value, code) = self.__accept (win, cvalue, name, hidden)
+      if value is None:
+        if name in self.inputData:
+          del self.inputData [name]
+      else:
+        self.inputData [name] = value
+
+      if code == 27:
+        self.inputData = None
+        break
+
+      elif code == curses.KEY_UP:
+        index = max (0, index - 1)
+
+      else:
+        index += 1
+
+
+  # ---------------------------------------------------------------------------
+  # Calculate and build the window
+  # ---------------------------------------------------------------------------
+
+  def __buildWindow (self, title, left, top, right, bottom):
+
+    leftcol  = 0
+    rightcol = 0
+    width    = 0
+    height   = 5        # box + title + spacer + bottom
+
+    for (label, name, ftype, default, master, elements) in self.fields:
+      if ftype in ['label', 'warning']:
+        arr = ([width] + [len (part) + 2 for part in label.splitlines ()])
+        width = max ([width] + [len (part) + 2 for part in label.splitlines 
()])
+        height += label.count ('\n') + 1
+
+      elif ftype in ['string', 'password']:
+        leftcol  = max (leftcol, len (label) + 1)
+        rightcol = max (rightcol, 20)
+        height  += 1
+
+      elif ftype == 'dropdown':
+        leftcol = max (leftcol, len (label) + 1)
+        height += 1
+
+        # We introspect only the first control since we do not provide them all
+        # for input
+        (label, allowed) = elements [0]
+        widest = 0
+        if master is None:
+          data = [allowed]
+        else:
+          data = allowed.values ()
+
+        for vdict in data:
+          widest = max ([widest] + [len (i) for i in vdict.values ()])
+
+        rightcol = max (rightcol, widest)
+
+
+    self.__width  = min (max (leftcol + rightcol + 4, width), right - left)
+    self.__height = min (height, bottom - top)
+    self.__wtop   = (bottom - top - self.__height) / 2
+    self.__wleft  = (right - left - self.__width) / 2
+
+    self.__window = curses.newwin (self.__height, self.__width,
+                                   self.__wtop, self.__wleft)
+    self.__window.bkgd (' ', self.attrs ['background'])
+    self.__window.box ()
+    self.__window.keypad (1)
+
+    self.__window.addstr (1, 1, title.center (self.__width - 2))
+
+    self.__leftcol  = leftcol
+    self.__rightcol = rightcol
+
+
+
+  # ---------------------------------------------------------------------------
+  # Get input for a given field
+  # ---------------------------------------------------------------------------
+
+  def __accept (self, win, default, name, hidden = False):
+
+    if name in self.lookups:
+      (master, allowed, current) = self.lookups [name]
+      current = self.__getCurrent (master, allowed)
+      self.lookups [name] = (master, allowed, current)
+
+      reverse = {}
+      for (k, v) in current.items ():
+        reverse [v] = k
+
+      value = current.get (default, '')
+    else:
+      value   = default or ''
+      current = None
+      revese  = None
+
+    win.keypad (1)
+
+    while True:
+      self.__display (win, value, hidden, True)
+
+      code = self.__window.getch ()
+
+      if code in [9, 10, 27, curses.KEY_DOWN, curses.KEY_UP]:
+        if code == 27:
+          rvalue = None
+
+        elif code == 9:
+          code = curses.KEY_DOWN
+
+        if code != 27 and name in self.lookups:
+          if reverse and not value in reverse:
+            curses.beep ()
+            continue
+          else:
+            rvalue = reverse.get (value)
+        else:
+          rvalue = value
+
+        break
+
+      elif code == curses.KEY_BACKSPACE:
+        value = value [:-1]
+
+      elif current and code == 23:
+        op = OptionsDialog ('Select Option', current, self.attrs, 23)
+        v = op.run ()
+        self.__window.redrawwin ()
+        curses.doupdate ()
+
+        if v is not None:
+          value = v
+          curses.ungetch (10)
+
+      elif curses.ascii.isprint (code):
+        value += chr (code)
+
+    self.__display (win, value, hidden, False)
+
+    return (rvalue, code)
+
+
+  # ---------------------------------------------------------------------------
+  # Display the visible portion of a given value within a window
+  # ---------------------------------------------------------------------------
+
+  def __display (self, win, value, hidden = False, edit = False):
+
+    data = value or ''
+    attr = self.attrs [edit and 'focusentry' or 'entry']
+    win.bkgd (' ', attr)
+
+    length = win.getmaxyx () [1] - 1
+    (top, left)   = win.getbegyx ()
+    (mtop, mleft) = self.__window.getbegyx ()
+    top  -= mtop
+    left -= mleft
+
+    dvalue = [data, '*' * len (data)] [hidden]
+    offset = max (0, len (dvalue) - length)
+    pos    = min (len (dvalue) - offset, length)
+
+    out = dvalue [offset:offset + length].ljust (length + 1)
+    self.__window.addnstr (top, left, out, length + 1, attr)
+
+    self.__window.move (top, (pos + left))
+
+
+  # ---------------------------------------------------------------------------
+  # Get the current value dictionary for a given dropdown
+  # ---------------------------------------------------------------------------
+
+  def __getCurrent (self, master, values):
+
+    if master is None:
+      return values
+    else:
+      return values.get (self.inputData.get (master), {})
+
+
+# =============================================================================
 # Module self test
 # =============================================================================
 
 if __name__ == '__main__':
   curses.initscr ()
+  curses.cbreak ()
   curses.raw ()
   curses.start_color ()
 
   curses.init_pair (3, curses.COLOR_BLACK, curses.COLOR_CYAN)
   curses.init_pair (4, curses.COLOR_BLUE, curses.COLOR_WHITE)
   curses.init_pair (5, curses.COLOR_WHITE, curses.COLOR_RED)
+  curses.init_pair (6, curses.COLOR_BLACK, curses.COLOR_CYAN)
+  curses.init_pair (9, curses.COLOR_WHITE, curses.COLOR_RED)
 
   attrs = {'background': curses.color_pair (4),
            'focusentry': curses.color_pair (5),
-           'window 1': curses.color_pair (3)}
+           'entry'     : curses.color_pair (6),
+           'window 1'  : curses.color_pair (3),
+           'warnmsg'   : curses.color_pair (9) + curses.A_BOLD}
 
   opts = {'foo': 'Foobar', 'baz': 'Barbaz and the Gang!',
           2: 'something', 3: 'quite', 4: 'interesting stuff', 5: 'hey ho',
           7: 'number 7'}
 
   opts = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8, 9:9, 10:10, 11:11, 12:12,
-  13:13, 14:14, 15:15, 16:16, 17:17, 18:18, 19:19, 20:20}
-  d = OptionsDialog ('Foos', opts, attrs, quit = 23, top = 2, bottom = 22)
-  r = d.run ()
+  13:13, 14:14, "k15":15, 16:16, 17:17, 18:18, 19:19, 20:20}
+  #d = OptionsDialog ('Foos', opts, attrs, quit = 23, top = 2, bottom = 22,
+      #returnKeys = True)
+  #r = d.run ()
 
+  #curses.endwin ()
 
+
+
+  #print "RESULT:", r
+
+  #sys.exit ()
+
+  # ---------------------------------------------------------------------------
+
+  cname = {'c1': 'demoa', 'c2': 'demob'}
+  ckey  = {'c1': 'ck-A', 'c2': 'ck-B'}
+
+  wija = {'c1': {'04': '2004', '05': '2005'},
+          'c2': {'24': '2024', '25': '2025', '26': '2026'}}
+
+  codes = {'24': {'241': 'c-24-1', '242': 'c-24-2'},
+           '25': {'251': 'c-25-1'}}
+
+  descr = 'Hier ist ein langer Text\nMit einem Zeilenumbruch'
+  fields = [('Foo!', '/home/johannes/gnue/share/gnue/images/gnue.png', 'image',
+             None, None, []),
+            ('Username', '_username', 'string', 'frodo', None, \
+              [('Name of the user', None)]),
+            ('', None, 'label', None, None, []),
+            ('Password', '_password', 'password', 'foo', None, [('yeah',1)]),
+            ('Foobar', '_foobar', 'dropdown', 'frob', None, \
+                [('single', {'trash': 'Da Trash', 'frob': 'Frob'})]),
+            ('Multi', '_multi', 'dropdown', '100', None, \
+                [('name', {'50': 'A 50', '100': 'B 100', '9': 'C 9'}),
+                ('sepp', {'50': 'se 50', '100': 'se 100', '9': 'se 9'})]),
+            (descr, '_depp', 'label', 'furz', None, []),
+            ('Das ist jetzt ein Fehler', None, 'warning', None, None, []),
+            ('Firma', 'company', 'dropdown', 'c1', None,
+                [('Name', cname), ('Code', ckey)]),
+            ('Wirtschaftsjahr', 'wija', 'dropdown', '05', 'company',
+                [('Jahr', wija)]),
+            ('Codes', 'codes', 'dropdown', None, 'wija',
+                [('Code', codes)]),
+            (u"Dre\xf6ksau 'bl\xf6sepp'", None, 'warning', None, None, [])]
+
+  dlg = InputDialog ('foobar', fields, attrs)
+  dlg.run ()
+
   curses.endwin ()
 
-  print "RESULT:", r
+  print "RES:", dlg.inputData





reply via email to

[Prev in Thread] Current Thread [Next in Thread]