commit-gnue
[Top][All Lists]
Advanced

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

[gnue] r9164 - trunk/gnue-forms/src/uidrivers/curses/widgets


From: johannes
Subject: [gnue] r9164 - trunk/gnue-forms/src/uidrivers/curses/widgets
Date: Thu, 14 Dec 2006 09:40:13 -0600 (CST)

Author: johannes
Date: 2006-12-14 09:40:12 -0600 (Thu, 14 Dec 2006)
New Revision: 9164

Modified:
   trunk/gnue-forms/src/uidrivers/curses/widgets/_base.py
   trunk/gnue-forms/src/uidrivers/curses/widgets/entry.py
Log:
Started refactoring curses entry widgets (not done yet)


Modified: trunk/gnue-forms/src/uidrivers/curses/widgets/_base.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/widgets/_base.py      2006-12-13 
09:59:22 UTC (rev 9163)
+++ trunk/gnue-forms/src/uidrivers/curses/widgets/_base.py      2006-12-14 
15:40:12 UTC (rev 9164)
@@ -38,6 +38,21 @@
     """
 
     # -------------------------------------------------------------------------
+    # Constructor
+    # -------------------------------------------------------------------------
+
+    def __init__(self, event):
+
+        UIWidget.__init__(self, event)
+        self.__focus_index = None
+
+        self.top = 0
+        self.left = 0
+        self.width = 0
+        self.height = 0
+
+
+    # -------------------------------------------------------------------------
     # Create a widget
     # -------------------------------------------------------------------------
 
@@ -46,12 +61,6 @@
         Initialize the coordinates of a widget.  These coordinates will be set
         later using set_size_and_fit().
         """
-
-        self.top = 0
-        self.left = 0
-        self.width = 0
-        self.height = 0
-
         self._parent = event.container
 
 
@@ -78,6 +87,17 @@
         """ Returns wether the container is ready for output or not """
         return self._container_is_ready_()
 
+
+    # -------------------------------------------------------------------------
+    # Call a method of a given widget (if it exists)
+    # -------------------------------------------------------------------------
+
+    def _call_widget_(self, index, method, *args, **kwargs):
+
+        func = getattr(self.widgets[index], method, None)
+        if func:
+            func(*args, **kwargs)
+
     
     # -------------------------------------------------------------------------
     # Focus handling
@@ -86,19 +106,21 @@
     def _ui_set_focus_(self, index):
 
         self._uiDriver._focus_widget = self
-        self._get_focus(index)
+        self.__focus_index = index
+        self._call_widget_(index, '_ui_set_focus_')
 
     # -------------------------------------------------------------------------
 
     def _ui_focus_in_(self, index):
 
-        pass
+        self._call_widget_(index, '_ui_focus_in_')
 
     # -------------------------------------------------------------------------
 
     def _ui_focus_out_(self, index):
 
-        self._lose_focus(index)
+        self._call_widget_(index, '_ui_focus_out_')
+        self.__focus_index = None
 
 
     # -------------------------------------------------------------------------
@@ -121,6 +143,44 @@
 
 
     # -------------------------------------------------------------------------
+    # Keypress
+    # -------------------------------------------------------------------------
+
+    def _keypress(self, key):
+
+        if self.__focus_index is not None:
+            widget = self.widgets[self.__focus_index]
+
+            # If the widget has an implementation for a keypress handler, let
+            # it try to handle the key
+            func = getattr(widget, '_keypress', None)
+            if func and func(key):
+                return
+
+        UIWidget._keypress(self, key)
+
+
+    # -------------------------------------------------------------------------
+    # Function keypress
+    # -------------------------------------------------------------------------
+
+    def _fkeypress(self, key, shift, ctrl, meta):
+
+        if self.__focus_index is not None:
+            widget = self.widgets[self.__focus_index]
+
+            # If the widget has an implementation for a keypress handler, let
+            # it try to handle the key
+            func = getattr(widget, '_fkeypress', None)
+            if func and func(key, shift, ctrl, meta):
+                return
+
+        UIWidget._fkeypress(self, key, shift, ctrl, meta)
+
+
+
+
+    # -------------------------------------------------------------------------
     # Get the minimum size of a widget
     # -------------------------------------------------------------------------
 

Modified: trunk/gnue-forms/src/uidrivers/curses/widgets/entry.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/widgets/entry.py      2006-12-13 
09:59:22 UTC (rev 9163)
+++ trunk/gnue-forms/src/uidrivers/curses/widgets/entry.py      2006-12-14 
15:40:12 UTC (rev 9164)
@@ -23,7 +23,7 @@
 
 import curses
 
-from _base import UIHelper
+from gnue.forms.uidrivers.curses.widgets import _base
 
 __all__ = ['UIEntry']
 
@@ -31,119 +31,96 @@
 # Entry class
 # =============================================================================
 
-class UIEntry(UIHelper):
+class UIEntry(_base.UIHelper):
     """
     Interface implementation for <entry> widgets.
     """
 
     # -------------------------------------------------------------------------
-    # Initialization
+    # Widget creation
     # -------------------------------------------------------------------------
 
-    def __init__(self, event):
+    def _create_widget_(self, event, spacer):
 
-        UIHelper.__init__(self, event)
+        _base.UIHelper._create_widget_(self, event, spacer)
+        
+        style = self._gfObject.style.lower()
+        build = getattr(self, '_UIEntry__build_%s' % style, None)
+        if not build:
+            build = self.__build_default
 
-        self.__style = self._gfObject.style.lower()
-        self.__value = {}
-        self.__offset = {}
-        self.__selection = {}
-        self.__enabled = {}
-        self.__voffset = {}
+        (self.label, self.widget) = build(spacer * (self._gfObject._gap + 1))
+        return self.widget
 
-        self.__is_multiline = (self.__style == 'multiline')
-        self.__focusIndex = None
 
-        self.__cursor = (0, 0)
+    # -------------------------------------------------------------------------
+    # Create the various entry widgets
+    # -------------------------------------------------------------------------
 
-        # additional indices for listboxes
-        self.__index  = {}
-        self.__pindex = {}
-        self.__oldCursor = 1
-        self.__choices = []
+    def __build_default(self, row_offset, password=False):
 
-        if self.__style == 'checkbox':
-            self.__setCursor(1, 0)
+        result = TextEntry(self, row_offset, password)
+        return (None, result)
 
     # -------------------------------------------------------------------------
-    # Initialization per row
-    # -------------------------------------------------------------------------
 
-    def _create_widget_(self, event, spacer):
+    def __build_password(self, row_offset):
 
-        UIHelper._create_widget_(self, event, spacer)
-        
-        self.__value[spacer] = None
-        self.__selection[spacer] = None
-        self.__enabled[spacer] = True
-        self.__offset[spacer]  = 0
-        self.__voffset[spacer] = 0
-        self.__index[spacer]   = 0
-        self.__pindex[spacer]  = 1
+        return self.__build_default(row_offset, True)
 
-
     # -------------------------------------------------------------------------
-    # Enable/disable this entry
-    # -------------------------------------------------------------------------
 
-    def _ui_enable_(self, index):
-        self.__enabled[index] = True
-        self.__repaint(index)
+    def __build_dropdown(self, row_offset):
 
+        result = DropDownEntry(self, row_offset)
+        return (None, result)
+
     # -------------------------------------------------------------------------
 
-    def _ui_disable_(self, index):
-        self.__enabled[index] = False
-        self.__repaint(index)
+    def __build_checkbox(self, row_offset):
 
+        result = Checkbox(self, row_offset)
+        return (None, result)
+
     # -------------------------------------------------------------------------
-    # Focus has changed to this entry
-    # -------------------------------------------------------------------------
 
-    def _get_focus(self, index):
+    def __build_label(self, row_offset):
 
-        self.__focusIndex = index
+        result = StaticText(self, row_offset)
+        return (None, result)
 
-        self.__offset[index]  = 0
-        self.__voffset[index] = 0
-        self.__index[index]   = 0
-        self.__pindex[index]  = 1
+    # -------------------------------------------------------------------------
 
-        if self.__style == 'listbox':
-            self.__oldCursor = curses.curs_set(0)
+    def __build_listbox(self, row_offset):
 
-        self.__update_cursor_position()
-        self.__repaint(index)
+        result = ListBoxEntry(self, row_offset)
+        return (None, result)
 
 
+
     # -------------------------------------------------------------------------
-    # Focus has changed away from this entry
+    # Enable/disable this entry
     # -------------------------------------------------------------------------
 
-    def _lose_focus(self, index):
+    def _ui_enable_(self, index):
 
-        if self.__style == 'listbox':
-            curses.curs_set(self.__oldCursor)
+        self.widgets[index]._ui_enable_()
 
-        self.__focusIndex = None
-        self.__selection[index] = None
+    # -------------------------------------------------------------------------
 
-        self.__repaint(index)
+    def _ui_disable_(self, index):
 
+        self.widgets[index]._ui_disable_()
+
+
     # -------------------------------------------------------------------------
     # Set value for entry
     # -------------------------------------------------------------------------
 
     def _ui_set_value_(self, index, value):
 
-        if self.__style == 'listbox' and value in self.__choices:
-            if self.__value[index] <> value:
-                self.__index[index]  = self.__choices.index(value)
-                self.__pindex[index] = 1
-                self.__offset[index] = self.__index[index]
+        self._call_widget_(index, '_ui_set_value_', value)
 
-        self.__value[index] = value
-        self.__repaint(index)
 
     # -------------------------------------------------------------------------
     # Update the list of choices
@@ -151,354 +128,564 @@
 
     def _ui_set_choices_(self, index, choices):
 
-        self.__choices = choices[:]
+        self._call_widget_(index, '_ui_set_choices_', choices)
 
+
     # -------------------------------------------------------------------------
     # Set cursor position
     # -------------------------------------------------------------------------
 
     def _ui_set_cursor_position_(self, index, position):
 
-        if not self.ready():
-            return
+        self._call_widget_(index, '_ui_set_cursor_position_', position)
 
-        old_selection = self.__selection[index]
-        self.__selection[index] = None
-        need_repaint = old_selection is not None
 
-        assert gDebug(2, "----- new position: %s" % position)
-        assert gDebug(2, "      Value       : %r" % self.__value[index])
-        assert gDebug(2, "      Curr.Offset : %s/%s" % (self.__offset[index],
-          self.__voffset[index]))
+    # -------------------------------------------------------------------------
+    # Set start and end of selection area
+    # -------------------------------------------------------------------------
 
-        if self.__style in ['checkbox', 'listbox']:
-            return
+    def _ui_set_selected_area_(self, index, selection1, selection2):
 
-        if self.__is_multiline:
-            if position == 0 or self.__value[index] is None:
-                self.__offset[index]  = 0
-                self.__voffset[index] = 0
-                self.__repaint(index)
-                self.__setCursor(0, 0)
-                return
+        self._call_widget_(index, '_ui_set_selected_area_', selection1,
+                selection2)
 
-            # Grab the text portion, which is everything up to position. If the
-            # last character is a newline, we can remove this, since it would
-            # result in a wrong vertical offset
-            value = self.__value[index][:position+1]
-            vcorr = [0, 1][value[-1] == '\n']
+    # -------------------------------------------------------------------------
+    # Clipboard and selection
+    # -------------------------------------------------------------------------
 
-            assert gDebug(2, "      Cut-Part    : %r" % value)
-            assert gDebug(2, "      Vert.Correct: %s" % vcorr)
+    def _ui_cut_(self, index):
+        
+        self._call_widget_(index, '_ui_cut_')
 
-            # Get the vertical cursor-position and offset
-            cpVertical  = value.count('\n') - self.__voffset[index] - vcorr
+    # -------------------------------------------------------------------------
 
-            if cpVertical < 0:
-                # the position is not visible right now, so we need to scroll
-                # up (which means changing the voffset). We set the
-                # cursor-position to line 0.
-                self.__voffset[index] += cpVertical
-                cpVertical  = 1
-                need_repaint = True
+    def _ui_copy_(self, index):
 
-            elif cpVertical >= self.height:
-                # the position is not visible right now, so we need to scroll
-                # down (which means changing the voffset).
-                self.__voffset[index] = value.count('\n') - \
-                        self.height + 1 - vcorr
-                cpVertical  = self.height            # Pos. are Zero-based
-                need_repaint = True
+        self._call_widget_(index, '_ui_copy_')
 
-            # Now, after having a valid row, we need to determine the 
horizontal
-            # offset based on the last line holding our requested position.
-            currentLine = value.splitlines()[-1]
-            cpHorizontal = len(currentLine) - self.__offset[index] - 1
+    # -------------------------------------------------------------------------
 
-            if cpHorizontal < 0:
-                # Beyond left margin, so scroll to the left
-                self.__offset[index] += cpHorizontal
-                cpHorizontal = 0
-                need_repaint  = True
+    def _ui_paste_(self, index):
+        
+        self._call_widget_(index, '_ui_paste_')
 
-            elif cpHorizontal > self.width:
-                self.__offset[index] = len(currentLine) - self.width
-                cpHorizontal = self.width
-                need_repaint  = True
+    # -------------------------------------------------------------------------
 
-            if need_repaint:
-                self.__repaint(index)
+    def _ui_select_all_(self, index):
+        
+        self._call_widget_(index, '_ui_select_all_')
 
-            cpHorizontal = min(cpHorizontal, self.width)
-            cpHorizontal = max(cpHorizontal, 0)
-            cpVertical   = min(cpVertical, self.height - 1)
-            cpVertical   = max(cpVertical, 0)
 
-            assert gDebug(2, "H/V: %s/%s - Offsets %s/%s" % (cpHorizontal,
-                cpVertical, self.__offset[index], self.__voffset[index]))
+    # -------------------------------------------------------------------------
+    # 
+    # -------------------------------------------------------------------------
 
-            self.__setCursor(cpHorizontal, cpVertical)
+    def set_size_and_fit(self, width, height):
 
+        self.width = width
+        self.height = height
 
+        for widget in self.widgets:
+            widget._repaint_()
+
+
+    # -------------------------------------------------------------------------
+    # Get the size hints for an entry
+    # -------------------------------------------------------------------------
+
+    def get_size_hints(self, vertical=None):
+
+        label = ''
+        if self._gfObject.style != 'checkbox':
+            label = getattr(self._gfObject, 'label', '')
+
+        # Only stretch entries if they're in a horizontal container or if they
+        # are multiline edits
+        if not vertical or self._gfObject.style == 'multiline':
+            stretch = self.stretch
         else:
-            if self.__style != ['checkbox']:
-                npos = position - self.__offset[index]
-                if npos > self.width:
-                    self.__offset[index] = position - self.width
-                    npos = self.width
-                    self.__repaint(index)
-                    need_repaint = False
+            stretch = 0
 
-                elif npos < 0:
-                    self.__offset[index] += npos
-                    npos = 0
-                    self.__repaint(index)
-                    need_repaint = False
+        return (self.min_width or 20, self.min_height or 1, len(label), 
stretch)
 
-                position = npos
 
-            if need_repaint:
-                self.__repaint(index)
+# =============================================================================
+# Base Entry Widget
+# =============================================================================
 
-            self.__setCursor(position, 0)
+class BaseEntry(object):
+    """
+    The base class for widget implementations
+    """
 
-
     # -------------------------------------------------------------------------
-    # Set start and end of selection area
+    # Constructor
     # -------------------------------------------------------------------------
 
-    def _ui_set_selected_area_(self, index, selection1, selection2):
+    def __init__(self, entry, row_offset):
 
-        if selection1 == selection2:
-            self.__selection[index] = None
-        else:
-            self.__selection[index] = (selection1, selection2)
+        self.entry = entry
+        self.row_offset = row_offset
+        self.value = None
+        self.choices = []
+        self.has_focus = False
+        self.enabled = True
 
-        self.__repaint(index)
-        gDebug(2, "Set-Selection: %s %s %s" % (index, selection1, selection2))
 
     # -------------------------------------------------------------------------
-    # Clipboard and selection
+    # Virtual Methods
     # -------------------------------------------------------------------------
 
-    def _ui_cut_(self, index):
+    def _repaint_(self):
+        """
+        Descendants must implement this method to draw the widget onto it's
+        parent
+        """
         pass
 
+
     # -------------------------------------------------------------------------
+    # Event Handler
+    # -------------------------------------------------------------------------
 
-    def _ui_copy_(self, index):
-        pass
+    def _ui_set_value_(self, value):
 
+        self.value = value
+        self._repaint_()
+
     # -------------------------------------------------------------------------
 
-    def _ui_paste_(self, index):
-        pass
+    def _ui_set_choices_(self, choices):
 
+        self.choices = choices
+        self._repaint_()
+
     # -------------------------------------------------------------------------
 
-    def _ui_select_all_(self, index):
-        pass
+    def _ui_focus_in_(self):
 
+        self.has_focus = True
+        self._repaint_()
+
     # -------------------------------------------------------------------------
-    # Update entry representation on screen
+
+    def _ui_focus_out_(self):
+
+        self.has_focus = False
+        self._repaint_()
+
     # -------------------------------------------------------------------------
 
-    def __repaint(self, index):
+    def _ui_enable_(self):
 
-        if not self.ready():
-            return
+        self.enabled = True
+        self._repaint_()
 
-        if self.__style == 'listbox':
-            # First draw the visible items of the listbox
-            offset = self.__offset[index]
-            lines  = self.__choices[offset:offset + self.height]
+    # -------------------------------------------------------------------------
 
-            for (line, value) in enumerate(lines):
-                text = value.ljust(self.width)[:self.width]
-                attr = self.__get_attr(index, (line == self.__pindex[index]-1))
-                # Note: this is not safe if there's a gap !
-                self._set_text(index+line, text, attr)
+    def _ui_disable_(self):
 
-            self._parent.move(self.left, self.top + self.__pindex[index] - 1)
+        self.enabled = False
+        self._repaint_()
 
-        elif self.__is_multiline:
-            # Create all visible, empty lines
-            data    = [''.ljust(self.width)] * self.height
-            hOffset = self.__offset[index]
-            vOffset = self.__voffset[index]
 
-            # Overwrite these empty lines with the data as stated by v/h-Offset
-            if self.__value[index]:
-                add = 
self.__value[index].splitlines()[vOffset:vOffset+self.height]
-                for (ix, text) in enumerate(add):
-                    text = text[hOffset:hOffset + self.width]
-                    data[ix] = text.ljust(self.width)[:self.width]
+    # -------------------------------------------------------------------------
+    # Properties
+    # -------------------------------------------------------------------------
 
-            attr = self.__get_attr(index)
+    def __get_top(self):
+        return self.entry.top + self.row_offset
 
-            # And write everything to screen
-            for (ix, text) in enumerate(data):
-                self._set_text(index + ix, text, attr, self.__selection[index])
+    top = property(__get_top, None, None, """ Get the upper left row """)
 
-        else:
-            value  = self.__value[index]
-            offset = self.__offset[index]
-            text = ''
+    # -------------------------------------------------------------------------
 
-            if self.__style in['default', 'label', 'dropdown']:
-                text = value or ''
-                text = text[offset:offset + self.width]
-                text += ' ' * (self.width - len(text))
+    def __get_left(self):
+        return self.entry.left
 
-            elif self.__style == 'password':
-                text = '*' * len(value or '')
-                text = text[offset:offset + self.width]
-                text += ' ' * (self.width - len(text))
+    left = property(__get_left, None, None, """ The upper left colum """)
 
-            elif self.__style == 'checkbox':
-                attr = self._uiDriver.attr['background']
-                self._set_text(index, '    %s' % self._gfObject.label, attr)
+    # -------------------------------------------------------------------------
 
-                if self.__value[index] is None:
-                    text = '[-]'
-                elif self.__value[index]:
-                    text = '[X]'
-                else:
-                    text = '[ ]'
+    def __get_width(self):
+        return self.entry.width
 
-            else:
-                gDebug(2, "STYLE==%s" % self.__style)
+    width = property(__get_width, None, None, """ The widget width """)
 
-            attr = self.__get_attr(index)
-            self._set_text(index, text, attr, self.__selection[index])
+    # -------------------------------------------------------------------------
 
+    def __get_height(self):
+        return self.entry.height
 
+    height = property(__get_height, None, None, """ The widget height """)
+
     # -------------------------------------------------------------------------
-    # Get the current screen attributes to be used
-    # -------------------------------------------------------------------------
 
-    def __get_attr(self, index, selected=False):
+    def __get_attr(self):
 
-        add = 0
-        if self.__style == 'label':
-            attr = 'background'
-        elif not self.__enabled[index]:
+        if not self.enabled:
             attr = 'disabled'
 
-        elif index == self.__focusIndex:
+        elif self.has_focus:
             attr = 'focusentry'
-            if self.__style == 'listbox' and selected:
-                add = curses.A_STANDOUT
         else:
             attr = 'entry'
 
-        return self._uiDriver.attr[attr] + add
+        return self.entry._uiDriver.attr[attr]
 
+    attribute = property(__get_attr, None, None,
+            """ The current attribute to use for this widget """)
 
+# =============================================================================
+# Static Text style entry
+# =============================================================================
+
+class StaticText(BaseEntry):
+    """
+    A static text entry is an read-only entry.
+    """
+
     # -------------------------------------------------------------------------
-    # handle keypress
+    # Draw the static text entry
     # -------------------------------------------------------------------------
 
-    def _keypress(self, key):
+    def _repaint_(self):
 
-        if self.__style == 'dropdown' and key == chr(self._uiDriver.lookupKey):
-            mapping = {}
-            for item in self.__choices:
-                mapping[item] = item
-            res = self._uiDriver.getOption(u_("Select option"), mapping)
-            if res is not None:
-                self._request('REPLACEVALUE', text = res)
+        text = ("%s" % self.value)[:self.width]
+        text += ' ' * (self.width - len(text))
+        self.entry._parent.write(self.left, self.top, text,
+                self.entry._uiDriver.attr['background'])
+
+
+
+# =============================================================================
+# Single line text entry
+# =============================================================================
+
+class TextEntry(BaseEntry):
+    """
+    Entry widget implementing single line text or password entries.
+    """
+
+    # -------------------------------------------------------------------------
+    # Constructor
+    # -------------------------------------------------------------------------
+
+    def __init__(self, entry, row_offset, password=False):
+
+        BaseEntry.__init__(self, entry, row_offset)
+        self.password = password
+        self.selection = None
+        self.offset = 0
+
+
+    # -------------------------------------------------------------------------
+    # UI event handler
+    # -------------------------------------------------------------------------
+
+    def _ui_set_cursor_position_(self, position):
+        """
+        Set the cursor position.  An already existing selection gets
+        unselected.
+        """
+
+        # If there is a selection atm, we have to unselected it.  Thus we need
+        # a repaint
+        need_repaint = self.selection is not None
+        self.selection = None
+
+        if not self.entry.ready():
+            return
+
+        pos = position - self.offset
+        if pos > self.width-1:
+            self.offset = position - self.width + 1
+            pos = self.width-1
+            need_repaint = True
+
+        elif pos < 0:
+            self.offset += pos
+            pos = 0
+            need_repaint = True
+
+        if need_repaint:
+            self._repaint_()
+
+        if self.has_focus:
+            self.entry._parent.move(self.left + pos, self.top)
+
+
+    # -------------------------------------------------------------------------
+
+    def _ui_set_selected_area_(self, selection1, selection2):
+        """
+        Set the current selection and repaint the widget
+        """
+
+        if selection1 == selection2:
+            self.selection = None
         else:
+            self.selection = (selection1, selection2)
+        self._repaint_()
+        
 
-            sel = self.__selection[self.__focusIndex]
-            assert gDebug(2, "SELECTION: %s" % repr(sel))
-            UIHelper._keypress(self, key)
+    # -------------------------------------------------------------------------
 
+    def _ui_focus_out_(self):
+        """
+        On leaving a widget make sure to remove a selection as well as reset
+        the offset.
+        """
 
+        self.selection = None
+        self.offset = 0
+
+        BaseEntry._ui_focus_out_(self)
+
+
+
     # -------------------------------------------------------------------------
-    # handle function keypress
+    # Implementation Virtual methods
     # -------------------------------------------------------------------------
 
-    def _fkeypress(self, key, shift, ctrl, meta):
+    def _repaint_(self):
+        """
+        Draw the text entry widget
+        """
 
-        if self.__style == 'listbox' and key in[curses.KEY_DOWN, 
curses.KEY_UP]:
-            self.__move([1, -1][key == curses.KEY_UP])
+        if self.password:
+            vtext = '*' * len(self.value or '')
         else:
-            UIHelper._fkeypress(self, key, shift, ctrl, meta)
+            vtext = self.value or ''
+        text = vtext[self.offset:self.offset + self.width]
+        text += ' ' * (self.width - len(text))
 
+        if self.selection:
+            (sel1, sel2) = self.selection
+            self.entry._parent.write(self.left, self.top, text[:sel1],
+                    self.attribute)
+            self.entry._parent.write(self.left + sel1, self.top,
+                    text[sel1:sel2], self.attribute + curses.A_STANDOUT)
+            self.entry._parent.write(self.left + sel2, self.top, text[sel2:],
+                    self.attribute)
+        else:
+            self.entry._parent.write(self.left, self.top, text, self.attribute)
 
+
+# =============================================================================
+# Drodown Entry
+# =============================================================================
+
+class DropDownEntry(TextEntry):
+    """
+    A dropdown entry extends a single line text entry since it has a list of
+    allowed values from which the user can choose one.
+    """
+
     # -------------------------------------------------------------------------
+    # Constructor
+    # -------------------------------------------------------------------------
 
-    def __move(self, direction):
+    def __init__(self, entry, row_offset):
 
-        index = self.__focusIndex
-        self.__pindex[index] += direction
-        self.__index[index] += direction
+        self.__mapping = {}
+        TextEntry.__init__(self, entry, row_offset)
 
-        if self.__pindex[index] > self.height:
-            if self.__index[index] < len(self.__choices):
-                self.__offset[index] += direction
 
-        elif self.__pindex[index] < 1:
-            self.__offset[index] = max(0, self.__offset[index] - 1)
+    # -------------------------------------------------------------------------
+    # UI Event handler
+    # -------------------------------------------------------------------------
 
-        self.__index[index] = max(0, self.__index[index])
-        self.__index[index] = min(len(self.__choices) - 1, self.__index[index])
+    def _ui_set_choices_(self, choices):
 
-        self.__pindex[index] = max(1, self.__pindex[index])
-        self.__pindex[index] = min(self.__pindex[index], self.height)
+        TextEntry._ui_set_choices_(self, choices)
 
-        self.__value[index] = self.__choices[self.__index[index]]
-        self._request('REPLACEVALUE', text = self.__value[index])
+        self.__mapping = {}
+        for item in self.choices:
+            self.__mapping[item] = item
 
+
     # -------------------------------------------------------------------------
-    # Set cursor position for widget
+    # Keypress handler
     # -------------------------------------------------------------------------
 
-    def __setCursor(self, x, y):
+    def _keypress(self, key):
+        """
+        If the key is the lookup key a selection dialog will be displayed.  If
+        the user selects an option a REPLACEVALUE event is generated.
+        Otherwise the key gets passed back and it should be handled by the
+        entry itself.
 
-        self.__cursor = (x, y)
-        self.__update_cursor_position()
+        @returns: True if the key event has been handled, False otherwise.
+        """
 
+        if key == chr(self.entry._uiDriver.lookupKey):
+            # We can handle the lookup key here
+            choice = self.entry._uiDriver.getOption(u_("Select option"),
+                    self.__mapping)
+            if choice is not None:
+                self.entry._request('REPLACEVALUE', text=choice)
+
+            result = True
+        else:
+            result = False
+
+        return result
+
+
+# =============================================================================
+# Checkbox widget
+# =============================================================================
+
+class Checkbox(BaseEntry):
+
     # -------------------------------------------------------------------------
-    # Update cursor position
+    # UI event handler
     # -------------------------------------------------------------------------
 
-    def __update_cursor_position(self):
+    def _ui_focus_in_(self):
 
-        if self.__focusIndex is not None:
-            (x, y) = self.__cursor
-            self._parent.move(self.left + x, self.top + self.__focusIndex + y)
+        BaseEntry._ui_focus_in_(self)
+        self.entry._parent.move(self.left + 1, self.top)
 
 
-    def set_size_and_fit(self, width, height):
+    # -------------------------------------------------------------------------
+    # Draw the checkbox in it's current state
+    # -------------------------------------------------------------------------
 
-        self.width = width
-        self.height = height
+    def _repaint_(self):
 
-        for index in range(len(self.widgets)):
-            self.__repaint(index)
+        label = '    %s' % self.entry._gfObject.label
+        text = label[:self.width]
+        text += ' ' * (self.width - len(text))
+        self.entry._parent.write(self.left, self.top, text,
+                self.entry._uiDriver.attr['background'])
 
+        if self.value is None:
+            text = '[-]'
+        elif self.value:
+            text = '[X]'
+        else:
+            text = '[ ]'
+
+        self.entry._parent.write(self.left, self.top, text, self.attribute)
+
+
+# =============================================================================
+# A ListBox entry
+# =============================================================================
+
+class ListBoxEntry(BaseEntry):
+
     # -------------------------------------------------------------------------
-    # Get the size hints for an entry
+    # Constructor
     # -------------------------------------------------------------------------
 
-    def get_size_hints(self, vertical=None):
+    def __init__(self, entry, row_offset):
 
-        label = ''
-        if self.__style != 'checkbox':
-            label = getattr(self._gfObject, 'label', '')
+        BaseEntry.__init__(self, entry, row_offset)
+        self.offset = 0
+        self.selected = None
+        self.display = None
+        self.__old_cursor = None
 
-        # Only stretch entries if they're in a horizontal container or if they
-        # are multiline edits
-        if not vertical or self.__is_multiline:
-            stretch = self.stretch
+
+    # -------------------------------------------------------------------------
+    # UI event handler
+    # -------------------------------------------------------------------------
+
+    def _ui_set_value_(self, value):
+
+        if value <> self.value:
+            # If the value has changed from outside (not due to a call of our
+            # __move method), we start off with a fresh display-offset.  This
+            # means the newly set value will be the first row displayed in the
+            # listbox widget.
+            if value in self.choices:
+                self.selected = self.offset = self.choices.index(value)
+            else:
+                self.selected = self.offset = 0
+
+            self.display = 1
+
+        BaseEntry._ui_set_value_(self, value)
+
+
+    # -------------------------------------------------------------------------
+
+    def _ui_focus_in_(self):
+
+        self.__old_cursor = curses.curs_set(0)
+        BaseEntry._ui_focus_in_(self)
+
+    # -------------------------------------------------------------------------
+
+    def _ui_focus_out_(self):
+
+        curses.curs_set(self.__old_cursor)
+        BaseEntry._ui_focus_out_(self)
+
+
+    # -------------------------------------------------------------------------
+    # Draw the listbox widget
+    # -------------------------------------------------------------------------
+
+    def _repaint_(self):
+        
+        lines = self.choices[self.offset:self.offset + self.height]
+        if len(lines) < self.height:
+            lines.extend([''] * (self.height - len(lines)))
+
+        for (row, value) in enumerate(lines):
+            text = value.ljust(self.width)[:self.width]
+            attr = self.attribute
+            if self.has_focus and row == self.display-1:
+                attr += curses.A_STANDOUT
+
+            self.entry._parent.write(self.left, self.top + row, text, attr)
+
+
+    # -------------------------------------------------------------------------
+    # Handle the up- and down-keys here
+    # -------------------------------------------------------------------------
+
+    def _fkeypress(self, key, shift, ctrl, meta):
+
+        if key in [curses.KEY_UP, curses.KEY_DOWN]:
+            self.__move([1, -1][key == curses.KEY_UP])
+            return True
         else:
-            stretch = 0
+            return False
 
-        return (self.min_width or 20, self.min_height or 1, len(label), 
stretch)
 
+    # -------------------------------------------------------------------------
+    # Select the prior/next element of the listbox
+    # -------------------------------------------------------------------------
 
+    def __move(self, direction):
+
+        self.display += direction
+        self.selected += direction
+
+        if self.display > self.height:
+            if self.selected < len(self.choices):
+                self.offset += direction
+
+        elif self.display < 1:
+            self.offset = max(0, self.offset - 1)
+
+        self.selected = max(0, self.selected)
+        self.selected = min(len(self.choices) - 1, self.selected)
+
+        self.display = max(1, self.display)
+        self.display = min(self.display, self.height)
+
+        # NOTE: in order to keep the display-index accross _ui_set_value_ calls
+        # we set it manually here.
+        self.value = self.choices[self.selected]
+        self.entry._request('REPLACEVALUE', text=self.value)
+
+
 # =============================================================================
 # Configuration data
 # =============================================================================
@@ -507,4 +694,4 @@
   'baseClass'  : UIEntry,
   'provides'   : 'GFEntry',
   'container'  : 0,
-  }
+}





reply via email to

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