]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/mixins/listctrl.py
Fix "warning: operation on 'y' may be undefined".
[wxWidgets.git] / wxPython / wx / lib / mixins / listctrl.py
index 77e49112de0f40f8e06c2a3c3d1a9dbbeaf1ef27..2de4d3bbca64a9bdb3e51ca7f197f20f679677b5 100644 (file)
 # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
 #
 # o wxColumnSorterMixin -> ColumnSorterMixin 
 # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)
 #
 # o wxColumnSorterMixin -> ColumnSorterMixin 
-# o wxListCtrlAutoWidthMixin -> ListCtrlAutoWidthMixin 
+# o wxListCtrlAutoWidthMixin -> ListCtrlAutoWidthMixin
+# ...
+# 13/10/2004 - Pim Van Heuven (pim@think-wize.com)
+# o wxTextEditMixin: Support Horizontal scrolling when TAB is pressed on long
+#       ListCtrls, support for WXK_DOWN, WXK_UP, performance improvements on
+#       very long ListCtrls, Support for virtual ListCtrls
+#
+# 15-Oct-2004 - Robin Dunn
+# o wxTextEditMixin: Added Shift-TAB support
 #
 
 import  locale
 #
 
 import  locale
@@ -54,7 +62,7 @@ class ColumnSorterMixin:
         list = self.GetListCtrl()
         if not list:
             raise ValueError, "No wx.ListCtrl available"
         list = self.GetListCtrl()
         if not list:
             raise ValueError, "No wx.ListCtrl available"
-        self.Bind(wx.EVT_LIST_COL_CLICK, self.__OnColClick, list)
+        list.Bind(wx.EVT_LIST_COL_CLICK, self.__OnColClick, list)
 
 
     def SetColumnCount(self, newNumColumns):
 
 
     def SetColumnCount(self, newNumColumns):
@@ -168,12 +176,25 @@ class ListCtrlAutoWidthMixin:
     def __init__(self):
         """ Standard initialiser.
         """
     def __init__(self):
         """ Standard initialiser.
         """
-        self._lastColMinWidth = None
-
+        self._resizeColMinWidth = None
+        self._resizeColStyle = "LAST"
+        self._resizeCol = 0
         self.Bind(wx.EVT_SIZE, self._onResize)
         self.Bind(wx.EVT_LIST_COL_END_DRAG, self._onResize, self)
 
 
         self.Bind(wx.EVT_SIZE, self._onResize)
         self.Bind(wx.EVT_LIST_COL_END_DRAG, self._onResize, self)
 
 
+    def setResizeColumn(self, col):
+        """
+        Specify which column that should be autosized.  Pass either
+        'LAST' or the column number.  Default is 'LAST'.
+        """
+        if col == "LAST":
+            self._resizeColStyle = "LAST"
+        else:
+            self._resizeColStyle = "COL"
+            self._resizeCol = col
+        
+
     def resizeLastColumn(self, minWidth):
         """ Resize the last column appropriately.
 
     def resizeLastColumn(self, minWidth):
         """ Resize the last column appropriately.
 
@@ -188,8 +209,13 @@ class ListCtrlAutoWidthMixin:
 
             'minWidth' is the preferred minimum width for the last column.
         """
 
             'minWidth' is the preferred minimum width for the last column.
         """
-        self._lastColMinWidth = minWidth
+        self.resizeColumn(minWidth)
+
+
+    def resizeColumn(self, minWidth):
+        self._resizeColMinWidth = minWidth
         self._doResize()
         self._doResize()
+        
 
     # =====================
     # == Private Methods ==
 
     # =====================
     # == Private Methods ==
@@ -200,7 +226,10 @@ class ListCtrlAutoWidthMixin:
 
             We automatically resize the last column in the list.
         """
 
             We automatically resize the last column in the list.
         """
-        wx.CallAfter(self._doResize)
+        if 'gtk2' in wx.PlatformInfo:
+            self._doResize()
+        else:
+            wx.CallAfter(self._doResize)
         event.Skip()
 
 
         event.Skip()
 
 
@@ -223,8 +252,13 @@ class ListCtrlAutoWidthMixin:
         numCols = self.GetColumnCount()
         if numCols == 0: return # Nothing to resize.
 
         numCols = self.GetColumnCount()
         if numCols == 0: return # Nothing to resize.
 
-        if self._lastColMinWidth == None:
-            self._lastColMinWidth = self.GetColumnWidth(numCols - 1)
+        if(self._resizeColStyle == "LAST"):
+            resizeCol = self.GetColumnCount()
+        else:
+            resizeCol = self._resizeCol
+
+        if self._resizeColMinWidth == None:
+            self._resizeColMinWidth = self.GetColumnWidth(resizeCol - 1)
 
         # We're showing the vertical scrollbar -> allow for scrollbar width
         # NOTE: on GTK, the scrollbar is included in the client size, but on
 
         # We're showing the vertical scrollbar -> allow for scrollbar width
         # NOTE: on GTK, the scrollbar is included in the client size, but on
@@ -236,21 +270,23 @@ class ListCtrlAutoWidthMixin:
                 listWidth = listWidth - scrollWidth
 
         totColWidth = 0 # Width of all columns except last one.
                 listWidth = listWidth - scrollWidth
 
         totColWidth = 0 # Width of all columns except last one.
-        for col in range(numCols-1):
-            totColWidth = totColWidth + self.GetColumnWidth(col)
+        for col in range(numCols):
+            if col != (resizeCol-1):
+                totColWidth = totColWidth + self.GetColumnWidth(col)
 
 
-        lastColWidth = self.GetColumnWidth(numCols - 1)
+        resizeColWidth = self.GetColumnWidth(resizeCol - 1)
 
 
-        if totColWidth + self._lastColMinWidth > listWidth:
+        if totColWidth + self._resizeColMinWidth > listWidth:
             # We haven't got the width to show the last column at its minimum
             # width -> set it to its minimum width and allow the horizontal
             # scrollbar to show.
             # We haven't got the width to show the last column at its minimum
             # width -> set it to its minimum width and allow the horizontal
             # scrollbar to show.
-            self.SetColumnWidth(numCols-1, self._lastColMinWidth)
+            self.SetColumnWidth(resizeCol-1, self._resizeColMinWidth)
             return
 
         # Resize the last column to take up the remaining available space.
 
             return
 
         # Resize the last column to take up the remaining available space.
 
-        self.SetColumnWidth(numCols-1, listWidth - totColWidth)
+        self.SetColumnWidth(resizeCol-1, listWidth - totColWidth)
+
 
 
 
 
 
 
@@ -350,8 +386,8 @@ from bisect import bisect
 
 
 class TextEditMixin:
 
 
 class TextEditMixin:
-    """
-    A mixin class that handles enables any text in any column of a
+    """    
+    A mixin class that enables any text in any column of a
     multi-column listctrl to be edited by clicking on the given row
     and column.  You close the text editor by hitting the ENTER key or
     clicking somewhere else on the listctrl. You switch to the next
     multi-column listctrl to be edited by clicking on the given row
     and column.  You close the text editor by hitting the ENTER key or
     clicking somewhere else on the listctrl. You switch to the next
@@ -360,36 +396,56 @@ class TextEditMixin:
     To use the mixin you have to include it in the class definition
     and call the __init__ function::
 
     To use the mixin you have to include it in the class definition
     and call the __init__ function::
 
-        class TestListCtrl(wx.ListCtrl, TextEdit):
+        class TestListCtrl(wx.ListCtrl, TextEditMixin):
             def __init__(self, parent, ID, pos=wx.DefaultPosition,
                          size=wx.DefaultSize, style=0):
                 wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
             def __init__(self, parent, ID, pos=wx.DefaultPosition,
                          size=wx.DefaultSize, style=0):
                 wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
-                TextEdit.__init__(self) 
+                TextEditMixin.__init__(self) 
 
 
     Authors:     Steve Zatz, Pim Van Heuven (pim@think-wize.com)
     """
 
 
     Authors:     Steve Zatz, Pim Van Heuven (pim@think-wize.com)
     """
+
+    editorBgColour = wx.Colour(255,255,175) # Yellow
+    editorFgColour = wx.Colour(0,0,0)       # black
         
     def __init__(self):
         #editor = wx.TextCtrl(self, -1, pos=(-1,-1), size=(-1,-1),
         #                     style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB \
         #                     |wx.TE_RICH2)
         
     def __init__(self):
         #editor = wx.TextCtrl(self, -1, pos=(-1,-1), size=(-1,-1),
         #                     style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB \
         #                     |wx.TE_RICH2)
+
+        self.make_editor()
+        self.Bind(wx.EVT_TEXT_ENTER, self.CloseEditor)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+
+
+    def make_editor(self, col_style=wx.LIST_FORMAT_LEFT):
         editor = wx.PreTextCtrl()
         editor = wx.PreTextCtrl()
-        editor.Hide()
-        editor.Create(self, -1, style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB|wx.TE_RICH2)
-        editor.SetBackgroundColour(wx.Colour(red=255,green=255,blue=175)) #Yellow
+        
+        style =wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB|wx.TE_RICH2
+        style |= {wx.LIST_FORMAT_LEFT: wx.TE_LEFT,
+                  wx.LIST_FORMAT_RIGHT: wx.TE_RIGHT,
+                  wx.LIST_FORMAT_CENTRE : wx.TE_CENTRE
+                  }[col_style]
+        
+        editor.Create(self, -1, style=style)
+        editor.SetBackgroundColour(self.editorBgColour)
+        editor.SetForegroundColour(self.editorFgColour)
         font = self.GetFont()
         editor.SetFont(font)
 
         font = self.GetFont()
         editor.SetFont(font)
 
+        self.curRow = 0
+        self.curCol = 0
+
+        editor.Hide()
         self.editor = editor
         self.editor = editor
-        self.Bind(wx.EVT_TEXT_ENTER, self.CloseEditor)
+
+        self.col_style = col_style
         self.editor.Bind(wx.EVT_CHAR, self.OnChar)
         self.editor.Bind(wx.EVT_KILL_FOCUS, self.CloseEditor)
         self.editor.Bind(wx.EVT_CHAR, self.OnChar)
         self.editor.Bind(wx.EVT_KILL_FOCUS, self.CloseEditor)
-        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
-        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
-        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
-        self.curRow = -1
-
+        
         
     def OnItemSelected(self, evt):
         self.curRow = evt.GetIndex()
         
     def OnItemSelected(self, evt):
         self.curRow = evt.GetIndex()
@@ -397,11 +453,35 @@ class TextEditMixin:
         
 
     def OnChar(self, event):
         
 
     def OnChar(self, event):
-        ''' Catch the TAB key code so we can open the editor at the next column (if any).'''
-        if event.GetKeyCode() == wx.WXK_TAB:
+        ''' Catch the TAB, Shift-TAB, cursor DOWN/UP key code
+            so we can open the editor at the next column (if any).'''
+
+        keycode = event.GetKeyCode()
+        if keycode == wx.WXK_TAB and event.ShiftDown():
+            self.CloseEditor()
+            if self.curCol-1 >= 0:
+                self.OpenEditor(self.curCol-1, self.curRow)
+            
+        elif keycode == wx.WXK_TAB:
             self.CloseEditor()
             if self.curCol+1 < self.GetColumnCount():
                 self.OpenEditor(self.curCol+1, self.curRow)
             self.CloseEditor()
             if self.curCol+1 < self.GetColumnCount():
                 self.OpenEditor(self.curCol+1, self.curRow)
+
+        elif keycode == wx.WXK_ESCAPE:
+            self.CloseEditor()
+
+        elif keycode == wx.WXK_DOWN:
+            self.CloseEditor()
+            if self.curRow+1 < self.GetItemCount():
+                self._SelectIndex(self.curRow+1)
+                self.OpenEditor(self.curCol, self.curRow)
+
+        elif keycode == wx.WXK_UP:
+            self.CloseEditor()
+            if self.curRow > 0:
+                self._SelectIndex(self.curRow-1)
+                self.OpenEditor(self.curCol, self.curRow)
+            
         else:
             event.Skip()
 
         else:
             event.Skip()
 
@@ -431,21 +511,53 @@ class TextEditMixin:
         for n in range(self.GetColumnCount()):
             loc = loc + self.GetColumnWidth(n)
             self.col_locs.append(loc)
         for n in range(self.GetColumnCount()):
             loc = loc + self.GetColumnWidth(n)
             self.col_locs.append(loc)
+
         
         
-        col = bisect(self.col_locs, x) - 1
+        col = bisect(self.col_locs, x+self.GetScrollPos(wx.HORIZONTAL)) - 1
         self.OpenEditor(col, row)
 
 
     def OpenEditor(self, col, row):
         ''' Opens an editor at the current position. '''
         self.OpenEditor(col, row)
 
 
     def OpenEditor(self, col, row):
         ''' Opens an editor at the current position. '''
+
+        if self.GetColumn(col).m_format != self.col_style:
+            self.make_editor(self.GetColumn(col).m_format)
     
         x0 = self.col_locs[col]
         x1 = self.col_locs[col+1] - x0
 
     
         x0 = self.col_locs[col]
         x1 = self.col_locs[col+1] - x0
 
+        scrolloffset = self.GetScrollPos(wx.HORIZONTAL)
+
+        # scroll forward
+        if x0+x1-scrolloffset > self.GetSize()[0]:
+            if wx.Platform == "__WXMSW__":
+                # don't start scrolling unless we really need to
+                offset = x0+x1-self.GetSize()[0]-scrolloffset
+                # scroll a bit more than what is minimum required
+                # so we don't have to scroll everytime the user presses TAB
+                # which is very tireing to the eye
+                addoffset = self.GetSize()[0]/4
+                # but be careful at the end of the list
+                if addoffset + scrolloffset < self.GetSize()[0]:
+                    offset += addoffset
+
+                self.ScrollList(offset, 0)
+                scrolloffset = self.GetScrollPos(wx.HORIZONTAL)
+            else:
+                # Since we can not programmatically scroll the ListCtrl
+                # close the editor so the user can scroll and open the editor
+                # again
+                self.editor.SetValue(self.GetItem(row, col).GetText())
+                self.curRow = row
+                self.curCol = col
+                self.CloseEditor()
+                return
+
         y0 = self.GetItemRect(row)[1]
         
         editor = self.editor
         y0 = self.GetItemRect(row)[1]
         
         editor = self.editor
-        editor.SetDimensions(x0,y0, x1,-1)
+        editor.SetDimensions(x0-scrolloffset,y0, x1,-1)
+        
         editor.SetValue(self.GetItem(row, col).GetText()) 
         editor.Show()
         editor.Raise()
         editor.SetValue(self.GetItem(row, col).GetText()) 
         editor.Show()
         editor.Raise()
@@ -456,11 +568,47 @@ class TextEditMixin:
         self.curCol = col
 
     
         self.curCol = col
 
     
+    # FIXME: this function is usually called twice - second time because
+    # it is binded to wx.EVT_KILL_FOCUS. Can it be avoided? (MW)
     def CloseEditor(self, evt=None):
         ''' Close the editor and save the new value to the ListCtrl. '''
         text = self.editor.GetValue()
         self.editor.Hide()
     def CloseEditor(self, evt=None):
         ''' Close the editor and save the new value to the ListCtrl. '''
         text = self.editor.GetValue()
         self.editor.Hide()
-        self.SetStringItem(self.curRow, self.curCol, text)
+        self.SetFocus()
+        
+        # post wxEVT_COMMAND_LIST_END_LABEL_EDIT
+        # Event can be vetoed. It doesn't has SetEditCanceled(), what would 
+        # require passing extra argument to CloseEditor() 
+        evt = wx.ListEvent(wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT, self.GetId())
+        evt.m_itemIndex = self.curRow
+        evt.m_col = self.curCol
+        item = self.GetItem(self.curRow, self.curCol)
+        evt.m_item.SetId(item.GetId()) 
+        evt.m_item.SetColumn(item.GetColumn()) 
+        evt.m_item.SetData(item.GetData()) 
+        evt.m_item.SetText(text) #should be empty string if editor was canceled
+        ret = self.GetEventHandler().ProcessEvent(evt)
+        if not ret or evt.IsAllowed():
+            if self.IsVirtual():
+                # replace by whather you use to populate the virtual ListCtrl
+                # data source
+                self.SetVirtualData(self.curRow, self.curCol, text)
+            else:
+                self.SetStringItem(self.curRow, self.curCol, text)
+        self.RefreshItem(self.curRow)
+
+    def _SelectIndex(self, row):
+        listlen = self.GetItemCount()
+        if row < 0 and not listlen:
+            return
+        if row > (listlen-1):
+            row = listlen -1
+            
+        self.SetItemState(self.curRow, ~wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+        self.EnsureVisible(row)
+        self.SetItemState(row, wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)