X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5841276a802f0fc29a260fd4dde6710e450e1b0c..25b3661bd4cb44304418b93c0dee1d0dfb99765c:/wxPython/wx/lib/mixins/listctrl.py diff --git a/wxPython/wx/lib/mixins/listctrl.py b/wxPython/wx/lib/mixins/listctrl.py index ddbb8b46a0..2de4d3bbca 100644 --- a/wxPython/wx/lib/mixins/listctrl.py +++ b/wxPython/wx/lib/mixins/listctrl.py @@ -17,7 +17,15 @@ # 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 @@ -54,7 +62,7 @@ class ColumnSorterMixin: 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): @@ -168,12 +176,25 @@ class ListCtrlAutoWidthMixin: 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) + 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. @@ -188,8 +209,13 @@ class ListCtrlAutoWidthMixin: '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() + # ===================== # == Private Methods == @@ -200,7 +226,10 @@ class ListCtrlAutoWidthMixin: 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() @@ -216,11 +245,20 @@ class ListCtrlAutoWidthMixin: or calculated a minimum width. This ensure that repeated calls to _doResize() don't cause the last column to size itself too large. """ + + if not self: # avoid a PyDeadObject error + return + 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 @@ -232,21 +270,23 @@ class ListCtrlAutoWidthMixin: 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. - self.SetColumnWidth(numCols-1, self._lastColMinWidth) + self.SetColumnWidth(resizeCol-1, self._resizeColMinWidth) return # Resize the last column to take up the remaining available space. - self.SetColumnWidth(numCols-1, listWidth - totColWidth) + self.SetColumnWidth(resizeCol-1, listWidth - totColWidth) + @@ -258,7 +298,7 @@ def selectBeforePopup(event): Works with both single-select and multi-select lists.""" ctrl = event.GetEventObject() - if isinstance(ctrl, wxListCtrl): + if isinstance(ctrl, wx.ListCtrl): n, flags = ctrl.HitTest(event.GetPosition()) if n >= 0: if not ctrl.GetItemState(n, wx.LIST_STATE_SELECTED): @@ -346,8 +386,8 @@ from bisect import bisect 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 @@ -356,36 +396,56 @@ class TextEditMixin: 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) - TextEdit.__init__(self) + TextEditMixin.__init__(self) 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) + + 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.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) + self.curRow = 0 + self.curCol = 0 + + editor.Hide() 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.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() @@ -393,11 +453,35 @@ class TextEditMixin: 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) + + 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() @@ -427,21 +511,53 @@ class TextEditMixin: 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. ''' + + 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 + 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 - editor.SetDimensions(x0,y0, x1,-1) + editor.SetDimensions(x0-scrolloffset,y0, x1,-1) + editor.SetValue(self.GetItem(row, col).GetText()) editor.Show() editor.Raise() @@ -452,11 +568,47 @@ class TextEditMixin: 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() - 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)