# --------------------------------------------------------------------------------- #
# ULTIMATELISTCTRL wxPython IMPLEMENTATION
# Inspired by and heavily based on the wxWidgets C++ generic version of wxListCtrl.
#
# Andrea Gavana, @ 08 May 2009
# Latest Revision: 09 Feb 2012, 21.00 GMT
#
#
# TODO List
#
# 1) Subitem selection;
# 2) Watermark? (almost, does not work very well :-( );
# 3) Groups? (Maybe, check ObjectListView);
# 4) Scrolling items as headers and footers;
# 5) Alpha channel for text/background of items;
# 6) Custom renderers for headers/footers (done);
# 7) Fading in and out on mouse motion (a la Windows Vista Aero);
# 8) Sub-text for headers/footers (grey text below the header/footer text);
# 9) Fixing the columns to the left or right side of the control layout;
# 10) Skins for header and scrollbars (implemented for headers/footers).
#
#
# For all kind of problems, requests of enhancements and bug reports, please
# write to me at:
#
# andrea.gavana@gmail.com
# andrea.gavana@maerskoil.com
#
# Or, obviously, to the wxPython mailing list!!!
#
#
# End Of Comments
# --------------------------------------------------------------------------------- #
"""
Description
===========
UltimateListCtrl is a class that mimics the behaviour of `wx.ListCtrl`, with almost
the same base functionalities plus some more enhancements. This class does
not rely on the native control, as it is a full owner-drawn list control.
In addition to the standard `wx.ListCtrl` behaviour this class supports:
Appearance
==========
* Multiple images for items/subitems;
* Images can be of any size and not limited to a single specific pair of `width`, `height`
as it is the case of `wx.ImageList`. Simply use L{PyImageList} instead of `wx.ImageList`
to add your images.
* Font, colour, background, custom renderers and formatting for items and subitems;
* Ability to add persistent data to an item using L{SetItemPyData} and L{GetItemPyData}:
the data can be any Python object and not necessarily an integer as in `wx.ListCtrl`;
* CheckBox-type items and subitems;
* RadioButton-type items and subitems;
* Overflowing items/subitems, a la `wx.grid.Grid`, i.e. an item/subitem may overwrite neighboring
items/subitems if its text would not normally fit in the space allotted to it;
* Hyperlink-type items and subitems: they look like an hyperlink, with the proper mouse
cursor on hovering;
* Multiline text items and subitems;
* Variable row heights depending on the item/subitem kind/text/window;
* User defined item/subitem renderers: these renderer classes **must** implement the methods
`DrawSubItem`, `GetLineHeight` and `GetSubItemWidth` (see the demo);
* Enabling/disabling items (together with their plain or grayed out icons);
* Whatever non-toplevel widget can be attached next to an item/subitem;
* Column headers are fully customizable in terms of icons, colour, font, alignment etc...;
* Column headers can have their own checkbox/radiobutton;
* Column footers are fully customizable in terms of icons, colour, font, alignment etc...;
* Column footers can have their own checkbox/radiobutton;
* Ability to hide/show columns;
* Default selection style, gradient (horizontal/vertical) selection style and Windows
Vista selection style.
And a lot more. Check the demo for an almost complete review of the functionalities.
Usage
=====
Usage example::
import sys
import wx
import wx.lib.agw.ultimatelistctrl as ULC
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent, -1, "UltimateListCtrl Demo")
list = ULC.UltimateListCtrl(self, wx.ID_ANY, agwStyle=wx.LC_REPORT|wx.LC_VRULES|wx.LC_HRULES|wx.LC_SINGLE_SEL)
list.InsertColumn(0, "Column 1")
list.InsertColumn(1, "Column 2")
index = list.InsertStringItem(sys.maxint, "Item 1")
list.SetStringItem(index, 1, "Sub-item 1")
index = list.InsertStringItem(sys.maxint, "Item 2")
list.SetStringItem(index, 1, "Sub-item 2")
choice = wx.Choice(list, -1, choices=["one", "two"])
index = list.InsertStringItem(sys.maxint, "A widget")
list.SetItemWindow(index, 1, choice, expand=True)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(list, 1, wx.EXPAND)
self.SetSizer(sizer)
# our normal wxApp-derived class, as usual
app = wx.PySimpleApp()
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Window Styles
=============
This class supports the following window styles:
=============================== =========== ====================================================================================================
Window Styles Hex Value Description
=============================== =========== ====================================================================================================
``ULC_VRULES`` 0x1 Draws light vertical rules between rows in report mode.
``ULC_HRULES`` 0x2 Draws light horizontal rules between rows in report mode.
``ULC_ICON`` 0x4 Large icon view, with optional labels.
``ULC_SMALL_ICON`` 0x8 Small icon view, with optional labels.
``ULC_LIST`` 0x10 Multicolumn list view, with optional small icons. Columns are computed automatically, i.e. you don't set columns as in ``ULC_REPORT``. In other words, the list wraps, unlike a `wx.ListBox`.
``ULC_REPORT`` 0x20 Single or multicolumn report view, with optional header.
``ULC_ALIGN_TOP`` 0x40 Icons align to the top. Win32 default, Win32 only.
``ULC_ALIGN_LEFT`` 0x80 Icons align to the left.
``ULC_AUTOARRANGE`` 0x100 Icons arrange themselves. Win32 only.
``ULC_VIRTUAL`` 0x200 The application provides items text on demand. May only be used with ``ULC_REPORT``.
``ULC_EDIT_LABELS`` 0x400 Labels are editable: the application will be notified when editing starts.
``ULC_NO_HEADER`` 0x800 No header in report mode.
``ULC_NO_SORT_HEADER`` 0x1000 No Docs.
``ULC_SINGLE_SEL`` 0x2000 Single selection (default is multiple).
``ULC_SORT_ASCENDING`` 0x4000 Sort in ascending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_TILE`` 0x10000 Each item appears as a full-sized icon with a label of one or more lines beside it (partially implemented).
``ULC_NO_HIGHLIGHT`` 0x20000 No highlight when an item is selected.
``ULC_STICKY_HIGHLIGHT`` 0x40000 Items are selected by simply hovering on them, with no need to click on them.
``ULC_STICKY_NOSELEVENT`` 0x80000 Don't send a selection event when using ``ULC_STICKY_HIGHLIGHT`` style.
``ULC_SEND_LEFTCLICK`` 0x100000 Send a left click event when an item is selected.
``ULC_HAS_VARIABLE_ROW_HEIGHT`` 0x200000 The list has variable row heights.
``ULC_AUTO_CHECK_CHILD`` 0x400000 When a column header has a checkbox associated, auto-check all the subitems in that column.
``ULC_AUTO_TOGGLE_CHILD`` 0x800000 When a column header has a checkbox associated, toggle all the subitems in that column.
``ULC_AUTO_CHECK_PARENT`` 0x1000000 Only meaningful foe checkbox-type items: when an item is checked/unchecked its column header item is checked/unchecked as well.
``ULC_SHOW_TOOLTIPS`` 0x2000000 Show tooltips for ellipsized items/subitems (text too long to be shown in the available space) containing the full item/subitem text.
``ULC_HOT_TRACKING`` 0x4000000 Enable hot tracking of items on mouse motion.
``ULC_BORDER_SELECT`` 0x8000000 Changes border colour whan an item is selected, instead of highlighting the item.
``ULC_TRACK_SELECT`` 0x10000000 Enables hot-track selection in a list control. Hot track selection means that an item is automatically selected when the cursor remains over the item for a certain period of time. The delay is retrieved on Windows using the `win32api` call `win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)`, and is defaulted to 400ms on other platforms. This style applies to all views of `UltimateListCtrl`.
``ULC_HEADER_IN_ALL_VIEWS`` 0x20000000 Show column headers in all view modes.
``ULC_NO_FULL_ROW_SELECT`` 0x40000000 When an item is selected, the only the item in the first column is highlighted.
``ULC_FOOTER`` 0x80000000 Show a footer too (only when header is present).
``ULC_USER_ROW_HEIGHT`` 0x100000000 Allows to set a custom row height (one value for all the items, only in report mode).
=============================== =========== ====================================================================================================
Events Processing
=================
This class processes the following events:
======================================== ====================================================================================================
Event Name Description
======================================== ====================================================================================================
``EVT_LIST_BEGIN_DRAG`` Begin dragging with the left mouse button.
``EVT_LIST_BEGIN_RDRAG`` Begin dragging with the right mouse button.
``EVT_LIST_BEGIN_LABEL_EDIT`` Begin editing a label. This can be prevented by calling `Veto()`.
``EVT_LIST_END_LABEL_EDIT`` Finish editing a label. This can be prevented by calling `Veto()`.
``EVT_LIST_DELETE_ITEM`` An item was deleted.
``EVT_LIST_DELETE_ALL_ITEMS`` All items were deleted.
``EVT_LIST_KEY_DOWN`` A key has been pressed.
``EVT_LIST_INSERT_ITEM`` An item has been inserted.
``EVT_LIST_COL_CLICK`` A column (`m_col`) has been left-clicked.
``EVT_LIST_COL_RIGHT_CLICK`` A column (`m_col`) has been right-clicked.
``EVT_LIST_COL_BEGIN_DRAG`` The user started resizing a column - can be vetoed.
``EVT_LIST_COL_END_DRAG`` The user finished resizing a column.
``EVT_LIST_COL_DRAGGING`` The divider between columns is being dragged.
``EVT_LIST_ITEM_SELECTED`` The item has been selected.
``EVT_LIST_ITEM_DESELECTED`` The item has been deselected.
``EVT_LIST_ITEM_RIGHT_CLICK`` The right mouse button has been clicked on an item.
``EVT_LIST_ITEM_MIDDLE_CLICK`` The middle mouse button has been clicked on an item.
``EVT_LIST_ITEM_ACTIVATED`` The item has been activated (``ENTER`` or double click).
``EVT_LIST_ITEM_FOCUSED`` The currently focused item has changed.
``EVT_LIST_CACHE_HINT`` Prepare cache for a virtual list control.
``EVT_LIST_ITEM_CHECKING`` An item/subitem is being checked.
``EVT_LIST_ITEM_CHECKED`` An item/subitem has been checked.
``EVT_LIST_COL_CHECKING`` A column header is being checked.
``EVT_LIST_COL_CHECKED`` A column header has being checked.
``EVT_LIST_FOOTER_CHECKING`` A column footer is being checked.
``EVT_LIST_FOOTER_CHECKED`` A column footer has being checked.
``EVT_LIST_ITEM_HYPERLINK`` An hyperlink item has been clicked.
``EVT_LIST_FOOTER_CLICK`` The user left-clicked on a column footer.
``EVT_LIST_FOOTER_RIGHT_CLICK`` The user right-clicked on a column footer.
``EVT_LIST_ITEM_LEFT_CLICK`` Send a left-click event after an item is selected.
``EVT_LIST_END_DRAG`` Notify an end-drag operation.
======================================== ====================================================================================================
Supported Platforms
===================
UltimateListCtrl has been tested on the following platforms:
* Windows (Windows XP);
License And Version
===================
UltimateListCtrl is distributed under the wxPython license.
Latest Revision: Andrea Gavana @ 09 Feb 2012, 21.00 GMT
Version 0.8
"""
import wx
import math
import bisect
import types
import zlib
import cStringIO
from wx.lib.expando import ExpandoTextCtrl
# Version Info
__version__ = "0.8"
# wxPython version string
_VERSION_STRING = wx.VERSION_STRING
# ----------------------------------------------------------------------------
# UltimateListCtrl constants
# ----------------------------------------------------------------------------
# style flags
ULC_VRULES = wx.LC_VRULES
ULC_HRULES = wx.LC_HRULES
ULC_ICON = wx.LC_ICON
ULC_SMALL_ICON = wx.LC_SMALL_ICON
ULC_LIST = wx.LC_LIST
ULC_REPORT = wx.LC_REPORT
ULC_TILE = 0x10000
ULC_ALIGN_TOP = wx.LC_ALIGN_TOP
ULC_ALIGN_LEFT = wx.LC_ALIGN_LEFT
ULC_AUTOARRANGE = wx.LC_AUTOARRANGE
ULC_VIRTUAL = wx.LC_VIRTUAL
ULC_EDIT_LABELS = wx.LC_EDIT_LABELS
ULC_NO_HEADER = wx.LC_NO_HEADER
ULC_NO_SORT_HEADER = wx.LC_NO_SORT_HEADER
ULC_SINGLE_SEL = wx.LC_SINGLE_SEL
ULC_SORT_ASCENDING = wx.LC_SORT_ASCENDING
ULC_SORT_DESCENDING = wx.LC_SORT_DESCENDING
ULC_NO_HIGHLIGHT = 0x20000
ULC_STICKY_HIGHLIGHT = 0x40000
ULC_STICKY_NOSELEVENT = 0x80000
ULC_SEND_LEFTCLICK = 0x100000
ULC_HAS_VARIABLE_ROW_HEIGHT = 0x200000
ULC_AUTO_CHECK_CHILD = 0x400000 # only meaningful for checkboxes
ULC_AUTO_TOGGLE_CHILD = 0x800000 # only meaningful for checkboxes
ULC_AUTO_CHECK_PARENT = 0x1000000 # only meaningful for checkboxes
ULC_SHOW_TOOLTIPS = 0x2000000 # shows tooltips on items with ellipsis (...)
ULC_HOT_TRACKING = 0x4000000 # enable hot tracking on mouse motion
ULC_BORDER_SELECT = 0x8000000 # changes border colour whan an item is selected, instead of highlighting the item
ULC_TRACK_SELECT = 0x10000000 # Enables hot-track selection in a list control. Hot track selection means that an item
# is automatically selected when the cursor remains over the item for a certain period
# of time. The delay is retrieved on Windows using the win32api call
# win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME), and is defaulted to 400ms
# on other platforms. This style applies to all styles of UltimateListCtrl.
ULC_HEADER_IN_ALL_VIEWS = 0x20000000 # Show column headers in all view modes
ULC_NO_FULL_ROW_SELECT = 0x40000000 # When an item is selected, the only the item in the first column is highlighted
ULC_FOOTER = 0x80000000 # Show a footer too (only when header is present)
ULC_USER_ROW_HEIGHT = 0x100000000 # Allows to set a custom row height (one value for all the items, only in report mode).
ULC_MASK_TYPE = ULC_ICON | ULC_SMALL_ICON | ULC_LIST | ULC_REPORT | ULC_TILE
ULC_MASK_ALIGN = ULC_ALIGN_TOP | ULC_ALIGN_LEFT
ULC_MASK_SORT = ULC_SORT_ASCENDING | ULC_SORT_DESCENDING
# for compatibility only
ULC_USER_TEXT = ULC_VIRTUAL
# Omitted because
# (a) too much detail
# (b) not enough style flags
# (c) not implemented anyhow in the generic version
#
# ULC_NO_SCROLL
# ULC_NO_LABEL_WRAP
# ULC_OWNERDRAW_FIXED
# ULC_SHOW_SEL_ALWAYS
# Mask flags to tell app/GUI what fields of UltimateListItem are valid
ULC_MASK_STATE = wx.LIST_MASK_STATE
ULC_MASK_TEXT = wx.LIST_MASK_TEXT
ULC_MASK_IMAGE = wx.LIST_MASK_IMAGE
ULC_MASK_DATA = wx.LIST_MASK_DATA
ULC_SET_ITEM = wx.LIST_SET_ITEM
ULC_MASK_WIDTH = wx.LIST_MASK_WIDTH
ULC_MASK_FORMAT = wx.LIST_MASK_FORMAT
ULC_MASK_FONTCOLOUR = 0x0080
ULC_MASK_FONT = 0x0100
ULC_MASK_BACKCOLOUR = 0x0200
ULC_MASK_KIND = 0x0400
ULC_MASK_ENABLE = 0x0800
ULC_MASK_CHECK = 0x1000
ULC_MASK_HYPERTEXT = 0x2000
ULC_MASK_WINDOW = 0x4000
ULC_MASK_PYDATA = 0x8000
ULC_MASK_SHOWN = 0x10000
ULC_MASK_RENDERER = 0x20000
ULC_MASK_OVERFLOW = 0x40000
ULC_MASK_FOOTER_TEXT = 0x80000
ULC_MASK_FOOTER_IMAGE = 0x100000
ULC_MASK_FOOTER_FORMAT = 0x200000
ULC_MASK_FOOTER_FONT = 0x400000
ULC_MASK_FOOTER_CHECK = 0x800000
ULC_MASK_FOOTER_KIND = 0x1000000
ULC_MASK_TOOLTIP = 0x2000000
# State flags for indicating the state of an item
ULC_STATE_DONTCARE = wx.LIST_STATE_DONTCARE
ULC_STATE_DROPHILITED = wx.LIST_STATE_DROPHILITED # MSW only
ULC_STATE_FOCUSED = wx.LIST_STATE_FOCUSED
ULC_STATE_SELECTED = wx.LIST_STATE_SELECTED
ULC_STATE_CUT = wx.LIST_STATE_CUT # MSW only
ULC_STATE_DISABLED = wx.LIST_STATE_DISABLED # OS2 only
ULC_STATE_FILTERED = wx.LIST_STATE_FILTERED # OS2 only
ULC_STATE_INUSE = wx.LIST_STATE_INUSE # OS2 only
ULC_STATE_PICKED = wx.LIST_STATE_PICKED # OS2 only
ULC_STATE_SOURCE = wx.LIST_STATE_SOURCE # OS2 only
# Hit test flags, used in HitTest
ULC_HITTEST_ABOVE = wx.LIST_HITTEST_ABOVE # Above the client area.
ULC_HITTEST_BELOW = wx.LIST_HITTEST_BELOW # Below the client area.
ULC_HITTEST_NOWHERE = wx.LIST_HITTEST_NOWHERE # In the client area but below the last item.
ULC_HITTEST_ONITEMICON = wx.LIST_HITTEST_ONITEMICON # On the bitmap associated with an item.
ULC_HITTEST_ONITEMLABEL = wx.LIST_HITTEST_ONITEMLABEL # On the label (string) associated with an item.
ULC_HITTEST_ONITEMRIGHT = wx.LIST_HITTEST_ONITEMRIGHT # In the area to the right of an item.
ULC_HITTEST_ONITEMSTATEICON = wx.LIST_HITTEST_ONITEMSTATEICON # On the state icon for a tree view item that is in a user-defined state.
ULC_HITTEST_TOLEFT = wx.LIST_HITTEST_TOLEFT # To the left of the client area.
ULC_HITTEST_TORIGHT = wx.LIST_HITTEST_TORIGHT # To the right of the client area.
ULC_HITTEST_ONITEMCHECK = 0x1000 # On the checkbox (if any)
ULC_HITTEST_ONITEM = ULC_HITTEST_ONITEMICON | ULC_HITTEST_ONITEMLABEL | ULC_HITTEST_ONITEMSTATEICON | ULC_HITTEST_ONITEMCHECK
# Flags for GetNextItem (MSW only except ULC_NEXT_ALL)
ULC_NEXT_ABOVE = wx.LIST_NEXT_ABOVE # Searches for an item above the specified item
ULC_NEXT_ALL = wx.LIST_NEXT_ALL # Searches for subsequent item by index
ULC_NEXT_BELOW = wx.LIST_NEXT_BELOW # Searches for an item below the specified item
ULC_NEXT_LEFT = wx.LIST_NEXT_LEFT # Searches for an item to the left of the specified item
ULC_NEXT_RIGHT = wx.LIST_NEXT_RIGHT # Searches for an item to the right of the specified item
# Alignment flags for Arrange (MSW only except ULC_ALIGN_LEFT)
ULC_ALIGN_DEFAULT = wx.LIST_ALIGN_DEFAULT
ULC_ALIGN_SNAP_TO_GRID = wx.LIST_ALIGN_SNAP_TO_GRID
# Column format (MSW only except ULC_FORMAT_LEFT)
ULC_FORMAT_LEFT = wx.LIST_FORMAT_LEFT
ULC_FORMAT_RIGHT = wx.LIST_FORMAT_RIGHT
ULC_FORMAT_CENTRE = wx.LIST_FORMAT_CENTRE
ULC_FORMAT_CENTER = ULC_FORMAT_CENTRE
# Autosize values for SetColumnWidth
ULC_AUTOSIZE = wx.LIST_AUTOSIZE
ULC_AUTOSIZE_USEHEADER = wx.LIST_AUTOSIZE_USEHEADER # partly supported by generic version
ULC_AUTOSIZE_FILL = -3
# Flag values for GetItemRect
ULC_RECT_BOUNDS = wx.LIST_RECT_BOUNDS
ULC_RECT_ICON = wx.LIST_RECT_ICON
ULC_RECT_LABEL = wx.LIST_RECT_LABEL
# Flag values for FindItem (MSW only)
ULC_FIND_UP = wx.LIST_FIND_UP
ULC_FIND_DOWN = wx.LIST_FIND_DOWN
ULC_FIND_LEFT = wx.LIST_FIND_LEFT
ULC_FIND_RIGHT = wx.LIST_FIND_RIGHT
# Items/subitems rect
ULC_GETSUBITEMRECT_WHOLEITEM = wx.LIST_GETSUBITEMRECT_WHOLEITEM
# ----------------------------------------------------------------------------
# UltimateListCtrl event macros
# ----------------------------------------------------------------------------
wxEVT_COMMAND_LIST_BEGIN_DRAG = wx.wxEVT_COMMAND_LIST_BEGIN_DRAG
wxEVT_COMMAND_LIST_BEGIN_RDRAG = wx.wxEVT_COMMAND_LIST_BEGIN_RDRAG
wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT = wx.wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
wxEVT_COMMAND_LIST_END_LABEL_EDIT = wx.wxEVT_COMMAND_LIST_END_LABEL_EDIT
wxEVT_COMMAND_LIST_DELETE_ITEM = wx.wxEVT_COMMAND_LIST_DELETE_ITEM
wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS = wx.wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS
wxEVT_COMMAND_LIST_ITEM_SELECTED = wx.wxEVT_COMMAND_LIST_ITEM_SELECTED
wxEVT_COMMAND_LIST_ITEM_DESELECTED = wx.wxEVT_COMMAND_LIST_ITEM_DESELECTED
wxEVT_COMMAND_LIST_KEY_DOWN = wx.wxEVT_COMMAND_LIST_KEY_DOWN
wxEVT_COMMAND_LIST_INSERT_ITEM = wx.wxEVT_COMMAND_LIST_INSERT_ITEM
wxEVT_COMMAND_LIST_COL_CLICK = wx.wxEVT_COMMAND_LIST_COL_CLICK
wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK = wx.wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK
wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK = wx.wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK
wxEVT_COMMAND_LIST_ITEM_ACTIVATED = wx.wxEVT_COMMAND_LIST_ITEM_ACTIVATED
wxEVT_COMMAND_LIST_CACHE_HINT = wx.wxEVT_COMMAND_LIST_CACHE_HINT
wxEVT_COMMAND_LIST_COL_RIGHT_CLICK = wx.wxEVT_COMMAND_LIST_COL_RIGHT_CLICK
wxEVT_COMMAND_LIST_COL_BEGIN_DRAG = wx.wxEVT_COMMAND_LIST_COL_BEGIN_DRAG
wxEVT_COMMAND_LIST_COL_DRAGGING = wx.wxEVT_COMMAND_LIST_COL_DRAGGING
wxEVT_COMMAND_LIST_COL_END_DRAG = wx.wxEVT_COMMAND_LIST_COL_END_DRAG
wxEVT_COMMAND_LIST_ITEM_FOCUSED = wx.wxEVT_COMMAND_LIST_ITEM_FOCUSED
wxEVT_COMMAND_LIST_FOOTER_CLICK = wx.NewEventType()
wxEVT_COMMAND_LIST_FOOTER_RIGHT_CLICK = wx.NewEventType()
wxEVT_COMMAND_LIST_FOOTER_CHECKING = wx.NewEventType()
wxEVT_COMMAND_LIST_FOOTER_CHECKED = wx.NewEventType()
wxEVT_COMMAND_LIST_ITEM_LEFT_CLICK = wx.NewEventType()
wxEVT_COMMAND_LIST_ITEM_CHECKING = wx.NewEventType()
wxEVT_COMMAND_LIST_ITEM_CHECKED = wx.NewEventType()
wxEVT_COMMAND_LIST_ITEM_HYPERLINK = wx.NewEventType()
wxEVT_COMMAND_LIST_END_DRAG = wx.NewEventType()
wxEVT_COMMAND_LIST_COL_CHECKING = wx.NewEventType()
wxEVT_COMMAND_LIST_COL_CHECKED = wx.NewEventType()
EVT_LIST_BEGIN_DRAG = wx.EVT_LIST_BEGIN_DRAG
EVT_LIST_BEGIN_RDRAG = wx.EVT_LIST_BEGIN_RDRAG
EVT_LIST_BEGIN_LABEL_EDIT = wx.EVT_LIST_BEGIN_LABEL_EDIT
EVT_LIST_END_LABEL_EDIT = wx.EVT_LIST_END_LABEL_EDIT
EVT_LIST_DELETE_ITEM = wx.EVT_LIST_DELETE_ITEM
EVT_LIST_DELETE_ALL_ITEMS = wx.EVT_LIST_DELETE_ALL_ITEMS
EVT_LIST_KEY_DOWN = wx.EVT_LIST_KEY_DOWN
EVT_LIST_INSERT_ITEM = wx.EVT_LIST_INSERT_ITEM
EVT_LIST_COL_CLICK = wx.EVT_LIST_COL_CLICK
EVT_LIST_COL_RIGHT_CLICK = wx.EVT_LIST_COL_RIGHT_CLICK
EVT_LIST_COL_BEGIN_DRAG = wx.EVT_LIST_COL_BEGIN_DRAG
EVT_LIST_COL_END_DRAG = wx.EVT_LIST_COL_END_DRAG
EVT_LIST_COL_DRAGGING = wx.EVT_LIST_COL_DRAGGING
EVT_LIST_ITEM_SELECTED = wx.EVT_LIST_ITEM_SELECTED
EVT_LIST_ITEM_DESELECTED = wx.EVT_LIST_ITEM_DESELECTED
EVT_LIST_ITEM_RIGHT_CLICK = wx.EVT_LIST_ITEM_RIGHT_CLICK
EVT_LIST_ITEM_MIDDLE_CLICK = wx.EVT_LIST_ITEM_MIDDLE_CLICK
EVT_LIST_ITEM_ACTIVATED = wx.EVT_LIST_ITEM_ACTIVATED
EVT_LIST_ITEM_FOCUSED = wx.EVT_LIST_ITEM_FOCUSED
EVT_LIST_CACHE_HINT = wx.EVT_LIST_CACHE_HINT
EVT_LIST_ITEM_LEFT_CLICK = wx.PyEventBinder(wxEVT_COMMAND_LIST_ITEM_LEFT_CLICK, 1)
EVT_LIST_ITEM_CHECKING = wx.PyEventBinder(wxEVT_COMMAND_LIST_ITEM_CHECKING, 1)
EVT_LIST_ITEM_CHECKED = wx.PyEventBinder(wxEVT_COMMAND_LIST_ITEM_CHECKED, 1)
EVT_LIST_ITEM_HYPERLINK = wx.PyEventBinder(wxEVT_COMMAND_LIST_ITEM_HYPERLINK, 1)
EVT_LIST_END_DRAG = wx.PyEventBinder(wxEVT_COMMAND_LIST_END_DRAG, 1)
EVT_LIST_COL_CHECKING = wx.PyEventBinder(wxEVT_COMMAND_LIST_COL_CHECKING, 1)
EVT_LIST_COL_CHECKED = wx.PyEventBinder(wxEVT_COMMAND_LIST_COL_CHECKED, 1)
EVT_LIST_FOOTER_CLICK = wx.PyEventBinder(wxEVT_COMMAND_LIST_FOOTER_CLICK, 1)
EVT_LIST_FOOTER_RIGHT_CLICK = wx.PyEventBinder(wxEVT_COMMAND_LIST_FOOTER_RIGHT_CLICK, 1)
EVT_LIST_FOOTER_CHECKING = wx.PyEventBinder(wxEVT_COMMAND_LIST_FOOTER_CHECKING, 1)
EVT_LIST_FOOTER_CHECKED = wx.PyEventBinder(wxEVT_COMMAND_LIST_FOOTER_CHECKED, 1)
# NOTE: If using the wxExtListBox visual attributes works everywhere then this can
# be removed, as well as the #else case below.
_USE_VISATTR = 0
# ----------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------
SCROLL_UNIT_X = 15
SCROLL_UNIT_Y = 15
# the spacing between the lines (in report mode)
LINE_SPACING = 0
# extra margins around the text label
EXTRA_WIDTH = 4
EXTRA_HEIGHT = 4
if wx.Platform == "__WXGTK__":
EXTRA_HEIGHT = 6
# margin between the window and the items
EXTRA_BORDER_X = 2
EXTRA_BORDER_Y = 2
# offset for the header window
HEADER_OFFSET_X = 1
HEADER_OFFSET_Y = 1
# margin between rows of icons in [small] icon view
MARGIN_BETWEEN_ROWS = 6
# when autosizing the columns, add some slack
AUTOSIZE_COL_MARGIN = 10
# default and minimal widths for the header columns
WIDTH_COL_DEFAULT = 80
WIDTH_COL_MIN = 10
# the space between the image and the text in the report mode
IMAGE_MARGIN_IN_REPORT_MODE = 5
# the space between the image and the text in the report mode in header
HEADER_IMAGE_MARGIN_IN_REPORT_MODE = 2
# and the width of the icon, if any
MARGIN_BETWEEN_TEXT_AND_ICON = 2
# Background Image Style
_StyleTile = 0
_StyleStretch = 1
# Windows Vista Colours
_rgbSelectOuter = wx.Colour(170, 200, 245)
_rgbSelectInner = wx.Colour(230, 250, 250)
_rgbSelectTop = wx.Colour(210, 240, 250)
_rgbSelectBottom = wx.Colour(185, 215, 250)
_rgbNoFocusTop = wx.Colour(250, 250, 250)
_rgbNoFocusBottom = wx.Colour(235, 235, 235)
_rgbNoFocusOuter = wx.Colour(220, 220, 220)
_rgbNoFocusInner = wx.Colour(245, 245, 245)
# Mouse hover time for track selection
HOVER_TIME = 400
if wx.Platform == "__WXMSW__":
try:
import win32gui, win32con
HOVER_TIME = win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)
except ImportError:
pass
# For PyImageList
IL_FIXED_SIZE = 0
IL_VARIABLE_SIZE = 1
# Python integers, to make long types to work with CreateListItem
INTEGER_TYPES = [types.IntType, types.LongType]
# ----------------------------------------------------------------------------
# Functions
# ----------------------------------------------------------------------------
# Utility method
[docs]def to_list(input):
"""
Converts the input data into a Python list.
:param `input`: can be an integer or a Python list (in which case nothing will
be done to `input`.
"""
if isinstance(input, types.ListType):
return input
elif isinstance(input, types.IntType):
return [input]
else:
raise Exception("Invalid parameter passed to `to_list`: only integers and list are accepted.")
[docs]def CheckVariableRowHeight(listCtrl, text):
"""
Checks whether a `text` contains multiline strings and if the `listCtrl` window
style is compatible with multiline strings.
:param `listCtrl`: an instance of L{UltimateListCtrl};
:param `text`: the text to analyze.
"""
if not listCtrl.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
if "\n" in text:
raise Exception("Multiline text items are not allowed without the ULC_HAS_VARIABLE_ROW_HEIGHT style.")
[docs]def CreateListItem(itemOrId, col):
"""
Creates a new instance of L{UltimateListItem}.
:param `itemOrId`: can be an instance of L{UltimateListItem} or an integer;
:param `col`: the item column.
"""
if type(itemOrId) in INTEGER_TYPES:
item = UltimateListItem()
item._itemId = itemOrId
item._col = col
else:
item = itemOrId
return item
# ----------------------------------------------------------------------------
[docs]def MakeDisabledBitmap(original):
"""
Creates a disabled-looking bitmap starting from the input one.
:param `original`: an instance of `wx.Bitmap` to be greyed-out.
"""
img = original.ConvertToImage()
return wx.BitmapFromImage(img.ConvertToGreyscale())
# ----------------------------------------------------------------------------
#----------------------------------------------------------------------
[docs]def GetdragcursorData():
""" Returns the drag and drop cursor image as a decompressed stream of characters. """
return zlib.decompress(
"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\xa2@,\xcf\xc1\
\x06$9z\xda>\x00)\xce\x02\x8f\xc8b\x06\x06na\x10fd\x985G\x02(\xd8W\xe2\x1aQ\
\xe2\x9c\x9f\x9b\x9b\x9aW\xc2\x90\xec\x11\xe4\xab\x90\x9cQ\x9a\x97\x9d\x93\
\x9a\xa7`l\xa4\x90\x99\x9e\x97_\x94\x9a\xc2\xeb\x18\xec\xec\xe9i\xa5\xa0\xa7\
W\xa5\xaa\x07\x01P:7\x1eH\xe4\xe8\xe9\xd9\x808\x11\xbc\x1e\xae\x11V\n\x06@`\
\xeehd\n\xa2-\x0c,\x8cA\xb4\x9b\t\x94o\xe2b\x08\xa2\xcd\\L\xdd@\xb4\xab\x85\
\x993\x886v\xb6p\x02\xd1\x86N\xa6\x16\x12\xf7~\xdf\x05\xbal\xa9\xa7\x8bcH\
\xc5\x9c3W9\xb9\x1a\x14\x04X/\xec\xfc\xbft\xed\x02\xa5\xf4\xc2m\xfa*<N\x17??\
\x0frqy\x9c\xd3\xb2f5\xaf\x89\x8f9Gk\xbc\x08\xa7\xbf\x06\x97\x98\x06S\xd8E\
\xbd\x9cE\xb2\x15\x9da\x89\xe2k\x0f\x9c\xb6|\x1a\xea\x14X\x1d6G\x83E\xe7\x9c\
\x1dO\xa8\xde\xb6\x84l\x15\x9eS\xcf\xc2tf\x15\xde\xf7\xb5\xb2]\xf0\x96+\xf5@\
D\x90\x1d\xef19_\xf5\xde5y\xb6+\xa7\xdeZ\xfbA\x9bu\x9f`\xffD\xafYn\xf6\x9eW\
\xeb>\xb6\x7f\x98\\U\xcb\xf5\xd5\xcb\x9a'\xe7\xf4\xd7\x0b\xba\x9e\xdb\x17E\
\xfdf\x97Z\xcb\xcc\xc0\xf0\xff?3\xc3\x92\xabN\x8arB\xc7\x8f\x03\x1d\xcc\xe0\
\xe9\xea\xe7\xb2\xce)\xa1\t\x00B7|\x00" )
[docs]def GetdragcursorBitmap():
""" Returns the drag and drop cursor image as a `wx.Bitmap`. """
return wx.BitmapFromImage(GetdragcursorImage())
[docs]def GetdragcursorImage():
""" Returns the drag and drop cursor image as a `wx.Image`. """
stream = cStringIO.StringIO(GetdragcursorData())
return wx.ImageFromStream(stream)
#-----------------------------------------------------------------------------
# PyImageList
#-----------------------------------------------------------------------------
[docs]class PyImageList(object):
"""
A L{PyImageList} contains a list of images. Images can have masks for
transparent drawing, and can be made from a variety of sources including
bitmaps and icons.
L{PyImageList} is used in conjunction with L{UltimateListCtrl}.
:note: The main improvements that L{PyImageList} introduces is the removal
of the limitation of same-size images inside the image list. If you use
the style ``IL_VARIABLE_SIZE`` then each image can have any size (in terms
of width and height).
"""
[docs] def __init__(self, width, height, mask=True, initialCount=1, style=IL_VARIABLE_SIZE):
"""
Default class constructor.
:param `width`: the width of the images in the image list, in pixels (unused
if you specify the ``IL_VARIABLE_SIZE`` style;
:param `height`: the height of the images in the image list, in pixels (unused
if you specify the ``IL_VARIABLE_SIZE`` style;
:param `mask`: ``True`` if masks should be created for all images (unused in
L{PyImageList});
:param `initialCount`: the initial size of the list (unused in L{PyImageList});
:param `style`: can be one of the following bits:
==================== ===== =================================
Style Flag Value Description
==================== ===== =================================
``IL_FIXED_SIZE`` 0 All the images in L{PyImageList} have the same size (width, height)
``IL_VARIABLE_SIZE`` 1 Each image can have any size (in terms of width and height)
==================== ===== =================================
"""
self._width = width
self._height = height
self._mask = mask
self._initialCount = 1
self._style = style
self._images = []
[docs] def GetImageCount(self):
""" Returns the number of images in the list. """
return len(self._images)
[docs] def Add(self, bitmap):
"""
Adds a new image or images using a bitmap.
:param `bitmap`: a valid `wx.Bitmap` object.
:return: The new zero-based image index.
:note: If the bitmap is wider than the images in the list and you are not using
the ``IL_VARIABLE_SIZE`` style, then the bitmap will automatically be split
into smaller images, each matching the dimensions of the image list.
"""
index = len(self._images)
# Mimic behavior of Windows ImageList_Add that automatically breaks up the added
# bitmap into sub-images of the correct size
if self._style & IL_FIXED_SIZE:
if self._width > 0 and bitmap.GetWidth() > self._width and \
bitmap.GetHeight() >= self._height:
numImages = bitmap.GetWidth()/self._width
for subIndex in xrange(numImages):
rect = wx.Rect(self._width * subIndex, 0, self._width, self._height)
tmpBmp = bitmap.GetSubBitmap(rect)
self._images.append(tmpBmp)
else:
self._images.append(bitmap)
else:
self._images.append(bitmap)
if self._width == 0 and self._height == 0:
self._width = bitmap.GetWidth()
self._height = bitmap.GetHeight()
return index
[docs] def AddIcon(self, icon):
"""
Adds a new image using an icon.
:param `icon`: a valid `wx.Icon` object.
:return: The new zero-based image index.
:note: If the icon is wider than the images in the list and you are not using
the ``IL_VARIABLE_SIZE`` style, then the icon will automatically be split
into smaller images, each matching the dimensions of the image list.
"""
return self.Add(wx.BitmapFromIcon(icon))
[docs] def AddWithColourMask(self, bitmap, maskColour):
"""
Adds a new image or images using a bitmap and a colour mask.
:param `bitmap`: a valid `wx.Bitmap` object;
:param `colour`: an instance of `wx.Colour`, a colour indicating which parts
of the image are transparent.
:return: The new zero-based image index.
:note: If the bitmap is wider than the images in the list and you are not using
the ``IL_VARIABLE_SIZE`` style, then the bitmap will automatically be split
into smaller images, each matching the dimensions of the image list.
"""
img = bitmap.ConvertToImage()
img.SetMaskColour(maskColour.Red(), maskColour.Green(), maskColour.Blue())
return self.Add(wx.BitmapFromImage(img))
[docs] def GetBitmap(self, index):
"""
Returns the bitmap corresponding to the given `index`, or `wx.NullBitmap`
if the index is invalid.
:param `index`: the bitmap index.
"""
if index >= len(self._images):
return wx.NullBitmap
return self._images[index]
[docs] def GetIcon(self, index):
"""
Returns the icon corresponding to the given `index`, or `wx.NullIcon`
if the index is invalid.
:param `index`: the icon index.
"""
if index >= len(self._images):
return wx.NullIcon
return wx.IconFromBitmap(self._images[index])
[docs] def Replace(self, index, bitmap):
"""
Replaces the existing image with the new bitmap.
:param `index`: the index at which the image should be replaced;
:param `bitmap`: the new bitmap to add to the image list, an instance of
`wx.Bitmap`.
"""
if index >= len(self._images):
raise Exception("Wrong index in image list")
self._images[index] = bitmap
return True
[docs] def ReplaceIcon(self, index, icon):
"""
Replaces the existing image with the new icon.
:param `index`: the index at which the image should be replaced;
:param `icon`: the new icon to add to the image list, an instance of
`wx.Icon`.
"""
return self.Replace(index, wx.BitmapFromIcon(icon))
[docs] def Remove(self, index):
"""
Removes the image at the given position.
:param `index`: the zero-based index of the image to be removed.
"""
if index >= len(self._images):
raise Exception("Wrong index in image list")
self._images.pop(index)
return True
[docs] def RemoveAll(self):
""" Removes all the images in the list. """
self._images = []
return True
[docs] def GetSize(self, index):
"""
Retrieves the size of an image in the list.
:param `index`: the zero-based index of the image.
:return: a tuple of `(width, height)` properties of the chosen bitmap.
"""
if index >= len(self._images):
raise Exception("Wrong index in image list")
bmp = self._images[index]
return bmp.GetWidth(), bmp.GetHeight()
[docs] def Draw(self, index, dc, x, y, flags, solidBackground=True):
"""
Draws a specified image onto a device context.
:param `index`: the image index, starting from zero;
:param `dc`: an instance of `wx.DC`;
:param `x`: x position on the device context;
:param `y`: y position on the device context;
:param `flags`: how to draw the image. A bitlist of a selection of the following:
================================= =======================================
Flag Paarameter Description
================================= =======================================
``wx.IMAGELIST_DRAW_NORMAL`` Draw the image normally
``wx.IMAGELIST_DRAW_TRANSPARENT`` Draw the image with transparency
``wx.IMAGELIST_DRAW_SELECTED`` Draw the image in selected state
``wx.IMAGELIST_DRAW_FOCUSED`` Draw the image in a focused state
================================= =======================================
:param `solidBackground`: currently unused.
"""
if index >= len(self._images):
raise Exception("Wrong index in image list")
bmp = self._images[index]
dc.DrawBitmap(bmp, x, y, (flags & wx.IMAGELIST_DRAW_TRANSPARENT) > 0)
return True
[docs]class SelectionStore(object):
"""
SelectionStore is used to store the selected items in the virtual
controls, i.e. it is well suited for storing even when the control contains
a huge (practically infinite) number of items.
Of course, internally it still has to store the selected items somehow (as
an array currently) but the advantage is that it can handle the selection
of all items (common operation) efficiently and that it could be made even
smarter in the future (e.g. store the selections as an array of ranges +
individual items) without changing its API.
"""
[docs] def __init__(self):
""" Default class constructor. """
# the array of items whose selection state is different from default
self._itemsSel = []
# the default state: normally, False (i.e. off) but maybe set to true if
# there are more selected items than non selected ones - this allows to
# handle selection of all items efficiently
self._defaultState = False
# the total number of items we handle
self._count = 0
# special case of SetItemCount(0)
[docs] def Clear(self):
""" Clears the number of selected items. """
self._itemsSel = []
self._count = 0
self._defaultState = False
# return the total number of selected items
[docs] def GetSelectedCount(self):
""" Return the total number of selected items. """
return (self._defaultState and [self._count - len(self._itemsSel)] or [len(self._itemsSel)])[0]
[docs] def IsSelected(self, item):
"""
Returns ``True`` if the given item is selected.
:param `item`: the item to check for selection state.
"""
isSel = item in self._itemsSel
# if the default state is to be selected, being in m_itemsSel means that
# the item is not selected, so we have to inverse the logic
return (self._defaultState and [not isSel] or [isSel])[0]
[docs] def SelectItem(self, item, select=True):
"""
Selects the given item.
:param `item`: the item to select;
:param `select`: ``True`` to select the item, ``False`` otherwise.
:return: ``True`` if the items selection really changed.
"""
# search for the item ourselves as like this we get the index where to
# insert it later if needed, so we do only one search in the array instead
# of two (adding item to a sorted array requires a search)
index = bisect.bisect_right(self._itemsSel, item)
isSel = index < len(self._itemsSel) and self._itemsSel[index] == item
if select != self._defaultState:
if item not in self._itemsSel:
bisect.insort_right(self._itemsSel, item)
return True
else: # reset to default state
if item in self._itemsSel:
self._itemsSel.remove(item)
return True
return False
[docs] def SelectRange(self, itemFrom, itemTo, select=True):
"""
Selects a range of items.
:param `itemFrom`: the first index of the selection range;
:param `itemTo`: the last index of the selection range;
:param `select`: ``True`` to select the items, ``False`` otherwise.
:return: ``True`` and fill the `itemsChanged` array with the indices of items
which have changed state if "few" of them did, otherwise return ``False``
(meaning that too many items changed state to bother counting them individually).
"""
# 100 is hardcoded but it shouldn't matter much: the important thing is
# that we don't refresh everything when really few (e.g. 1 or 2) items
# change state
MANY_ITEMS = 100
# many items (> half) changed state
itemsChanged = []
# are we going to have more [un]selected items than the other ones?
if itemTo - itemFrom > self._count/2:
if select != self._defaultState:
# the default state now becomes the same as 'select'
self._defaultState = select
# so all the old selections (which had state select) shouldn't be
# selected any more, but all the other ones should
selOld = self._itemsSel[:]
self._itemsSel = []
# TODO: it should be possible to optimize the searches a bit
# knowing the possible range
for item in xrange(itemFrom):
if item not in selOld:
self._itemsSel.append(item)
for item in xrange(itemTo + 1, self._count):
if item not in selOld:
self._itemsSel.append(item)
else: # select == self._defaultState
# get the inclusive range of items between itemFrom and itemTo
count = len(self._itemsSel)
start = bisect.bisect_right(self._itemsSel, itemFrom)
end = bisect.bisect_right(self._itemsSel, itemTo)
if itemFrom < start:
start = itemFrom
if start == count or self._itemsSel[start] < itemFrom:
start += 1
if end == count or self._itemsSel[end] > itemTo:
end -= 1
if start <= end:
# delete all of them (from end to avoid changing indices)
for i in xrange(end, start-1, -1):
if itemsChanged:
if len(itemsChanged) > MANY_ITEMS:
# stop counting (see comment below)
itemsChanged = []
else:
itemsChanged.append(self._itemsSel[i])
self._itemsSel.pop(i)
else:
self._itemsSel = []
else: # "few" items change state
if itemsChanged:
itemsChanged = []
# just add the items to the selection
for item in xrange(itemFrom, itemTo+1):
if self.SelectItem(item, select) and itemsChanged:
itemsChanged.append(item)
if len(itemsChanged) > MANY_ITEMS:
# stop counting them, we'll just eat gobs of memory
# for nothing at all - faster to refresh everything in
# this case
itemsChanged = []
# we set it to None if there are many items changing state
return itemsChanged
[docs] def OnItemDelete(self, item):
"""
Must be called when an item is deleted.
:param `item`: the item that is being deleted.
"""
count = len(self._itemsSel)
i = bisect.bisect_right(self._itemsSel, item)
if i < count and self._itemsSel[i] == item:
# this item itself was in m_itemsSel, remove it from there
self._itemsSel.pop(i)
count -= 1
# and adjust the index of all which follow it
while i < count:
i += 1
self._itemsSel[i] -= 1
[docs] def SetItemCount(self, count):
"""
Sets the total number of items we handle.
:param `count`: the total number of items we handle.
"""
# forget about all items whose indices are now invalid if the size
# decreased
if count < self._count:
for i in xrange(len(self._itemsSel), 0, -1):
if self._itemsSel[i - 1] >= count:
self._itemsSel.pop(i - 1)
# remember the new number of items
self._count = count
# ----------------------------------------------------------------------------
# UltimateListItemAttr: a structure containing the visual attributes of an item
# ----------------------------------------------------------------------------
[docs]class UltimateListItemAttr(object):
"""
Represents the attributes (colour, font, ...) of a L{UltimateListCtrl}
L{UltimateListItem}.
"""
[docs] def __init__(self, colText=wx.NullColour, colBack=wx.NullColour, font=wx.NullFont,
enabled=True, footerColText=wx.NullColour, footerColBack=wx.NullColour,
footerFont=wx.NullFont):
"""
Default class constructor.
:param `colText`: the item text colour;
:param `colBack`: the item background colour;
:param `font`: the item font;
:param `enabled`: ``True`` if the item should be enabled, ``False`` if it is disabled;
:param `footerColText`: for footer items, the item text colour;
:param `footerColBack`: for footer items, the item background colour;
:param `footerFont`: for footer items, the item font.
"""
self._colText = colText
self._colBack = colBack
self._font = font
self._enabled = enabled
self._footerColText = footerColText
self._footerColBack = footerColBack
self._footerFont = footerFont
# setters
[docs] def SetTextColour(self, colText):
"""
Sets a new text colour.
:param `colText`: an instance of `wx.Colour`.
"""
self._colText = colText
[docs] def SetBackgroundColour(self, colBack):
"""
Sets a new background colour.
:param `colBack`: an instance of `wx.Colour`.
"""
self._colBack = colBack
[docs] def SetFont(self, font):
"""
Sets a new font for the item.
:param `font`: an instance of `wx.Font`.
"""
self._font = font
[docs] def Enable(self, enable=True):
"""
Enables or disables the item.
:param `enable`: ``True`` to enable the item, ``False`` to disable it.
"""
self._enabled = enable
[docs] def HasTextColour(self):
""" Returns ``True`` if the currently set text colour is valid. """
return self._colText.Ok()
[docs] def HasBackgroundColour(self):
""" Returns ``True`` if the currently set background colour is valid. """
return self._colBack.Ok()
[docs] def HasFont(self):
""" Returns ``True`` if the currently set font is valid. """
return self._font.Ok()
[docs] def GetTextColour(self):
""" Returns the currently set text colour. """
return self._colText
[docs] def GetBackgroundColour(self):
""" Returns the currently set background colour. """
return self._colBack
[docs] def GetFont(self):
""" Returns the currently set item font. """
return self._font
[docs] def IsEnabled(self):
""" Returns ``True`` if the item is enabled. """
return self._enabled
# ----------------------------------------------------------------------------
# UltimateListItem: the item or column info, used to exchange data with UltimateListCtrl
# ----------------------------------------------------------------------------
[docs]class UltimateListItem(wx.Object):
""" This class stores information about a L{UltimateListCtrl} item or column. """
[docs] def __init__(self, item=None):
"""
Default class constructor.
:param `item`: if not ``None``, another instance of L{UltimateListItem}.
"""
if not item:
self.Init()
self._attr = None
else:
self._mask = item._mask # Indicates what fields are valid
self._itemId = item._itemId # The zero-based item position
self._col = item._col # Zero-based column, if in report mode
self._state = item._state # The state of the item
self._stateMask = item._stateMask # Which flags of self._state are valid (uses same flags)
self._text = item._text # The label/header text
self._tooltip = item._tooltip # The label/header tooltip text
self._image = item._image[:] # The zero-based indexes into an image list
self._data = item._data # App-defined data
self._pyData = item._pyData # Python-specific data
self._format = item._format # left, right, centre
self._width = item._width # width of column
self._colour = item._colour # item text colour
self._font = item._font # item font
self._checked = item._checked # The checking state for the item (if kind > 0)
self._kind = item._kind # Whether it is a normal, checkbox-like or a radiobutton-like item
self._enabled = item._enabled # Whether the item is enabled or not
self._hypertext = item._hypertext # indicates if the item is hypertext
self._visited = item._visited # visited state for an hypertext item
self._wnd = item._wnd
self._windowenabled = item._windowenabled
self._windowsize = item._windowsize
self._isColumnShown = item._isColumnShown
self._customRenderer = item._customRenderer
self._overFlow = item._overFlow
self._footerChecked = item._footerChecked
self._footerFormat = item._footerFormat
self._footerImage = item._footerImage
self._footerKind = item._footerKind
self._footerText = item._footerText
self._expandWin = item._expandWin
self._attr = None
# copy list item attributes
if item.HasAttributes():
self._attr = item.GetAttributes()[:]
# resetting
[docs] def Clear(self):
""" Resets the item state to the default. """
self.Init()
self._text = ""
self.ClearAttributes()
[docs] def ClearAttributes(self):
""" Deletes the item attributes if they have been stored. """
if self._attr:
del self._attr
self._attr = None
# setters
[docs] def SetMask(self, mask):
"""
Sets the mask of valid fields.
:param `mask`: any combination of the following bits:
============================ ========= ==============================
Mask Bits Hex Value Description
============================ ========= ==============================
``ULC_MASK_STATE`` 0x1 L{GetState} is valid
``ULC_MASK_TEXT`` 0x2 L{GetText} is valid
``ULC_MASK_IMAGE`` 0x4 L{GetImage} is valid
``ULC_MASK_DATA`` 0x8 L{GetData} is valid
``ULC_MASK_WIDTH`` 0x20 L{GetWidth} is valid
``ULC_MASK_FORMAT`` 0x40 L{GetFormat} is valid
``ULC_MASK_FONTCOLOUR`` 0x80 L{GetTextColour} is valid
``ULC_MASK_FONT`` 0x100 L{GetFont} is valid
``ULC_MASK_BACKCOLOUR`` 0x200 L{GetBackgroundColour} is valid
``ULC_MASK_KIND`` 0x400 L{GetKind} is valid
``ULC_MASK_ENABLE`` 0x800 L{IsEnabled} is valid
``ULC_MASK_CHECK`` 0x1000 L{IsChecked} is valid
``ULC_MASK_HYPERTEXT`` 0x2000 L{IsHyperText} is valid
``ULC_MASK_WINDOW`` 0x4000 L{GetWindow} is valid
``ULC_MASK_PYDATA`` 0x8000 L{GetPyData} is valid
``ULC_MASK_SHOWN`` 0x10000 L{IsShown} is valid
``ULC_MASK_RENDERER`` 0x20000 L{GetCustomRenderer} is valid
``ULC_MASK_OVERFLOW`` 0x40000 L{GetOverFlow} is valid
``ULC_MASK_FOOTER_TEXT`` 0x80000 L{GetFooterText} is valid
``ULC_MASK_FOOTER_IMAGE`` 0x100000 L{GetFooterImage} is valid
``ULC_MASK_FOOTER_FORMAT`` 0x200000 L{GetFooterFormat} is valid
``ULC_MASK_FOOTER_FONT`` 0x400000 L{GetFooterFont} is valid
``ULC_MASK_FOOTER_CHECK`` 0x800000 L{IsFooterChecked} is valid
``ULC_MASK_FOOTER_KIND`` 0x1000000 L{GetFooterKind} is valid
============================ ========= ==============================
"""
self._mask = mask
[docs] def SetId(self, id):
"""
Sets the zero-based item position.
:param `id`: the zero-based item position.
"""
self._itemId = id
[docs] def SetColumn(self, col):
"""
Sets the zero-based column.
:param `col`: the zero-based column.
:note: This method is neaningful only in report mode.
"""
self._col = col
[docs] def SetState(self, state):
"""
Sets the item state flags.
:param `state`: any combination of the following bits:
============================ ========= ==============================
State Bits Hex Value Description
============================ ========= ==============================
``ULC_STATE_DONTCARE`` 0x0 Don't care what the state is
``ULC_STATE_DROPHILITED`` 0x1 The item is highlighted to receive a drop event
``ULC_STATE_FOCUSED`` 0x2 The item has the focus
``ULC_STATE_SELECTED`` 0x4 The item is selected
``ULC_STATE_CUT`` 0x8 The item is in the cut state
``ULC_STATE_DISABLED`` 0x10 The item is disabled
``ULC_STATE_FILTERED`` 0x20 The item has been filtered
``ULC_STATE_INUSE`` 0x40 The item is in use
``ULC_STATE_PICKED`` 0x80 The item has been picked
``ULC_STATE_SOURCE`` 0x100 The item is a drag and drop source
============================ ========= ==============================
:note: The valid state flags are influenced by the value of the state mask.
:see: L{SetStateMask}
"""
self._mask |= ULC_MASK_STATE
self._state = state
self._stateMask |= state
[docs] def SetStateMask(self, stateMask):
"""
Sets the bitmask that is used to determine which of the state flags are
to be set.
:param `stateMask`: the state bitmask.
:see: L{SetState} for a list of valid state bits.
"""
self._stateMask = stateMask
[docs] def SetText(self, text):
"""
Sets the text label for the item.
:param `text`: the text label for the item.
"""
self._mask |= ULC_MASK_TEXT
self._text = text
[docs] def SetImage(self, image):
"""
Sets the zero-based indexes of the images associated with the item into the
image list.
:param `image`: a Python list with the zero-based indexes of the images
associated with the item into the image list.
"""
self._mask |= ULC_MASK_IMAGE
if image is None:
image = []
self._image = to_list(image)
[docs] def SetData(self, data):
"""
Sets client data for the item.
:param `data`: the client data associated to the item.
:note: Please note that client data is associated with the item and not
with subitems.
"""
self._mask |= ULC_MASK_DATA
self._data = data
[docs] def SetPyData(self, pyData):
"""
Sets data for the item, which can be any Python object.
:param `data`: any Python object associated to the item.
:note: Please note that Python data is associated with the item and not
with subitems.
"""
self._mask |= ULC_MASK_PYDATA
self._pyData = pyData
[docs] def SetWidth(self, width):
"""
Sets the column width.
:param `width`: the column width.
:note: This method is meaningful only for column headers in report mode.
"""
self._mask |= ULC_MASK_WIDTH
self._width = width
[docs] def SetAlign(self, align):
"""
Sets the alignment for the item.
:param `align`: one of the following bits:
============================ ========= ==============================
Alignment Bits Hex Value Description
============================ ========= ==============================
``ULC_FORMAT_LEFT`` 0x0 The item is left-aligned
``ULC_FORMAT_RIGHT`` 0x1 The item is right-aligned
``ULC_FORMAT_CENTRE`` 0x2 The item is centre-aligned
``ULC_FORMAT_CENTER`` 0x2 The item is center-aligned
============================ ========= ==============================
"""
self._mask |= ULC_MASK_FORMAT
self._format = align
[docs] def SetTextColour(self, colText):
"""
Sets the text colour for the item.
:param `colText`: a valid `wx.Colour` object.
"""
self.Attributes().SetTextColour(colText)
[docs] def SetBackgroundColour(self, colBack):
"""
Sets the background colour for the item.
:param `colBack`: a valid `wx.Colour` object.
"""
self.Attributes().SetBackgroundColour(colBack)
[docs] def SetFont(self, font):
"""
Sets the font for the item.
:param `font`: a valid `wx.Font` object.
"""
self.Attributes().SetFont(font)
[docs] def Enable(self, enable=True):
"""
Enables or disables the item.
:param `enable`: ``True`` to enable the item, ``False`` to disable it.
"""
self.Attributes().Enable(enable)
# accessors
[docs] def GetMask(self):
"""
Returns a bit mask indicating which fields of the structure are valid.
:see: L{SetMask} for a list of valid bit masks.
"""
return self._mask
[docs] def GetId(self):
""" Returns the zero-based item position. """
return self._itemId
[docs] def GetColumn(self):
"""
Returns the zero-based column.
:note: This method is meaningful only in report mode.
"""
return self._col
[docs] def GetState(self):
"""
Returns a bit field representing the state of the item.
:see: L{SetState} for a list of valid item states.
"""
return self._state & self._stateMask
[docs] def GetText(self):
""" Returns the label/header text. """
return self._text
[docs] def GetImage(self):
"""
Returns a Python list with the zero-based indexes of the images associated
with the item into the image list.
"""
return self._image
[docs] def GetData(self):
"""
Returns client data associated with the control.
:note: Please note that client data is associated with the item and not
with subitems.
"""
return self._data
[docs] def GetPyData(self):
"""
Returns data for the item, which can be any Python object.
:note: Please note that Python data is associated with the item and not
with subitems.
"""
return self._pyData
[docs] def GetWidth(self):
"""
Returns the column width.
:note: This method is meaningful only for column headers in report mode.
"""
return self._width
[docs] def GetAlign(self):
"""
Returns the alignment for the item.
:see: L{SetAlign} for a list of valid alignment bits.
"""
return self._format
[docs] def GetAttributes(self):
""" Returns the associated L{UltimateListItemAttr} attributes. """
return self._attr
[docs] def HasAttributes(self):
""" Returns ``True`` if the item has attributes associated with it. """
return self._attr != None
[docs] def GetTextColour(self):
""" Returns the text colour. """
return (self.HasAttributes() and [self._attr.GetTextColour()] or [wx.NullColour])[0]
[docs] def GetBackgroundColour(self):
""" Returns the background colour. """
return (self.HasAttributes() and [self._attr.GetBackgroundColour()] or [wx.NullColour])[0]
[docs] def GetFont(self):
""" Returns the item font. """
return (self.HasAttributes() and [self._attr.GetFont()] or [wx.NullFont])[0]
[docs] def IsEnabled(self):
""" Returns ``True`` if the item is enabled. """
return (self.HasAttributes() and [self._attr.IsEnabled()] or [True])[0]
# creates self._attr if we don't have it yet
[docs] def Attributes(self):
"""
Returns the associated attributes if they exist, or create a new L{UltimateListItemAttr}
structure and associate it with this item.
"""
if not self._attr:
self._attr = UltimateListItemAttr()
return self._attr
[docs] def SetKind(self, kind):
"""
Sets the item kind.
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
self._mask |= ULC_MASK_KIND
self._kind = kind
[docs] def GetKind(self):
"""
Returns the item kind.
:see: L{SetKind} for a valid list of item's kind.
"""
return self._kind
[docs] def IsChecked(self):
""" Returns whether the item is checked or not. """
return self._checked
[docs] def Check(self, checked=True):
"""
Checks/unchecks an item.
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
:note: This method is meaningful only for check and radio items.
"""
self._mask |= ULC_MASK_CHECK
self._checked = checked
[docs] def IsShown(self):
""" Returns ``True`` if the item is shown, or ``False`` if it is hidden. """
return self._isColumnShown
[docs] def SetShown(self, shown=True):
"""
Sets an item as shown/hidden.
:param `shown`: ``True`` to show the item, ``False`` to hide it.
"""
self._mask |= ULC_MASK_SHOWN
self._isColumnShown = shown
[docs] def SetHyperText(self, hyper=True):
"""
Sets whether the item is hypertext or not.
:param `hyper`: ``True`` to set hypertext behaviour, ``False`` otherwise.
"""
self._mask |= ULC_MASK_HYPERTEXT
self._hypertext = hyper
[docs] def SetVisited(self, visited=True):
"""
Sets whether an hypertext item was visited or not.
:param `visited`: ``True`` to set a hypertext item as visited, ``False`` otherwise.
"""
self._mask |= ULC_MASK_HYPERTEXT
self._visited = visited
[docs] def GetVisited(self):
""" Returns whether an hypertext item was visited or not. """
return self._visited
[docs] def IsHyperText(self):
""" Returns whether the item is hypetext or not. """
return self._hypertext
[docs] def SetWindow(self, wnd, expand=False):
"""
Sets the window associated to the item.
:param `wnd`: a non-toplevel window to be displayed next to the item;
:param `expand`: ``True`` to expand the column where the item/subitem lives,
so that the window will be fully visible.
"""
self._mask |= ULC_MASK_WINDOW
self._wnd = wnd
listCtrl = wnd.GetParent()
mainWin = listCtrl._mainWin
wnd.Reparent(mainWin)
if wnd.GetSizer(): # the window is a complex one hold by a sizer
size = wnd.GetBestSize()
else: # simple window, without sizers
size = wnd.GetSize()
# We have to bind the wx.EVT_SET_FOCUS for the associated window
# No other solution to handle the focus changing from an item in
# UltimateListCtrl and the window associated to an item
# Do better strategies exist?
self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self._windowsize = size
# The window is enabled only if the item is enabled
self._wnd.Enable(self._enabled)
self._windowenabled = self._enabled
self._expandWin = expand
mainWin._hasWindows = True
mainWin._itemWithWindow.append(self)
# This is needed as otherwise widgets that should be invisible
# are shown at the top left corner of ULC
mainWin.HideWindows()
mainWin.Refresh()
[docs] def GetWindow(self):
""" Returns the window associated to the item. """
return self._wnd
[docs] def DeleteWindow(self):
""" Deletes the window associated to the item (if any). """
if self._wnd:
listCtrl = self._wnd.GetParent()
if self in listCtrl._itemWithWindow:
listCtrl._itemWithWindow.remove(self)
self._wnd.Destroy()
self._wnd = None
[docs] def GetWindowEnabled(self):
""" Returns whether the associated window is enabled or not. """
if not self._wnd:
raise Exception("\nERROR: This Item Has No Window Associated")
return self._windowenabled
[docs] def SetWindowEnabled(self, enable=True):
"""
Sets whether the associated window is enabled or not.
:param `enable`: ``True`` to enable the associated window, ``False`` to disable it.
"""
if not self._wnd:
raise Exception("\nERROR: This Item Has No Window Associated")
self._windowenabled = enable
self._wnd.Enable(enable)
[docs] def GetWindowSize(self):
""" Returns the associated window size. """
return self._windowsize
[docs] def SetCustomRenderer(self, renderer):
"""
Associate a custom renderer to this item.
:param `renderer`: a class able to correctly render the item.
:note: the renderer class **must** implement the methods `DrawSubItem`,
`GetLineHeight` and `GetSubItemWidth`.
"""
self._mask |= ULC_MASK_RENDERER
self._customRenderer = renderer
[docs] def GetCustomRenderer(self):
""" Returns the custom renderer associated with this item (if any). """
return self._customRenderer
[docs] def SetOverFlow(self, over=True):
"""
Sets the item in the overflow/non overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `over`: ``True`` to set the item in a overflow state, ``False`` otherwise.
"""
self._mask |= ULC_MASK_OVERFLOW
self._overFlow = over
[docs] def GetOverFlow(self):
"""
Returns if the item is in the overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
"""
return self._overFlow
[docs] def Init(self):
""" Initializes an empty L{UltimateListItem}. """
self._mask = 0
self._itemId = 0
self._col = 0
self._state = 0
self._stateMask = 0
self._image = []
self._data = 0
self._pyData = None
self._text = ""
self._tooltip = ""
self._format = ULC_FORMAT_CENTRE
self._width = 0
self._colour = wx.Colour(0, 0, 0)
self._font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
self._kind = 0
self._checked = False
self._enabled = True
self._hypertext = False # indicates if the item is hypertext
self._visited = False # visited state for an hypertext item
self._wnd = None
self._windowenabled = False
self._windowsize = wx.Size()
self._isColumnShown = True
self._customRenderer = None
self._overFlow = False
self._footerChecked = False
self._footerFormat = ULC_FORMAT_CENTRE
self._footerImage = []
self._footerKind = 0
self._footerText = ""
self._expandWin = False
[docs] def OnSetFocus(self, event):
"""
Handles the ``wx.EVT_SET_FOCUS`` event for the window associated to an item.
:param `event`: a `wx.FocusEvent` event to be processed.
"""
listCtrl = self._wnd.GetParent()
select = listCtrl.GetItemState(self._itemId, ULC_STATE_SELECTED)
# If the window is associated to an item that currently is selected
# (has focus) we don't kill the focus. Otherwise we do it.
if not select:
listCtrl._hasFocus = False
else:
listCtrl._hasFocus = True
listCtrl.SetFocus()
event.Skip()
# ----------------------------------------------------------------------------
# ListEvent - the event class for the UltimateListCtrl notifications
# ----------------------------------------------------------------------------
[docs]class CommandListEvent(wx.PyCommandEvent):
"""
A list event holds information about events associated with L{UltimateListCtrl}
objects.
"""
[docs] def __init__(self, commandTypeOrEvent=None, winid=0):
"""
Default class constructor.
For internal use: do not call it in your code!
:param `commandTypeOrEvent`: the event type or another instance of
`wx.PyCommandEvent`;
:param `winid`: the event identifier.
"""
if type(commandTypeOrEvent) == types.IntType:
wx.PyCommandEvent.__init__(self, commandTypeOrEvent, winid)
self.m_code = 0
self.m_oldItemIndex = 0
self.m_itemIndex = 0
self.m_col = 0
self.m_pointDrag = wx.Point()
self.m_item = UltimateListItem()
self.m_editCancelled = False
else:
wx.PyCommandEvent.__init__(self, commandTypeOrEvent.GetEventType(), commandTypeOrEvent.GetId())
self.m_code = commandTypeOrEvent.m_code
self.m_oldItemIndex = commandTypeOrEvent.m_oldItemIndex
self.m_itemIndex = commandTypeOrEvent.m_itemIndex
self.m_col = commandTypeOrEvent.m_col
self.m_pointDrag = commandTypeOrEvent.m_pointDrag
self.m_item = commandTypeOrEvent.m_item
self.m_editCancelled = commandTypeOrEvent.m_editCancelled
[docs] def GetKeyCode(self):
""" Returns the key code if the event is a keypress event. """
return self.m_code
[docs] def GetIndex(self):
""" Returns the item index. """
return self.m_itemIndex
[docs] def GetColumn(self):
"""
Returns the column position: it is only used with ``COL`` events.
For the column dragging events, it is the column to the left of the divider
being dragged, for the column click events it may be -1 if the user clicked
in the list control header outside any column.
"""
return self.m_col
[docs] def GetPoint(self):
""" Returns the position of the mouse pointer if the event is a drag event. """
return self.m_pointDrag
[docs] def GetLabel(self):
""" Returns the (new) item label for ``EVT_LIST_END_LABEL_EDIT`` event. """
return self.m_item._text
[docs] def GetText(self):
""" Returns the item text. """
return self.m_item._text
[docs] def GetImage(self):
""" Returns the item image. """
return self.m_item._image
[docs] def GetData(self):
""" Returns the item data. """
return self.m_item._data
[docs] def GetMask(self):
""" Returns the item mask. """
return self.m_item._mask
[docs] def GetItem(self):
""" Returns the item itself. """
return self.m_item
# for wxEVT_COMMAND_LIST_CACHE_HINT only
[docs] def GetCacheFrom(self):
"""
Returns the first item which the list control advises us to cache.
:note: This method is meaningful for ``EVT_LIST_CACHE_HINT`` event only.
"""
return self.m_oldItemIndex
[docs] def GetCacheTo(self):
"""
Returns the last item (inclusive) which the list control advises us to cache.
:note: This method is meaningful for ``EVT_LIST_CACHE_HINT`` event only.
"""
return self.m_itemIndex
# was label editing canceled? (for wxEVT_COMMAND_LIST_END_LABEL_EDIT only)
[docs] def IsEditCancelled(self):
"""
Returns ``True`` if it the label editing has been cancelled by the user
(L{GetLabel} returns an empty string in this case but it doesn't allow
the application to distinguish between really cancelling the edit and
the admittedly rare case when the user wants to rename it to an empty
string).
:note: This method only makes sense for ``EVT_LIST_END_LABEL_EDIT`` messages.
"""
return self.m_editCancelled
[docs] def SetEditCanceled(self, editCancelled):
"""
Sets the item editing as cancelled/not cancelled.
:param `editCancelled`: ``True`` to set the item editing as cancelled, ``False``
otherwise.
:note: This method only makes sense for ``EVT_LIST_END_LABEL_EDIT`` messages.
"""
self.m_editCancelled = editCancelled
# ----------------------------------------------------------------------------
# UltimateListEvent is a special class for all events associated with list controls
#
# NB: note that not all accessors make sense for all events, see the event
# descriptions below
# ----------------------------------------------------------------------------
[docs]class UltimateListEvent(CommandListEvent):
"""
A list event holds information about events associated with L{UltimateListCtrl}
objects.
"""
[docs] def __init__(self, commandTypeOrEvent=None, winid=0):
"""
Default class constructor.
For internal use: do not call it in your code!
:param `commandTypeOrEvent`: the event type or another instance of
`wx.PyCommandEvent`;
:param `winid`: the event identifier.
"""
CommandListEvent.__init__(self, commandTypeOrEvent, winid)
if type(commandTypeOrEvent) == types.IntType:
self.notify = wx.NotifyEvent(commandTypeOrEvent, winid)
else:
self.notify = wx.NotifyEvent(commandTypeOrEvent.GetEventType(), commandTypeOrEvent.GetId())
[docs] def GetNotifyEvent(self):
""" Returns the actual `wx.NotifyEvent`. """
return self.notify
[docs] def IsAllowed(self):
"""
Returns ``True`` if the change is allowed (L{Veto} hasn't been called) or
``False`` otherwise (if it was).
"""
return self.notify.IsAllowed()
[docs] def Veto(self):
"""
Prevents the change announced by this event from happening.
:note: It is in general a good idea to notify the user about the reasons
for vetoing the change because otherwise the applications behaviour (which
just refuses to do what the user wants) might be quite surprising.
"""
self.notify.Veto()
[docs] def Allow(self):
"""
This is the opposite of L{Veto}: it explicitly allows the event to be processed.
For most events it is not necessary to call this method as the events are
allowed anyhow but some are forbidden by default (this will be mentioned
in the corresponding event description).
"""
self.notify.Allow()
# ============================================================================
# private classes
# ============================================================================
#-----------------------------------------------------------------------------
# ColWidthInfo (internal)
#-----------------------------------------------------------------------------
[docs]class ColWidthInfo(object):
""" A simple class which holds information about L{UltimateListCtrl} columns. """
[docs] def __init__(self, w=0, needsUpdate=True):
"""
Default class constructor
:param `w`: the initial width of the column;
:param `needsUpdate`: ``True`` if the column needs refreshing, ``False``
otherwise.
"""
self._nMaxWidth = w
self._bNeedsUpdate = needsUpdate
#-----------------------------------------------------------------------------
# UltimateListItemData (internal)
#-----------------------------------------------------------------------------
[docs]class UltimateListItemData(object):
"""
A simple class which holds information about L{UltimateListItem} visual
attributes (client rectangles, positions, etc...).
"""
[docs] def __init__(self, owner):
"""
Default class constructor
:param `owner`: an instance of L{UltimateListCtrl}.
"""
# the list ctrl we are in
self._owner = owner
self.Init()
# the item coordinates are not used in report mode, instead this pointer
# is None and the owner window is used to retrieve the item position and
# size
if owner.InReportView():
self._rect = None
else:
self._rect = wx.Rect()
[docs] def SetImage(self, image):
"""
Sets the zero-based indexes of the images associated with the item into the
image list.
:param `image`: a Python list with the zero-based indexes of the images
associated with the item into the image list.
"""
self._image = to_list(image)
[docs] def SetData(self, data):
"""
Sets client data for the item.
:param `data`: the client data associated to the item.
:note: Please note that client data is associated with the item and not
with subitems.
"""
self._data = data
[docs] def HasText(self):
""" Returns ``True`` if the item text is not the empty string. """
return self._text != ""
[docs] def GetText(self):
""" Returns the item text. """
return self._text
[docs] def GetBackgroundColour(self):
""" Returns the currently set background colour. """
return self._backColour
[docs] def GetColour(self):
""" Returns the currently set text colour. """
return self._colour
[docs] def GetFont(self):
""" Returns the currently set font. """
return (self._hasFont and [self._font] or [wx.NullFont])[0]
[docs] def SetText(self, text):
"""
Sets the text label for the item.
:param `text`: the text label for the item.
"""
self._text = text
[docs] def SetColour(self, colour):
"""
Sets the text colour for the item.
:param `colour`: an instance of `wx.Colour`.
"""
if colour == wx.NullColour or colour == None:
if self._hasColour:
self._hasColour = False
del self._colour
return
self._hasColour = True
self._colour = colour
[docs] def SetFont(self, font):
"""
Sets the text font for the item.
:param `font`: an instance of `wx.Font`.
"""
if font == wx.NullFont:
self._hasFont = False
del self._font
return
self._hasFont = True
self._font = font
[docs] def SetBackgroundColour(self, colour):
"""
Sets the background colour for the item.
:param `colour`: an instance of `wx.Colour`.
"""
if colour == wx.NullColour:
self._hasBackColour = False
del self._backColour
return
self._hasBackColour = True
self._backColour = colour
# we can't use empty string for measuring the string width/height, so
# always return something
[docs] def GetTextForMeasuring(self):
"""
Returns the item text or a simple string if the item text is the
empty string.
"""
s = self.GetText()
if not s.strip():
s = 'H'
return s
[docs] def GetImage(self):
"""
Returns a Python list with the zero-based indexes of the images associated
with the item into the image list.
"""
return self._image
[docs] def HasImage(self):
""" Returns ``True`` if the item has at least one image associated with it. """
return len(self._image) > 0
[docs] def SetKind(self, kind):
"""
Sets the item kind.
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
self._kind = kind
[docs] def GetKind(self):
"""
Returns the item kind.
:see: L{SetKind} for a list of valid item kinds.
"""
return self._kind
[docs] def IsChecked(self):
""" Returns whether the item is checked or not. """
return self._checked
[docs] def Check(self, checked=True):
"""
Checks/unchecks an item.
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
:note: This method is meaningful only for check and radio items.
"""
self._checked = checked
[docs] def SetHyperText(self, hyper=True):
"""
Sets whether the item is hypertext or not.
:param `hyper`: ``True`` to set hypertext behaviour, ``False`` otherwise.
"""
self._hypertext = hyper
[docs] def SetVisited(self, visited=True):
"""
Sets whether an hypertext item was visited or not.
:param `visited`: ``True`` to set a hypertext item as visited, ``False`` otherwise.
"""
self._visited = visited
[docs] def GetVisited(self):
"""Returns whether an hypertext item was visited or not."""
return self._visited
[docs] def IsHyperText(self):
"""Returns whether the item is hypetext or not."""
return self._hypertext
[docs] def SetWindow(self, wnd, expand=False):
"""
Sets the window associated to the item.
:param `wnd`: a non-toplevel window to be displayed next to the item;
:param `expand`: ``True`` to expand the column where the item/subitem lives,
so that the window will be fully visible.
"""
self._mask |= ULC_MASK_WINDOW
self._wnd = wnd
if wnd.GetSizer(): # the window is a complex one hold by a sizer
size = wnd.GetBestSize()
else: # simple window, without sizers
size = wnd.GetSize()
# We have to bind the wx.EVT_SET_FOCUS for the associated window
# No other solution to handle the focus changing from an item in
# UltimateListCtrl and the window associated to an item
# Do better strategies exist?
self._windowsize = size
# The window is enabled only if the item is enabled
self._wnd.Enable(self._enabled)
self._windowenabled = self._enabled
self._expandWin = expand
[docs] def GetWindow(self):
""" Returns the window associated to the item. """
return self._wnd
[docs] def DeleteWindow(self):
""" Deletes the window associated to the item (if any). """
if self._wnd:
self._wnd.Destroy()
self._wnd = None
[docs] def GetWindowEnabled(self):
""" Returns whether the associated window is enabled or not. """
if not self._wnd:
raise Exception("\nERROR: This Item Has No Window Associated")
return self._windowenabled
[docs] def SetWindowEnabled(self, enable=True):
"""
Sets whether the associated window is enabled or not.
:param `enable`: ``True`` to enable the associated window, ``False`` to disable it.
"""
if not self._wnd:
raise Exception("\nERROR: This Item Has No Window Associated")
self._windowenabled = enable
self._wnd.Enable(enable)
[docs] def GetWindowSize(self):
""" Returns the associated window size. """
return self._windowsize
[docs] def SetAttr(self, attr):
"""
Sets the item attributes.
:param `attr`: an instance of L{UltimateListItemAttr}.
"""
self._attr = attr
[docs] def GetAttr(self):
""" Returns the item attributes. """
return self._attr
[docs] def HasColour(self):
""" Returns ``True`` if the currently set text colour is valid. """
return self._hasColour
[docs] def HasFont(self):
""" Returns ``True`` if the currently set font is valid. """
return self._hasFont
[docs] def HasBackgroundColour(self):
""" Returns ``True`` if the currently set background colour is valid. """
return self._hasBackColour
[docs] def SetCustomRenderer(self, renderer):
"""
Associate a custom renderer to this item.
:param `renderer`: a class able to correctly render the item.
:note: the renderer class **must** implement the methods `DrawSubItem`,
`GetLineHeight` and `GetSubItemWidth`.
"""
self._mask |= ULC_MASK_RENDERER
self._customRenderer = renderer
[docs] def GetCustomRenderer(self):
""" Returns the custom renderer associated with this item (if any). """
return self._customRenderer
[docs] def SetOverFlow(self, over=True):
"""
Sets the item in the overflow/non overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `over`: ``True`` to set the item in a overflow state, ``False`` otherwise.
"""
self._mask |= ULC_MASK_OVERFLOW
self._overFlow = over
[docs] def GetOverFlow(self):
"""
Returns if the item is in the overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
"""
return self._overFlow
[docs] def Init(self):
""" Initializes the item data structure. """
# the item image or -1
self._image = []
# user data associated with the item
self._data = 0
self._pyData = None
self._colour = wx.Colour(0, 0, 0)
self._hasColour = False
self._hasFont = False
self._hasBackColour = False
self._text = ""
self._tooltip = ""
# kind = 0: normal item
# kind = 1: checkbox-type item
self._kind = 0
self._checked = False
self._enabled = True
# custom attributes or None
self._attr = None
self._hypertext = False
self._visited = False
self._wnd = None
self._windowenabled = True
self._windowsize = wx.Size()
self._isColumnShown = True
self._customRenderer = None
self._overFlow = False
self._expandWin = False
[docs] def SetItem(self, info):
"""
Sets information about the item.
:param `info`: an instance of L{UltimateListItemData}.
"""
if info._mask & ULC_MASK_TEXT:
CheckVariableRowHeight(self._owner, info._text)
self.SetText(info._text)
if info._mask & ULC_MASK_TOOLTIP:
self.SetToolTip(info._tooltip)
if info._mask & ULC_MASK_KIND:
self._kind = info._kind
if info._mask & ULC_MASK_CHECK:
self._checked = info._checked
if info._mask & ULC_MASK_ENABLE:
self._enabled = info._enabled
if info._mask & ULC_MASK_IMAGE:
self._image = info._image[:]
if info._mask & ULC_MASK_DATA:
self._data = info._data
if info._mask & ULC_MASK_PYDATA:
self._pyData = info._pyData
if info._mask & ULC_MASK_HYPERTEXT:
self._hypertext = info._hypertext
self._visited = info._visited
if info._mask & ULC_MASK_FONTCOLOUR:
self.SetColour(info.GetTextColour())
if info._mask & ULC_MASK_FONT:
self.SetFont(info.GetFont())
if info._mask & ULC_MASK_BACKCOLOUR:
self.SetBackgroundColour(info.GetBackgroundColour())
if info._mask & ULC_MASK_WINDOW:
self._wnd = info._wnd
self._windowenabled = info._windowenabled
self._windowsize = info._windowsize
self._expandWin = info._expandWin
if info._mask & ULC_MASK_SHOWN:
self._isColumnShown = info._isColumnShown
if info._mask & ULC_MASK_RENDERER:
self._customRenderer = info._customRenderer
if info._mask & ULC_MASK_OVERFLOW:
self._overFlow = info._overFlow
if info.HasAttributes():
if self._attr:
self._attr = info.GetAttributes()
else:
self._attr = UltimateListItemAttr(info.GetTextColour(), info.GetBackgroundColour(),
info.GetFont(), info.IsEnabled(), info.GetFooterTextColour(),
info.GetFooterBackgroundColour(), info.GetFooterFont())
if self._rect:
self._rect.x = -1
self._rect.y = -1
self._rect.height = 0
self._rect.width = info._width
[docs] def SetPosition(self, x, y):
"""
Sets the item position.
:param `x`: the item `x` position;
:param `y`: the item `y` position.
"""
self._rect.x = x
self._rect.y = y
[docs] def SetSize(self, width, height):
"""
Sets the item size.
:param `width`: the item width, in pixels;
:param `height`: the item height, in pixels.
"""
if width != -1:
self._rect.width = width
if height != -1:
self._rect.height = height
[docs] def IsHit(self, x, y):
"""
Returns ``True`` if the input position is inside the item client rectangle.
:param `x`: the `x` mouse position;
:param `y`: the `y` mouse position.
"""
return wx.Rect(self.GetX(), self.GetY(), self.GetWidth(), self.GetHeight()).Contains((x, y))
[docs] def GetX(self):
""" Returns the item `x` position. """
return self._rect.x
[docs] def GetY(self):
""" Returns the item `y` position. """
return self._rect.y
[docs] def GetWidth(self):
""" Returns the item width, in pixels. """
return self._rect.width
[docs] def GetHeight(self):
""" Returns the item height, in pixels. """
return self._rect.height
[docs] def GetItem(self, info):
"""
Returns information about the item.
:param `info`: an instance of L{UltimateListItemData}.
"""
mask = info._mask
if not mask:
# by default, get everything for backwards compatibility
mask = -1
if mask & ULC_MASK_TEXT:
info._text = self._text
if mask & ULC_MASK_TOOLTIP:
info._tooltip = self._tooltip
if mask & ULC_MASK_IMAGE:
info._image = self._image[:]
if mask & ULC_MASK_DATA:
info._data = self._data
if mask & ULC_MASK_PYDATA:
info._pyData = self._pyData
if info._mask & ULC_MASK_FONT:
info.SetFont(self.GetFont())
if mask & ULC_MASK_KIND:
info._kind = self._kind
if mask & ULC_MASK_CHECK:
info._checked = self._checked
if mask & ULC_MASK_ENABLE:
info._enabled = self._enabled
if mask & ULC_MASK_HYPERTEXT:
info._hypertext = self._hypertext
info._visited = self._visited
if mask & ULC_MASK_WINDOW:
info._wnd = self._wnd
info._windowenabled = self._windowenabled
info._windowsize = self._windowsize
info._expandWin = self._expandWin
if mask & ULC_MASK_SHOWN:
info._isColumnShown = self._isColumnShown
if mask & ULC_MASK_RENDERER:
info._customRenderer = self._customRenderer
if mask & ULC_MASK_OVERFLOW:
info._overFlow = self._overFlow
if self._attr:
if self._attr.HasTextColour():
info.SetTextColour(self._attr.GetTextColour())
if self._attr.HasBackgroundColour():
info.SetBackgroundColour(self._attr.GetBackgroundColour())
if self._attr.HasFont():
info.SetFont(self._attr.GetFont())
info.Enable(self._attr.IsEnabled())
return info
[docs] def IsEnabled(self):
""" Returns ``True`` if the item is enabled, ``False`` if it is disabled. """
return self._enabled
[docs] def Enable(self, enable=True):
"""
Enables or disables the item.
:param `enable`: ``True`` to enable the item, ``False`` to disable it.
"""
self._enabled = enable
#-----------------------------------------------------------------------------
# UltimateListHeaderData (internal)
#-----------------------------------------------------------------------------
[docs]class GeometryInfo(object):
"""
A simple class which holds items geometries for L{UltimateListCtrl} not in
report mode.
"""
[docs] def __init__(self):
""" Default class constructor. """
# total item rect
self._rectAll = wx.Rect()
# label only
self._rectLabel = wx.Rect()
# icon only
self._rectIcon = wx.Rect()
# the part to be highlighted
self._rectHighlight = wx.Rect()
# the checkbox/radiobutton rect (if any)
self._rectCheck = wx.Rect()
# extend all our rects to be centered inside the one of given width
[docs] def ExtendWidth(self, w):
"""
Extends all our rectangles to be centered inside the one of given width.
:param `w`: the given width.
"""
if self._rectAll.width > w:
raise Exception("width can only be increased")
self._rectAll.width = w
self._rectLabel.x = self._rectAll.x + (w - self._rectLabel.width)/2
self._rectIcon.x = self._rectAll.x + (w - self._rectIcon.width)/2
self._rectHighlight.x = self._rectAll.x + (w - self._rectHighlight.width)/2
#-----------------------------------------------------------------------------
# UltimateListLineData (internal)
#-----------------------------------------------------------------------------
[docs]class UltimateListLineData(object):
""" A simple class which holds line geometries for L{UltimateListCtrl}. """
[docs] def __init__(self, owner):
"""
Default class constructor.
:param `owner`: an instance of L{UltimateListCtrl}.
"""
# the list of subitems: only may have more than one item in report mode
self._items = []
# is this item selected? [NB: not used in virtual mode]
self._highlighted = False
# back pointer to the list ctrl
self._owner = owner
self._height = self._width = self._x = self._y = -1
if self.InReportView():
self._gi = None
else:
self._gi = GeometryInfo()
if self.GetMode() in [ULC_REPORT, ULC_TILE] or self.HasMode(ULC_HEADER_IN_ALL_VIEWS):
self.InitItems(self._owner.GetColumnCount())
else:
self.InitItems(1)
[docs] def SetReportView(self, inReportView):
"""
Sets whether L{UltimateListLineData} is in report view or not.
:param `inReportView`: ``True`` to set L{UltimateListLineData} in report view, ``False``
otherwise.
"""
# we only need m_gi when we're not in report view so update as needed
if inReportView:
del self._gi
self._gi = None
else:
self._gi = GeometryInfo()
[docs] def GetHeight(self):
""" Returns the line height, in pixels. """
return self._height
[docs] def SetHeight(self, height):
"""
Sets the line height.
:param `height`: the new line height.
"""
self._height = height
[docs] def GetWidth(self):
""" Returns the line width. """
return self._width
[docs] def SetWidth(self, width):
"""
Sets the line width.
:param `width`: the new line width.
"""
self._width = width
[docs] def GetX(self):
""" Returns the line `x` position. """
return self._x
[docs] def SetX(self, x):
"""
Sets the line `x` position.
:param `x`: the new line `x` position.
"""
self._x = x
[docs] def GetY(self):
""" Returns the line `y` position. """
return self._y
[docs] def SetY(self, y):
"""
Sets the line `y` position.
:param `y`: the new line `y` position.
"""
self._y = y
[docs] def ResetDimensions(self):
""" Resets the line dimensions (client rectangle). """
self._height = self._width = self._x = self._y = -1
[docs] def HasImage(self, col=0):
"""
Returns ``True`` if the first item in the line has at least one image
associated with it.
"""
return self.GetImage(col) != []
[docs] def HasText(self):
"""
Returns ``True`` if the text of first item in the line is not the empty
string.
"""
return self.GetText(0) != ""
[docs] def IsHighlighted(self):
""" Returns ``True`` if the line is highlighted. """
if self.IsVirtual():
raise Exception("unexpected call to IsHighlighted")
return self._highlighted
[docs] def GetMode(self):
""" Returns the current highlighting mode. """
return self._owner.GetListCtrl().GetAGWWindowStyleFlag() & ULC_MASK_TYPE
[docs] def HasMode(self, mode):
"""
Returns ``True`` if the parent L{UltimateListCtrl} has the window
style specified by `mode`.
:param `mode`: the window style to check.
"""
return self._owner.GetListCtrl().HasAGWFlag(mode)
[docs] def InReportView(self):
""" Returns ``True`` if the parent L{UltimateListCtrl} is in report view. """
return self._owner.HasAGWFlag(ULC_REPORT)
[docs] def IsVirtual(self):
""" Returns ``True`` if the parent L{UltimateListCtrl} has the ``ULC_VIRTUAL`` style set. """
return self._owner.IsVirtual()
[docs] def CalculateSize(self, dc, spacing):
"""
Calculates the line size and item positions.
:param `dc`: an instance of `wx.DC`;
:param `spacing`: the spacing between the items, in pixels.
"""
item = self._items[0]
mode = self.GetMode()
if mode in [ULC_ICON, ULC_SMALL_ICON]:
self._gi._rectAll.width = spacing
s = item.GetText()
if not s:
lh = -1
self._gi._rectLabel.width = 0
self._gi._rectLabel.height = 0
else:
lw, lh = dc.GetTextExtent(s)
lw += EXTRA_WIDTH
lh += EXTRA_HEIGHT
self._gi._rectAll.height = spacing + lh
if lw > spacing:
self._gi._rectAll.width = lw
self._gi._rectLabel.width = lw
self._gi._rectLabel.height = lh
if item.HasImage():
w, h = self._owner.GetImageSize(item.GetImage())
self._gi._rectIcon.width = w + 8
self._gi._rectIcon.height = h + 8
if self._gi._rectIcon.width > self._gi._rectAll.width:
self._gi._rectAll.width = self._gi._rectIcon.width
if self._gi._rectIcon.height + lh > self._gi._rectAll.height - 4:
self._gi._rectAll.height = self._gi._rectIcon.height + lh + 4
if item.HasText():
self._gi._rectHighlight.width = self._gi._rectLabel.width
self._gi._rectHighlight.height = self._gi._rectLabel.height
else:
self._gi._rectHighlight.width = self._gi._rectIcon.width
self._gi._rectHighlight.height = self._gi._rectIcon.height
elif mode == ULC_LIST:
s = item.GetTextForMeasuring()
lw, lh = dc.GetTextExtent(s)
lw += EXTRA_WIDTH
lh += EXTRA_HEIGHT
self._gi._rectLabel.width = lw
self._gi._rectLabel.height = lh
self._gi._rectAll.width = lw
self._gi._rectAll.height = lh
if item.HasImage():
w, h = self._owner.GetImageSize(item.GetImage())
h += 4
self._gi._rectIcon.width = w
self._gi._rectIcon.height = h
self._gi._rectAll.width += 4 + w
if h > self._gi._rectAll.height:
self._gi._rectAll.height = h
if item.GetKind() in [1, 2]:
w, h = self._owner.GetCheckboxImageSize()
h += 4
self._gi._rectCheck.width = w
self._gi._rectCheck.height = h
self._gi._rectAll.width += 4 + w
if h > self._gi._rectAll.height:
self._gi._rectAll.height = h
self._gi._rectHighlight.width = self._gi._rectAll.width
self._gi._rectHighlight.height = self._gi._rectAll.height
elif mode == ULC_REPORT:
raise Exception("unexpected call to SetSize")
else:
raise Exception("unknown mode")
[docs] def SetPosition(self, x, y, spacing):
"""
Sets the line position.
:param `x`: the current `x` coordinate;
:param `y`: the current `y` coordinate;
:param `spacing`: the spacing between items, in pixels.
"""
item = self._items[0]
mode = self.GetMode()
if mode in [ULC_ICON, ULC_SMALL_ICON]:
self._gi._rectAll.x = x
self._gi._rectAll.y = y
if item.HasImage():
self._gi._rectIcon.x = self._gi._rectAll.x + 4 + (self._gi._rectAll.width - self._gi._rectIcon.width)/2
self._gi._rectIcon.y = self._gi._rectAll.y + 4
if item.HasText():
if self._gi._rectLabel.width > spacing:
self._gi._rectLabel.x = self._gi._rectAll.x + 2
else:
self._gi._rectLabel.x = self._gi._rectAll.x + 2 + (spacing/2) - (self._gi._rectLabel.width/2)
self._gi._rectLabel.y = self._gi._rectAll.y + self._gi._rectAll.height + 2 - self._gi._rectLabel.height
self._gi._rectHighlight.x = self._gi._rectLabel.x - 2
self._gi._rectHighlight.y = self._gi._rectLabel.y - 2
else:
self._gi._rectHighlight.x = self._gi._rectIcon.x - 4
self._gi._rectHighlight.y = self._gi._rectIcon.y - 4
elif mode == ULC_LIST:
self._gi._rectAll.x = x
self._gi._rectAll.y = y
wcheck = hcheck = 0
if item.GetKind() in [1, 2]:
wcheck, hcheck = self._owner.GetCheckboxImageSize()
wcheck += 2
self._gi._rectCheck.x = self._gi._rectAll.x + 2
self._gi._rectCheck.y = self._gi._rectAll.y + 2
self._gi._rectHighlight.x = self._gi._rectAll.x
self._gi._rectHighlight.y = self._gi._rectAll.y
self._gi._rectLabel.y = self._gi._rectAll.y + 2
if item.HasImage():
self._gi._rectIcon.x = self._gi._rectAll.x + wcheck + 2
self._gi._rectIcon.y = self._gi._rectAll.y + 2
self._gi._rectLabel.x = self._gi._rectAll.x + 6 + self._gi._rectIcon.width + wcheck
else:
self._gi._rectLabel.x = self._gi._rectAll.x + 2 + wcheck
elif mode == ULC_REPORT:
raise Exception("unexpected call to SetPosition")
else:
raise Exception("unknown mode")
[docs] def InitItems(self, num):
"""
Initializes the list of items.
:param `num`: the initial number of items to store.
"""
for i in xrange(num):
self._items.append(UltimateListItemData(self._owner))
[docs] def SetItem(self, index, info):
"""
Sets information about the item.
:param `index`: the index of the item;
:param `info`: an instance of L{UltimateListItem}.
"""
item = self._items[index]
item.SetItem(info)
[docs] def GetItem(self, index, info):
"""
Returns information about the item.
:param `index`: the index of the item;
:param `info`: an instance of L{UltimateListItem}.
"""
item = self._items[index]
return item.GetItem(info)
[docs] def GetText(self, index):
"""
Returns the item text at the position `index`.
:param `index`: the index of the item.
"""
item = self._items[index]
return item.GetText()
[docs] def SetText(self, index, s):
"""
Sets the item text at the position `index`.
:param `index`: the index of the item;
:param `s`: the new item text.
"""
item = self._items[index]
item.SetText(s)
[docs] def SetImage(self, index, image):
"""
Sets the zero-based indexes of the images associated with the item into the
image list.
:param `index`: the index of the item;
:param `image`: a Python list with the zero-based indexes of the images
associated with the item into the image list.
"""
item = self._items[index]
item.SetImage(image)
[docs] def GetImage(self, index=0):
"""
Returns a Python list with the zero-based indexes of the images associated
with the item into the image list.
:param `index`: the index of the item.
"""
item = self._items[index]
return item.GetImage()
[docs] def Check(self, index, checked=True):
"""
Checks/unchecks an item.
:param `index`: the index of the item;
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
:note: This method is meaningful only for check and radio items.
"""
item = self._items[index]
item.Check(checked)
[docs] def SetKind(self, index, kind=0):
"""
Sets the item kind.
:param `index`: the index of the item;
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
item = self._items[index]
item.SetKind(kind)
[docs] def GetKind(self, index=0):
"""
Returns the item kind.
:param `index`: the index of the item.
:see: L{SetKind} for a list of valid item kinds.
"""
item = self._items[index]
return item.GetKind()
[docs] def IsChecked(self, index):
"""
Returns whether the item is checked or not.
:param `index`: the index of the item.
"""
item = self._items[index]
return item.IsChecked()
[docs] def SetColour(self, index, c):
"""
Sets the text colour for the item.
:param `index`: the index of the item;
:param `c`: an instance of `wx.Colour`.
"""
item = self._items[index]
item.SetColour(c)
[docs] def GetAttr(self):
"""
Returns an instance of L{UltimateListItemAttr} associated with the first item
in the line.
"""
item = self._items[0]
return item.GetAttr()
[docs] def SetAttr(self, attr):
"""
Sets an instance of L{UltimateListItemAttr} to the first item in the line.
:param `attr`: an instance of L{UltimateListItemAttr}.
"""
item = self._items[0]
item.SetAttr(attr)
[docs] def SetAttributes(self, dc, attr, highlighted):
"""
Sets various attributes to the input device context.
:param `dc`: an instance of `wx.DC`;
:param `attr`: an instance of L{UltimateListItemAttr};
:param `highlighted`: ``True`` if the item is highlighted, ``False`` otherwise.
"""
listctrl = self._owner.GetParent()
# fg colour
# don't use foreground colour for drawing highlighted items - this might
# make them completely invisible (and there is no way to do bit
# arithmetics on wxColour, unfortunately)
if not self._owner.HasAGWFlag(ULC_BORDER_SELECT) and not self._owner.HasAGWFlag(ULC_NO_FULL_ROW_SELECT):
if highlighted:
if wx.Platform == "__WXMAC__":
if self._owner.HasFocus():
colText = wx.WHITE
else:
colText = wx.BLACK
else:
colText = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
else:
if attr and attr.HasTextColour():
colText = attr.GetTextColour()
else:
colText = listctrl.GetForegroundColour()
elif attr and attr.HasTextColour():
colText = attr.GetTextColour()
else:
colText = listctrl.GetForegroundColour()
dc.SetTextForeground(colText)
# font
if attr and attr.HasFont():
font = attr.GetFont()
else:
font = listctrl.GetFont()
dc.SetFont(font)
# bg colour
hasBgCol = attr and attr.HasBackgroundColour()
if highlighted or hasBgCol:
if highlighted:
dc.SetBrush(self._owner.GetHighlightBrush())
else:
dc.SetBrush(wx.Brush(attr.GetBackgroundColour(), wx.SOLID))
dc.SetPen(wx.TRANSPARENT_PEN)
return True
return False
[docs] def Draw(self, line, dc):
"""
Draws the line on the specified device context.
:param `line`: an instance of L{UltimateListLineData};
:param `dc`: an instance of `wx.DC`.
"""
item = self._items[0]
highlighted = self.IsHighlighted()
attr = self.GetAttr()
useGradient, gradientStyle = self._owner._usegradients, self._owner._gradientstyle
useVista = self._owner._vistaselection
hasFocus = self._owner._hasFocus
borderOnly = self._owner.HasAGWFlag(ULC_BORDER_SELECT)
drawn = False
if self.SetAttributes(dc, attr, highlighted):
drawn = True
if not borderOnly:
if useGradient:
if gradientStyle == 0:
# horizontal gradient
self.DrawHorizontalGradient(dc, self._gi._rectAll, hasFocus)
else:
# vertical gradient
self.DrawVerticalGradient(dc, self._gi._rectAll, hasFocus)
elif useVista:
# Vista selection style
self.DrawVistaRectangle(dc, self._gi._rectAll, hasFocus)
else:
if highlighted:
flags = wx.CONTROL_SELECTED
if self._owner.HasFocus() and wx.Platform == "__WXMAC__":
flags |= wx.CONTROL_FOCUSED
wx.RendererNative.Get().DrawItemSelectionRect(self._owner, dc, self._gi._rectHighlight, flags)
else:
dc.DrawRectangleRect(self._gi._rectHighlight)
else:
if borderOnly:
dc.SetBrush(wx.WHITE_BRUSH)
dc.SetPen(wx.TRANSPARENT_PEN)
dc.DrawRectangleRect(self._gi._rectAll)
if item.GetKind() in [1, 2]:
rectCheck = self._gi._rectCheck
self._owner.DrawCheckbox(dc, rectCheck.x, rectCheck.y, item.GetKind(), item.IsChecked(), item.IsEnabled())
if item.HasImage():
# centre the image inside our rectangle, this looks nicer when items
# ae aligned in a row
rectIcon = self._gi._rectIcon
self._owner.DrawImage(item.GetImage()[0], dc, rectIcon.x, rectIcon.y, True)
if item.HasText():
rectLabel = self._gi._rectLabel
dc.SetClippingRect(rectLabel)
dc.DrawText(item.GetText(), rectLabel.x, rectLabel.y)
dc.DestroyClippingRegion()
if self._owner.HasAGWFlag(ULC_HOT_TRACKING):
if line == self._owner._newHotCurrent and not drawn:
r = wx.Rect(*self._gi._rectAll)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetPen(wx.Pen(wx.NamedColour("orange")))
dc.DrawRoundedRectangleRect(r, 3)
if borderOnly and drawn:
dc.SetPen(wx.Pen(wx.Colour(0, 191, 255), 2))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
r = wx.Rect(*self._gi._rectAll)
r.x += 1
r.y += 1
r.width -= 1
r.height -= 1
dc.DrawRoundedRectangleRect(r, 4)
[docs] def HideItemWindow(self, item):
"""
If the input item has a window associated with it, hide it.
:param `item`: an instance of L{UltimateListItem}.
"""
wnd = item.GetWindow()
if wnd and wnd.IsShown():
wnd.Hide()
[docs] def DrawInReportMode(self, dc, line, rect, rectHL, highlighted, current, enabled, oldPN, oldBR):
"""
Draws the line on the specified device context when the parent L{UltimateListCtrl}
is in report mode.
:param `dc`: an instance of `wx.DC`;
:param `line`: an instance of L{UltimateListLineData};
:param `rect`: the item client rectangle;
:param `rectHL`: the item client rectangle when the item is highlighted;
:param `highlighted`: ``True`` if the item is highlighted, ``False`` otherwise;
:param `current`: ``True`` if the item is the current item;
:param `enabled`: ``True`` if the item is enabled, ``False`` otherwise;
:param `oldPN`: an instance of `wx.Pen`, to save and restore at the end of
the drawing;
:param `oldBR`: an instance of `wx.Brush`, to save and restore at the end of
the drawing.
"""
attr = self.GetAttr()
useGradient, gradientStyle = self._owner._usegradients, self._owner._gradientstyle
useVista = self._owner._vistaselection
hasFocus = self._owner._hasFocus
borderOnly = self._owner.HasAGWFlag(ULC_BORDER_SELECT)
nofullRow = self._owner.HasAGWFlag(ULC_NO_FULL_ROW_SELECT)
drawn = False
dc.SetBrush(wx.TRANSPARENT_BRUSH)
if nofullRow:
x = rect.x + HEADER_OFFSET_X
y = rect.y
height = rect.height
for col, item in enumerate(self._items):
width = self._owner.GetColumnWidth(col)
if self._owner.IsColumnShown(col):
paintRect = wx.Rect(x, y, self._owner.GetColumnWidth(col)-2*HEADER_OFFSET_X, rect.height)
break
xOld = x
x += width
else:
paintRect = wx.Rect(*rectHL)
if self.SetAttributes(dc, attr, highlighted) and enabled:
drawn = True
if not borderOnly:
if useGradient:
if gradientStyle == 0:
# horizontal gradient
self.DrawHorizontalGradient(dc, paintRect, hasFocus)
else:
# vertical gradient
self.DrawVerticalGradient(dc, paintRect, hasFocus)
elif useVista:
# Vista selection style
self.DrawVistaRectangle(dc, paintRect, hasFocus)
else:
if highlighted:
flags = wx.CONTROL_SELECTED
if hasFocus:
flags |= wx.CONTROL_FOCUSED
if current:
flags |= wx.CONTROL_CURRENT
wx.RendererNative.Get().DrawItemSelectionRect(self._owner, dc, paintRect, flags)
else:
dc.DrawRectangleRect(paintRect)
else:
if borderOnly:
dc.SetBrush(wx.WHITE_BRUSH)
dc.SetPen(wx.TRANSPARENT_PEN)
dc.DrawRectangleRect(paintRect)
x = rect.x + HEADER_OFFSET_X
y = rect.y
height = rect.height
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
boldFont.SetWeight(wx.BOLD)
for col, item in enumerate(self._items):
if not self._owner.IsColumnShown(col):
self.HideItemWindow(item)
continue
width = self._owner.GetColumnWidth(col)
xOld = x
x += width
if item.GetCustomRenderer():
customRect = wx.Rect(xOld-HEADER_OFFSET_X, rect.y, width, rect.height)
item.GetCustomRenderer().DrawSubItem(dc, customRect, line, highlighted, enabled)
continue
overflow = item.GetOverFlow() and item.HasText()
if item.GetKind() in [1, 2]:
# We got a checkbox-type item
ix, iy = self._owner.GetCheckboxImageSize()
checked = item.IsChecked()
self._owner.DrawCheckbox(dc, xOld, y + (height-iy+1)/2, item.GetKind(), checked, enabled)
xOld += ix
width -= ix
if item.HasImage():
images = item.GetImage()
for img in images:
ix, iy = self._owner.GetImageSize([img])
self._owner.DrawImage(img, dc, xOld, y + (height-iy)/2, enabled)
xOld += ix
width -= ix
## if images:
## width -= IMAGE_MARGIN_IN_REPORT_MODE - MARGIN_BETWEEN_TEXT_AND_ICON
wnd = item.GetWindow()
xSize = 0
if wnd:
xSize, ySize = item.GetWindowSize()
wndx = xOld - HEADER_OFFSET_X + width - xSize - 3
xa, ya = self._owner.CalcScrolledPosition((0, rect.y))
wndx += xa
if rect.height > ySize and not item._expandWin:
ya += (rect.height - ySize)/2
itemRect = wx.Rect(xOld-2*HEADER_OFFSET_X, rect.y, width-xSize-HEADER_OFFSET_X, rect.height)
if overflow:
itemRect = wx.Rect(xOld-2*HEADER_OFFSET_X, rect.y, rectHL.width-xSize-HEADER_OFFSET_X, rect.height)
dc.SetClippingRect(itemRect)
if item.HasBackgroundColour():
dc.SetBrush(wx.Brush(item.GetBackgroundColour()))
dc.SetPen(wx.Pen(item.GetBackgroundColour()))
dc.DrawRectangleRect(itemRect)
dc.SetBrush(oldBR)
dc.SetPen(oldPN)
if item.HasText():
coloured = item.HasColour()
c = dc.GetTextForeground()
oldTF = wx.Colour(c.Red(),c.Green(),c.Blue())
oldFT = dc.GetFont()
font = item.HasFont()
if not enabled:
dc.SetTextForeground(self._owner.GetDisabledTextColour())
else:
if coloured:
dc.SetTextForeground(item.GetColour())
elif useVista and drawn:
dc.SetTextForeground(wx.BLACK)
if item.IsHyperText():
dc.SetFont(self._owner.GetHyperTextFont())
if item.GetVisited():
dc.SetTextForeground(self._owner.GetHyperTextVisitedColour())
else:
dc.SetTextForeground(self._owner.GetHyperTextNewColour())
else:
if font:
dc.SetFont(item.GetFont())
itemRect = wx.Rect(itemRect.x+MARGIN_BETWEEN_TEXT_AND_ICON, itemRect.y, itemRect.width-8, itemRect.height)
self.DrawTextFormatted(dc, item.GetText(), line, col, itemRect, overflow)
if coloured:
dc.SetTextForeground(oldTF)
if font:
dc.SetFont(oldFT)
dc.DestroyClippingRegion()
if wnd:
if not wnd.IsShown():
wnd.Show()
if item._expandWin:
wRect = wx.Rect(*itemRect)
wRect.x += xa + 2
wRect.width = width - 8
wRect.y = ya + 2
wRect.height -= 4
if wnd.GetRect() != wRect:
wnd.SetRect(wRect)
else:
if wnd.GetPosition() != (wndx, ya):
wnd.SetPosition((wndx, ya))
if self._owner.HasAGWFlag(ULC_HOT_TRACKING):
if line == self._owner._newHotCurrent and not drawn:
r = wx.Rect(*paintRect)
r.y += 1
r.height -= 1
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetPen(wx.Pen(wx.NamedColour("orange")))
dc.DrawRoundedRectangleRect(r, 3)
dc.SetPen(oldPN)
if borderOnly and drawn:
dc.SetPen(wx.Pen(wx.Colour(0, 191, 255), 2))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
rect = wx.Rect(*paintRect)
rect.y += 1
rect.height -= 1
dc.DrawRoundedRectangleRect(rect, 3)
dc.SetPen(oldPN)
[docs] def DrawTextFormatted(self, dc, text, row, col, itemRect, overflow):
"""
Draws the item text, correctly formatted.
:param `dc`: an instance of `wx.DC`;
:param `text`: the item text;
:param `row`: the line number to which this item belongs to;
:param `col`: the column number to which this item belongs to;
:param `itemRect`: the item client rectangle;
:param `overflow`: ``True`` if the item should overflow into neighboring columns,
``False`` otherwise.
"""
# determine if the string can fit inside the current width
w, h, dummy = dc.GetMultiLineTextExtent(text)
width = itemRect.width
shortItems = self._owner._shortItems
tuples = (row, col)
# it can, draw it using the items alignment
item = self._owner.GetColumn(col)
align = item.GetAlign()
if align == ULC_FORMAT_RIGHT:
textAlign = wx.ALIGN_RIGHT
elif align == ULC_FORMAT_CENTER:
textAlign = wx.ALIGN_CENTER
else:
textAlign = wx.ALIGN_LEFT
if w <= width:
if tuples in shortItems:
shortItems.remove(tuples)
dc.DrawLabel(text, itemRect, textAlign|wx.ALIGN_CENTER_VERTICAL)
else: # otherwise, truncate and add an ellipsis if possible
if tuples not in shortItems:
shortItems.append(tuples)
# determine the base width
ellipsis = "..."
base_w, h = dc.GetTextExtent(ellipsis)
# continue until we have enough space or only one character left
newText = text.split("\n")
theText = ""
for text in newText:
lenText = len(text)
drawntext = text
w, dummy = dc.GetTextExtent(text)
while lenText > 1:
if w + base_w <= width:
break
w_c, h_c = dc.GetTextExtent(drawntext[-1])
drawntext = drawntext[0:-1]
lenText -= 1
w -= w_c
# if still not enough space, remove ellipsis characters
while len(ellipsis) > 0 and w + base_w > width:
ellipsis = ellipsis[0:-1]
base_w, h = dc.GetTextExtent(ellipsis)
theText += drawntext + ellipsis + "\n"
theText = theText.rstrip()
# now draw the text
dc.DrawLabel(theText, itemRect, textAlign|wx.ALIGN_CENTER_VERTICAL)
[docs] def DrawVerticalGradient(self, dc, rect, hasfocus):
"""
Gradient fill from colour 1 to colour 2 from top to bottom.
:param `dc`: an instance of `wx.DC`;
:param `rect`: the rectangle to be filled with the gradient shading;
:param `hasfocus`: ``True`` if the main L{UltimateListCtrl} has focus, ``False``
otherwise.
"""
oldpen = dc.GetPen()
oldbrush = dc.GetBrush()
dc.SetPen(wx.TRANSPARENT_PEN)
# calculate gradient coefficients
if hasfocus:
col2 = self._owner._secondcolour
col1 = self._owner._firstcolour
else:
col2 = self._owner._highlightUnfocusedBrush.GetColour()
col1 = self._owner._highlightUnfocusedBrush2.GetColour()
r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
flrect = float(rect.height)
rstep = float((r2 - r1)) / flrect
gstep = float((g2 - g1)) / flrect
bstep = float((b2 - b1)) / flrect
rf, gf, bf = 0, 0, 0
for y in xrange(rect.y, rect.y + rect.height):
currCol = (r1 + rf, g1 + gf, b1 + bf)
dc.SetBrush(wx.Brush(currCol, wx.SOLID))
dc.DrawRectangle(rect.x, y, rect.width, 1)
rf = rf + rstep
gf = gf + gstep
bf = bf + bstep
dc.SetPen(oldpen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangleRect(rect)
dc.SetBrush(oldbrush)
[docs] def DrawHorizontalGradient(self, dc, rect, hasfocus):
"""
Gradient fill from colour 1 to colour 2 from left to right.
:param `dc`: an instance of `wx.DC`;
:param `rect`: the rectangle to be filled with the gradient shading;
:param `hasfocus`: ``True`` if the main L{UltimateListCtrl} has focus, ``False``
otherwise.
"""
oldpen = dc.GetPen()
oldbrush = dc.GetBrush()
dc.SetPen(wx.TRANSPARENT_PEN)
# calculate gradient coefficients
if hasfocus:
col2 = self._owner._secondcolour
col1 = self._owner._firstcolour
else:
col2 = self._owner._highlightUnfocusedBrush.GetColour()
col1 = self._owner._highlightUnfocusedBrush2.GetColour()
r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
flrect = float(rect.width)
rstep = float((r2 - r1)) / flrect
gstep = float((g2 - g1)) / flrect
bstep = float((b2 - b1)) / flrect
rf, gf, bf = 0, 0, 0
for x in xrange(rect.x, rect.x + rect.width):
currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf))
dc.SetBrush(wx.Brush(currCol, wx.SOLID))
dc.DrawRectangle(x, rect.y, 1, rect.height)
rf = rf + rstep
gf = gf + gstep
bf = bf + bstep
dc.SetPen(oldpen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangleRect(rect)
dc.SetBrush(oldbrush)
[docs] def DrawVistaRectangle(self, dc, rect, hasfocus):
"""
Draws the selected item(s) with the Windows Vista style.
:param `dc`: an instance of `wx.DC`;
:param `rect`: the rectangle to be filled with the gradient shading;
:param `hasfocus`: ``True`` if the main L{UltimateListCtrl} has focus, ``False``
otherwise.
"""
if hasfocus:
outer = _rgbSelectOuter
inner = _rgbSelectInner
top = _rgbSelectTop
bottom = _rgbSelectBottom
else:
outer = _rgbNoFocusOuter
inner = _rgbNoFocusInner
top = _rgbNoFocusTop
bottom = _rgbNoFocusBottom
oldpen = dc.GetPen()
oldbrush = dc.GetBrush()
bdrRect = wx.Rect(*rect.Get())
filRect = wx.Rect(*rect.Get())
filRect.Deflate(1,1)
r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue())
r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue())
flrect = float(filRect.height)
if flrect < 1:
flrect = self._owner._lineHeight
rstep = float((r2 - r1)) / flrect
gstep = float((g2 - g1)) / flrect
bstep = float((b2 - b1)) / flrect
rf, gf, bf = 0, 0, 0
dc.SetPen(wx.TRANSPARENT_PEN)
for y in xrange(filRect.y, filRect.y + filRect.height):
currCol = (r1 + rf, g1 + gf, b1 + bf)
dc.SetBrush(wx.Brush(currCol, wx.SOLID))
dc.DrawRectangle(filRect.x, y, filRect.width, 1)
rf = rf + rstep
gf = gf + gstep
bf = bf + bstep
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetPen(wx.Pen(outer))
dc.DrawRoundedRectangleRect(bdrRect, 3)
bdrRect.Deflate(1, 1)
dc.SetPen(wx.Pen(inner))
dc.DrawRoundedRectangleRect(bdrRect, 2)
dc.SetPen(oldpen)
dc.SetBrush(oldbrush)
[docs] def Highlight(self, on):
"""
Sets the current line as highlighted or not highlighted.
:param `on`: ``True`` to set the current line as highlighted, ``False``
otherwise.
"""
if on == self._highlighted:
return False
self._highlighted = on
return True
[docs] def ReverseHighlight(self):
"""
Reverses the line highlighting, switching it off if it was on and vice-versa.
"""
self.Highlight(not self.IsHighlighted())
#-----------------------------------------------------------------------------
# UltimateListHeaderWindow (internal)
#-----------------------------------------------------------------------------
[docs]class UltimateListRenameTimer(wx.Timer):
""" Timer used for enabling in-place edit. """
[docs] def __init__(self, owner):
"""
Default class constructor.
For internal use: do not call it in your code!
:param `owner`: an instance of L{UltimateListCtrl}.
"""
wx.Timer.__init__(self)
self._owner = owner
[docs] def Notify(self):
""" The timer has expired. """
self._owner.OnRenameTimer()
#-----------------------------------------------------------------------------
# UltimateListTextCtrl (internal)
#-----------------------------------------------------------------------------
[docs]class UltimateListTextCtrl(ExpandoTextCtrl):
"""
Control used for in-place edit.
This is a subclass of `ExpandoTextCtrl` as L{UltimateListCtrl} supports multiline
text items.
:note: To add a newline character in a multiline item, press ``Shift`` + ``Enter``
as the ``Enter`` key alone is consumed by L{UltimateListCtrl} to finish
the editing and ``Ctrl`` + ``Enter`` is consumed by the platform for tab navigation.
"""
[docs] def __init__(self, owner, itemEdit):
"""
Default class constructor.
For internal use: do not call it in your code!
:param `owner`: the control parent (an instance of L{UltimateListCtrl} );
:param `itemEdit`: an instance of L{UltimateListItem}.
"""
self._startValue = owner.GetItemText(itemEdit)
self._currentValue = self._startValue
self._itemEdited = itemEdit
self._owner = owner
self._finished = False
self._aboutToFinish = False
rectLabel = owner.GetLineLabelRect(itemEdit)
rectLabel.x, rectLabel.y = self._owner.CalcScrolledPosition(rectLabel.x, rectLabel.y)
xSize, ySize = rectLabel.width + 10, rectLabel.height
expandoStyle = wx.WANTS_CHARS
if wx.Platform in ["__WXGTK__", "__WXMAC__"]:
expandoStyle |= wx.SIMPLE_BORDER
else:
expandoStyle |= wx.SUNKEN_BORDER
ExpandoTextCtrl.__init__(self, owner, -1, self._startValue, wx.Point(rectLabel.x, rectLabel.y),
wx.Size(xSize, ySize), expandoStyle)
self.Bind(wx.EVT_CHAR, self.OnChar)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
[docs] def AcceptChanges(self):
""" Accepts/refuses the changes made by the user. """
value = self.GetValue()
if value == self._startValue:
# nothing changed, always accept
# when an item remains unchanged, the owner
# needs to be notified that the user decided
# not to change the tree item label, and that
# the edit has been cancelled
self._owner.OnRenameCancelled(self._itemEdited)
return True
if not self._owner.OnRenameAccept(self._itemEdited, value):
# vetoed by the user
return False
# accepted, do rename the item
self._owner.SetItemText(self._itemEdited, value)
if value.count("\n") != self._startValue.count("\n"):
self._owner.ResetLineDimensions()
self._owner.Refresh()
return True
[docs] def Finish(self):
""" Finish editing. """
try:
if not self._finished:
self._finished = True
self._owner.SetFocusIgnoringChildren()
self._owner.ResetTextControl()
except wx.PyDeadObjectError:
return
[docs] def OnChar(self, event):
"""
Handles the ``wx.EVT_CHAR`` event for L{UltimateListTextCtrl}.
:param `event`: a `wx.KeyEvent` event to be processed.
"""
keycode = event.GetKeyCode()
shiftDown = event.ShiftDown()
if keycode in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
if shiftDown:
event.Skip()
else:
self._aboutToFinish = True
self.SetValue(self._currentValue)
# Notify the owner about the changes
self.AcceptChanges()
# Even if vetoed, close the control (consistent with MSW)
wx.CallAfter(self.Finish)
elif keycode == wx.WXK_ESCAPE:
self.StopEditing()
else:
event.Skip()
[docs] def OnKeyUp(self, event):
"""
Handles the ``wx.EVT_KEY_UP`` event for L{UltimateListTextCtrl}.
:param `event`: a `wx.KeyEvent` event to be processed.
"""
if not self._finished:
# auto-grow the textctrl:
parentSize = self._owner.GetSize()
myPos = self.GetPosition()
mySize = self.GetSize()
dc = wx.ClientDC(self)
sx, sy, dummy = dc.GetMultiLineTextExtent(self.GetValue() + "M")
if myPos.x + sx > parentSize.x:
sx = parentSize.x - myPos.x
if mySize.x > sx:
sx = mySize.x
self.SetSize((sx, -1))
self._currentValue = self.GetValue()
event.Skip()
[docs] def OnKillFocus(self, event):
"""
Handles the ``wx.EVT_KILL_FOCUS`` event for L{UltimateListTextCtrl}.
:param `event`: a `wx.FocusEvent` event to be processed.
"""
if not self._finished and not self._aboutToFinish:
# We must finish regardless of success, otherwise we'll get
# focus problems:
if not self.AcceptChanges():
self._owner.OnRenameCancelled(self._itemEdited)
# We must let the native text control handle focus, too, otherwise
# it could have problems with the cursor (e.g., in wxGTK).
event.Skip()
wx.CallAfter(self.Finish)
[docs] def StopEditing(self):
""" Suddenly stops the editing. """
self._owner.OnRenameCancelled(self._itemEdited)
self.Finish()
#-----------------------------------------------------------------------------
# UltimateListMainWindow (internal)
#-----------------------------------------------------------------------------
[docs]class UltimateListMainWindow(wx.PyScrolledWindow):
"""
This is the main widget implementation of L{UltimateListCtrl}.
"""
[docs] def __init__(self, parent, id, pos=wx.DefaultPosition,
size=wx.DefaultSize, style=0, agwStyle=0, name="listctrlmainwindow"):
"""
Default class constructor.
:param `parent`: parent window. Must not be ``None``;
:param `id`: window identifier. A value of -1 indicates a default value;
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
chosen by either the windowing system or wxPython, depending on platform;
:param `size`: the control size. A value of (-1, -1) indicates a default size,
chosen by either the windowing system or wxPython, depending on platform;
:param `style`: the underlying `wx.PyScrolledWindow` window style;
:param `agwStyle`: the AGW-specific window style; can be almost any combination of the following
bits:
=============================== =========== ====================================================================================================
Window Styles Hex Value Description
=============================== =========== ====================================================================================================
``ULC_VRULES`` 0x1 Draws light vertical rules between rows in report mode.
``ULC_HRULES`` 0x2 Draws light horizontal rules between rows in report mode.
``ULC_ICON`` 0x4 Large icon view, with optional labels.
``ULC_SMALL_ICON`` 0x8 Small icon view, with optional labels.
``ULC_LIST`` 0x10 Multicolumn list view, with optional small icons. Columns are computed automatically, i.e. you don't set columns as in ``ULC_REPORT``. In other words, the list wraps, unlike a `wx.ListBox`.
``ULC_REPORT`` 0x20 Single or multicolumn report view, with optional header.
``ULC_ALIGN_TOP`` 0x40 Icons align to the top. Win32 default, Win32 only.
``ULC_ALIGN_LEFT`` 0x80 Icons align to the left.
``ULC_AUTOARRANGE`` 0x100 Icons arrange themselves. Win32 only.
``ULC_VIRTUAL`` 0x200 The application provides items text on demand. May only be used with ``ULC_REPORT``.
``ULC_EDIT_LABELS`` 0x400 Labels are editable: the application will be notified when editing starts.
``ULC_NO_HEADER`` 0x800 No header in report mode.
``ULC_NO_SORT_HEADER`` 0x1000 No Docs.
``ULC_SINGLE_SEL`` 0x2000 Single selection (default is multiple).
``ULC_SORT_ASCENDING`` 0x4000 Sort in ascending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_TILE`` 0x10000 Each item appears as a full-sized icon with a label of one or more lines beside it (partially implemented).
``ULC_NO_HIGHLIGHT`` 0x20000 No highlight when an item is selected.
``ULC_STICKY_HIGHLIGHT`` 0x40000 Items are selected by simply hovering on them, with no need to click on them.
``ULC_STICKY_NOSELEVENT`` 0x80000 Don't send a selection event when using ``ULC_STICKY_HIGHLIGHT`` style.
``ULC_SEND_LEFTCLICK`` 0x100000 Send a left click event when an item is selected.
``ULC_HAS_VARIABLE_ROW_HEIGHT`` 0x200000 The list has variable row heights.
``ULC_AUTO_CHECK_CHILD`` 0x400000 When a column header has a checkbox associated, auto-check all the subitems in that column.
``ULC_AUTO_TOGGLE_CHILD`` 0x800000 When a column header has a checkbox associated, toggle all the subitems in that column.
``ULC_AUTO_CHECK_PARENT`` 0x1000000 Only meaningful foe checkbox-type items: when an item is checked/unchecked its column header item is checked/unchecked as well.
``ULC_SHOW_TOOLTIPS`` 0x2000000 Show tooltips for ellipsized items/subitems (text too long to be shown in the available space) containing the full item/subitem text.
``ULC_HOT_TRACKING`` 0x4000000 Enable hot tracking of items on mouse motion.
``ULC_BORDER_SELECT`` 0x8000000 Changes border colour whan an item is selected, instead of highlighting the item.
``ULC_TRACK_SELECT`` 0x10000000 Enables hot-track selection in a list control. Hot track selection means that an item is automatically selected when the cursor remains over the item for a certain period of time. The delay is retrieved on Windows using the `win32api` call `win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)`, and is defaulted to 400ms on other platforms. This style applies to all views of `UltimateListCtrl`.
``ULC_HEADER_IN_ALL_VIEWS`` 0x20000000 Show column headers in all view modes.
``ULC_NO_FULL_ROW_SELECT`` 0x40000000 When an item is selected, the only the item in the first column is highlighted.
``ULC_FOOTER`` 0x80000000 Show a footer too (only when header is present).
``ULC_USER_ROW_HEIGHT`` 0x100000000 Allows to set a custom row height (one value for all the items, only in report mode).
=============================== =========== ====================================================================================================
:param `name`: the window name.
"""
wx.PyScrolledWindow.__init__(self, parent, id, pos, size, style|wx.HSCROLL|wx.VSCROLL, name)
# the list of column objects
self._columns = []
# the array of all line objects for a non virtual list control (for the
# virtual list control we only ever use self._lines[0])
self._lines = []
# currently focused item or -1
self._current = -1
# the number of lines per page
self._linesPerPage = 0
# Automatically resized column - this column expands to fill the width of the window
self._resizeColumn = -1
self._resizeColMinWidth = None
# this flag is set when something which should result in the window
# redrawing happens (i.e. an item was added or deleted, or its appearance
# changed) and OnPaint() doesn't redraw the window while it is set which
# allows to minimize the number of repaintings when a lot of items are
# being added. The real repainting occurs only after the next OnIdle()
# call
self._dirty = False
self._parent = parent
self.Init()
self._highlightBrush = wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT), wx.SOLID)
btnshadow = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
self._highlightUnfocusedBrush = wx.Brush(btnshadow, wx.SOLID)
r, g, b = btnshadow.Red(), btnshadow.Green(), btnshadow.Blue()
backcolour = (max((r >> 1) - 20, 0),
max((g >> 1) - 20, 0),
max((b >> 1) - 20, 0))
backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2])
self._highlightUnfocusedBrush2 = wx.Brush(backcolour)
self.SetScrollbars(0, 0, 0, 0, 0, 0)
attr = wx.ListCtrl.GetClassDefaultAttributes()
self.SetOwnForegroundColour(attr.colFg)
self.SetOwnBackgroundColour(attr.colBg)
self.SetOwnFont(attr.font)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus)
self.Bind(wx.EVT_CHAR, self.OnChar)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
self.Bind(wx.EVT_TIMER, self.OnHoverTimer, self._hoverTimer)
[docs] def Init(self):
""" Initializes the L{UltimateListMainWindow} widget. """
self._dirty = True
self._countVirt = 0
self._lineFrom = None
self._lineTo = - 1
self._linesPerPage = 0
self._headerWidth = 0
self._lineHeight = 0
self._userLineHeight = None
self._small_image_list = None
self._normal_image_list = None
self._small_spacing = 30
self._normal_spacing = 40
self._hasFocus = False
self._dragCount = 0
self._isCreated = False
self._lastOnSame = False
self._renameTimer = UltimateListRenameTimer(self)
self._textctrl = None
self._current = -1
self._lineLastClicked = -1
self._lineSelectSingleOnUp = -1
self._lineBeforeLastClicked = -1
self._dragStart = wx.Point(-1, -1)
self._aColWidths = []
self._selStore = SelectionStore()
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
# Background image settings
self._backgroundImage = None
self._imageStretchStyle = _StyleTile
# Disabled items colour
self._disabledColour = wx.Colour(180, 180, 180)
# Gradient selection colours
self._firstcolour = colour= wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
self._secondcolour = wx.WHITE
self._usegradients = False
self._gradientstyle = 1 # Vertical Gradient
# Vista Selection Styles
self._vistaselection = False
self.SetImageListCheck(16, 16)
# Disabled items colour
self._disabledColour = wx.Colour(180, 180, 180)
# Hyperlinks things
normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
self._hypertextfont = wx.Font(normalFont.GetPointSize(), normalFont.GetFamily(),
normalFont.GetStyle(), wx.NORMAL, True,
normalFont.GetFaceName(), normalFont.GetEncoding())
self._hypertextnewcolour = wx.BLUE
self._hypertextvisitedcolour = wx.Colour(200, 47, 200)
self._isonhyperlink = False
self._itemWithWindow = []
self._hasWindows = False
self._shortItems = []
self._isDragging = False
self._cursor = wx.STANDARD_CURSOR
image = GetdragcursorImage()
# since this image didn't come from a .cur file, tell it where the hotspot is
image.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 1)
image.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 1)
# make the image into a cursor
self._dragCursor = wx.CursorFromImage(image)
self._dragItem = None
self._dropTarget = None
self._oldHotCurrent = None
self._newHotCurrent = None
self._waterMark = None
self._hoverTimer = wx.Timer(self, wx.ID_ANY)
self._hoverItem = -1
[docs] def GetMainWindowOfCompositeControl(self):
""" Returns the L{UltimateListMainWindow} parent. """
return self.GetParent()
[docs] def DoGetBestSize(self):
"""
Gets the size which best suits the window: for a control, it would be the
minimal size which doesn't truncate the control, for a panel - the same size
as it would have after a call to `Fit()`.
"""
return wx.Size(100, 80)
[docs] def HasAGWFlag(self, flag):
"""
Returns ``True`` if the window has the given `flag` bit set.
:param `flag`: the bit to check.
:see: L{UltimateListCtrl.SetSingleStyle} for a list of valid flags.
"""
return self._parent.HasAGWFlag(flag)
[docs] def IsColumnShown(self, column):
"""
Returns ``True`` if the input column is shown, ``False`` if it is hidden.
:param `column`: an integer specifying the column index.
"""
return self.GetColumn(column).IsShown()
# return True if this is a virtual list control
[docs] def IsVirtual(self):
""" Returns ``True`` if the window has the ``ULC_VIRTUAL`` style set. """
return self.HasAGWFlag(ULC_VIRTUAL)
# return True if the control is in report mode
[docs] def InReportView(self):
""" Returns ``True`` if the window is in report mode. """
return self.HasAGWFlag(ULC_REPORT)
[docs] def InTileView(self):
"""
Returns ``True`` if the window is in tile mode (partially implemented).
:todo: Fully implement tile view for L{UltimateListCtrl}.
"""
return self.HasAGWFlag(ULC_TILE)
# return True if we are in single selection mode, False if multi sel
[docs] def IsSingleSel(self):
""" Returns ``True`` if we are in single selection mode, ``False`` if multi selection. """
return self.HasAGWFlag(ULC_SINGLE_SEL)
[docs] def HasFocus(self):
""" Returns ``True`` if the window has focus. """
return self._hasFocus
# do we have a header window?
[docs] def HasHeader(self):
""" Returns ``True`` if the header window is shown. """
if (self.InReportView() or self.InTileView()) and not self.HasAGWFlag(ULC_NO_HEADER):
return True
if self.HasAGWFlag(ULC_HEADER_IN_ALL_VIEWS):
return True
return False
# do we have a footer window?
[docs] def HasFooter(self):
""" Returns ``True`` if the footer window is shown. """
if self.HasHeader() and self.HasAGWFlag(ULC_FOOTER):
return True
return False
# toggle the line state and refresh it
[docs] def ReverseHighlight(self, line):
"""
Toggles the line state and refreshes it.
:param `line`: an instance of L{UltimateListLineData}.
"""
self.HighlightLine(line, not self.IsHighlighted(line))
self.RefreshLine(line)
[docs] def SetUserLineHeight(self, height):
"""
Sets a custom value for the L{UltimateListMainWindow} item height.
:param `height`: the custom height for all the items, in pixels.
:note: This method can be used only with ``ULC_REPORT`` and ``ULC_USER_ROW_HEIGHT`` styles set.
"""
if self.HasAGWFlag(ULC_REPORT) and self.HasAGWFlag(ULC_USER_ROW_HEIGHT):
self._userLineHeight = height
return
raise Exception("SetUserLineHeight can only be used with styles ULC_REPORT and ULC_USER_ROW_HEIGHT set.")
[docs] def GetUserLineHeight(self):
"""
Returns the custom value for the L{UltimateListMainWindow} item height, if previously set with
L{SetUserLineHeight}.
:note: This method can be used only with ``ULC_REPORT`` and ``ULC_USER_ROW_HEIGHT`` styles set.
"""
if self.HasAGWFlag(ULC_REPORT) and self.HasAGWFlag(ULC_USER_ROW_HEIGHT):
return self._userLineHeight
raise Exception("GetUserLineHeight can only be used with styles ULC_REPORT and ULC_USER_ROW_HEIGHT set.")
# get the size of the total line rect
[docs] def GetLineSize(self, line):
"""
Returns the size of the total line client rectangle.
:param `line`: an instance of L{UltimateListLineData}.
"""
return self.GetLineRect(line).GetSize()
# bring the current item into view
[docs] def MoveToFocus(self):
""" Brings tyhe current item into view. """
self.MoveToItem(self._current)
[docs] def GetColumnCount(self):
""" Returns the total number of columns in the L{UltimateListCtrl}. """
return len(self._columns)
[docs] def GetItemText(self, item):
"""
Returns the item text.
:param `item`: an instance of L{UltimateListItem}.
"""
info = UltimateListItem()
info._mask = ULC_MASK_TEXT
info._itemId = item
info = self.GetItem(info)
return info._text
[docs] def SetItemText(self, item, value):
"""
Sets the item text.
:param `item`: an instance of L{UltimateListItem};
:param `value`: the new item text.
"""
info = UltimateListItem()
info._mask = ULC_MASK_TEXT
info._itemId = item
info._text = value
self.SetItem(info)
[docs] def IsEmpty(self):
""" Returns ``True`` if the window has no items in it. """
return self.GetItemCount() == 0
[docs] def ResetCurrent(self):
""" Resets the current item to ``None``. """
self.ChangeCurrent(-1)
[docs] def HasCurrent(self):
"""
Returns ``True`` if the current item has been set, either programmatically
or by user intervention.
"""
return self._current != -1
# override base class virtual to reset self._lineHeight when the font changes
[docs] def SetFont(self, font):
"""
Overridden base class virtual to reset the line height when the font changes.
:param `font`: a valid `wx.Font` object.
:note: Overridden from `wx.PyScrolledWindow`.
"""
if not wx.PyScrolledWindow.SetFont(self, font):
return False
self._lineHeight = 0
self.ResetLineDimensions()
return True
[docs] def ResetLineDimensions(self, force=False):
"""
Resets the line dimensions, so that client rectangles and positions are
recalculated.
:param `force`: ``True`` to reset all line dimensions.
"""
if (self.HasAGWFlag(ULC_REPORT) and self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT) and not self.IsVirtual()) or force:
for l in xrange(self.GetItemCount()):
line = self.GetLine(l)
line.ResetDimensions()
# these are for UltimateListLineData usage only
# get the backpointer to the list ctrl
[docs] def GetListCtrl(self):
""" Returns the parent widget, an instance of L{UltimateListCtrl}. """
return self.GetParent()
# get the brush to use for the item highlighting
[docs] def GetHighlightBrush(self):
""" Returns the brush to use for the item highlighting. """
return (self._hasFocus and [self._highlightBrush] or [self._highlightUnfocusedBrush])[0]
# get the line data for the given index
[docs] def GetLine(self, n):
"""
Returns the line data for the given index.
:param `n`: the line index.
"""
if self.IsVirtual():
self.CacheLineData(n)
n = 0
return self._lines[n]
# force us to recalculate the range of visible lines
[docs] def ResetVisibleLinesRange(self, reset=False):
"""
Forces us to recalculate the range of visible lines.
:param `reset`: ``True`` to reset all line dimensions, which will then be
recalculated.
"""
self._lineFrom = -1
if self.IsShownOnScreen() and reset:
self.ResetLineDimensions()
# Called on EVT_SIZE to resize the _resizeColumn to fill the width of the window
[docs] def ResizeColumns(self):
"""
If ``ULC_AUTOSIZE_FILL`` was passed to L{UltimateListCtrl.SetColumnWidth} then
that column's width will be expanded to fill the window on a resize event.
Called by L{UltimateListCtrl.OnSize} when the window is resized.
"""
if not self: # Avoid PyDeadObjectErrors on Mac
return
if self._resizeColumn == -1:
return
numCols = self.GetColumnCount()
if numCols == 0: return # Nothing to resize.
resizeCol = self._resizeColumn
if self._resizeColMinWidth == None:
self._resizeColMinWidth = self.GetColumnWidth(resizeCol)
# We're showing the vertical scrollbar -> allow for scrollbar width
# NOTE: on GTK, the scrollbar is included in the client size, but on
# Windows it is not included
listWidth = self.GetClientSize().width
if wx.Platform != '__WXMSW__':
if self.GetItemCount() > self.GetCountPerPage():
scrollWidth = wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X)
listWidth = listWidth - scrollWidth
totColWidth = 0 # Width of all columns except last one.
for col in range(numCols):
if col != (resizeCol) and self.IsColumnShown(col):
totColWidth = totColWidth + self.GetColumnWidth(col)
resizeColWidth = self.GetColumnWidth(resizeCol)
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(resizeCol, self._resizeColMinWidth)
return
# Resize the last column to take up the remaining available space.
self.SetColumnWidth(resizeCol, listWidth - totColWidth)
# get the colour to be used for drawing the rules
[docs] def GetRuleColour(self):
""" Returns the colour to be used for drawing the horizontal and vertical rules. """
return wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)
[docs] def SetReportView(self, inReportView):
"""
Sets whether L{UltimateListCtrl} is in report view or not.
:param `inReportView`: ``True`` to set L{UltimateListCtrl} in report view, ``False``
otherwise.
"""
for line in self._lines:
line.SetReportView(inReportView)
[docs] def CacheLineData(self, line):
"""
Saves the current line attributes.
:param `line`: an instance of L{UltimateListLineData}.
:note: This method is used only if the L{UltimateListCtrl} has the ``ULC_VIRTUAL``
style set.
"""
listctrl = self.GetListCtrl()
ld = self.GetDummyLine()
countCol = self.GetColumnCount()
for col in xrange(countCol):
ld.SetText(col, listctrl.OnGetItemText(line, col))
ld.SetToolTip(col, listctrl.OnGetItemToolTip(line, col))
ld.SetColour(col, listctrl.OnGetItemTextColour(line, col))
ld.SetImage(col, listctrl.OnGetItemColumnImage(line, col))
kind = listctrl.OnGetItemColumnKind(line, col)
ld.SetKind(col, kind)
if kind > 0:
ld.Check(col, listctrl.OnGetItemColumnCheck(line, col))
ld.SetAttr(listctrl.OnGetItemAttr(line))
[docs] def GetDummyLine(self):
"""
Returns a dummy line.
:note: This method is used only if the L{UltimateListCtrl} has the ``ULC_VIRTUAL``
style set.
"""
if self.IsEmpty():
raise Exception("invalid line index")
if not self.IsVirtual():
raise Exception("GetDummyLine() shouldn't be called")
# we need to recreate the dummy line if the number of columns in the
# control changed as it would have the incorrect number of fields
# otherwise
if len(self._lines) > 0 and len(self._lines[0]._items) != self.GetColumnCount():
self._lines = []
if not self._lines:
line = UltimateListLineData(self)
self._lines.append(line)
return self._lines[0]
# ----------------------------------------------------------------------------
# line geometry (report mode only)
# ----------------------------------------------------------------------------
[docs] def GetLineHeight(self, item=None):
"""
Returns the line height for a specific item.
:param `item`: if not ``None``, an instance of L{UltimateListItem}.
"""
# we cache the line height as calling GetTextExtent() is slow
if self.HasAGWFlag(ULC_REPORT) and self.HasAGWFlag(ULC_USER_ROW_HEIGHT):
if self._userLineHeight is not None:
return self._userLineHeight
if item is None or not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
if not self._lineHeight:
dc = wx.ClientDC(self)
dc.SetFont(self.GetFont())
dummy, y = dc.GetTextExtent("H")
if self._small_image_list and self._small_image_list.GetImageCount():
iw, ih = self._small_image_list.GetSize(0)
y = max(y, ih)
y += EXTRA_HEIGHT
self._lineHeight = y + LINE_SPACING
return self._lineHeight
else:
line = self.GetLine(item)
LH = line.GetHeight()
if LH != -1:
return LH
dc = wx.ClientDC(self)
allTextY = 0
for col, items in enumerate(line._items):
if items.GetCustomRenderer():
allTextY = max(allTextY, items.GetCustomRenderer().GetLineHeight())
continue
if items.HasFont():
dc.SetFont(items.GetFont())
else:
dc.SetFont(self.GetFont())
text_x, text_y, dummy = dc.GetMultiLineTextExtent(items.GetText())
allTextY = max(text_y, allTextY)
if items.GetWindow():
xSize, ySize = items.GetWindowSize()
allTextY = max(allTextY, ySize)
if self._small_image_list and self._small_image_list.GetImageCount():
for img in items._image:
iw, ih = self._small_image_list.GetSize(img)
allTextY = max(allTextY, ih)
allTextY += EXTRA_HEIGHT
line.SetHeight(allTextY)
return allTextY
[docs] def GetLineY(self, line):
"""
Returns the line `y` position.
:param `line`: an instance of L{UltimateListLineData}.
"""
if self.IsVirtual():
return LINE_SPACING + line*self.GetLineHeight()
lineItem = self.GetLine(line)
lineY = lineItem.GetY()
if lineY != -1:
return lineY
lineY = 0
for l in xrange(line):
lineY += self.GetLineHeight(l)
lineItem.SetY(LINE_SPACING + lineY)
return LINE_SPACING + lineY
[docs] def GetLineRect(self, line):
"""
Returns the line client rectangle.
:param `line`: an instance of L{UltimateListLineData}.
"""
if not self.InReportView():
return self.GetLine(line)._gi._rectAll
rect = wx.Rect(HEADER_OFFSET_X, self.GetLineY(line), self.GetHeaderWidth(), self.GetLineHeight(line))
return rect
[docs] def GetLineLabelRect(self, line, col=0):
"""
Returns the line client rectangle for the item text only.
Note this is the full column width unless an image or
checkbox exists. It is not the width of the text itself
:param `line`: an instance of L{UltimateListLineData}.
"""
if not self.InReportView():
return self.GetLine(line)._gi._rectLabel
image_x = 0
image_width = 0
for c in range(col):
image_x += self.GetColumnWidth(c)
item = self.GetLine(line)
if item.HasImage(col):
ix, iy = self.GetImageSize(item.GetImage(col))
image_x += ix
image_width = ix
if item.GetKind(col) in [1, 2]:
image_x += self.GetCheckboxImageSize()[0]
image_width += self.GetCheckboxImageSize()[0]
rect = wx.Rect(image_x + HEADER_OFFSET_X, self.GetLineY(line), self.GetColumnWidth(col) - image_width, self.GetLineHeight(line))
return rect
[docs] def GetLineIconRect(self, line):
"""
Returns the line client rectangle for the item image only.
:param `line`: an instance of L{UltimateListLineData}.
"""
if not self.InReportView():
return self.GetLine(line)._gi._rectIcon
ld = self.GetLine(line)
image_x = HEADER_OFFSET_X
if ld.GetKind() in [1, 2]:
image_x += self.GetCheckboxImageSize()[0]
rect = wx.Rect(image_x, self.GetLineY(line), *self.GetImageSize(ld.GetImage()))
return rect
[docs] def GetLineCheckboxRect(self, line):
"""
Returns the line client rectangle for the item checkbox image only.
:param `line`: an instance of L{UltimateListLineData}.
"""
if not self.InReportView():
return self.GetLine(line)._gi._rectCheck
ld = self.GetLine(line)
LH = self.GetLineHeight(line)
wcheck, hcheck = self.GetCheckboxImageSize()
rect = wx.Rect(HEADER_OFFSET_X, self.GetLineY(line) + LH/2 - hcheck/2, wcheck, hcheck)
return rect
[docs] def GetLineHighlightRect(self, line):
"""
Returns the line client rectangle when the line is highlighted.
:param `line`: an instance of L{UltimateListLineData}.
"""
return (self.InReportView() and [self.GetLineRect(line)] or [self.GetLine(line)._gi._rectHighlight])[0]
[docs] def HitTestLine(self, line, x, y):
"""
HitTest method for a L{UltimateListCtrl} line.
:param `line`: an instance of L{UltimateListLineData};
:param `x`: the mouse `x` position;
:param `y`: the mouse `y` position.
:return: a tuple of values, representing the item hit and a hit flag. The
hit flag can be one of the following bits:
=============================== ========= ================================
HitTest Flag Hex Value Description
=============================== ========= ================================
``ULC_HITTEST_ABOVE`` 0x1 Above the client area
``ULC_HITTEST_BELOW`` 0x2 Below the client area
``ULC_HITTEST_NOWHERE`` 0x4 In the client area but below the last item
``ULC_HITTEST_ONITEM`` 0x2a0 Anywhere on the item (text, icon, checkbox image)
``ULC_HITTEST_ONITEMICON`` 0x20 On the bitmap associated with an item
``ULC_HITTEST_ONITEMLABEL`` 0x80 On the label (string) associated with an item
``ULC_HITTEST_ONITEMRIGHT`` 0x100 In the area to the right of an item
``ULC_HITTEST_ONITEMSTATEICON`` 0x200 On the state icon for a list view item that is in a user-defined state
``ULC_HITTEST_TOLEFT`` 0x400 To the left of the client area
``ULC_HITTEST_TORIGHT`` 0x800 To the right of the client area
``ULC_HITTEST_ONITEMCHECK`` 0x1000 On the item checkbox (if any)
=============================== ========= ================================
"""
ld = self.GetLine(line)
if self.InReportView():# and not self.IsVirtual():
lineY = self.GetLineY(line)
xstart = HEADER_OFFSET_X
for col, item in enumerate(ld._items):
if not self.IsColumnShown(col):
continue
width = self.GetColumnWidth(col)
xOld = xstart
xstart += width
ix = 0
#if (line, col) in self._shortItems:
#rect = wx.Rect(xOld, lineY, width, self.GetLineHeight(line))
rect = self.GetLineLabelRect(line,col)
if rect.Contains((x, y)):
newItem = self.GetParent().GetItem(line, col)
return newItem, ULC_HITTEST_ONITEMLABEL
if item.GetKind() in [1, 2]:
# We got a checkbox-type item
ix, iy = self.GetCheckboxImageSize()
LH = self.GetLineHeight(line)
rect = wx.Rect(xOld, lineY + LH/2 - iy/2, ix, iy)
if rect.Contains((x, y)):
newItem = self.GetParent().GetItem(line, col)
return newItem, ULC_HITTEST_ONITEMCHECK
if item.IsHyperText():
start, end = self.GetItemTextSize(item)
rect = wx.Rect(xOld+start, lineY, end, self.GetLineHeight(line))
if rect.Contains((x, y)):
newItem = self.GetParent().GetItem(line, col)
return newItem, ULC_HITTEST_ONITEMLABEL
xOld += ix
if ld.HasImage() and self.GetLineIconRect(line).Contains((x, y)):
return self.GetParent().GetItem(line), ULC_HITTEST_ONITEMICON
# VS: Testing for "ld.HasText() || InReportView()" instead of
# "ld.HasText()" is needed to make empty lines in report view
# possible
if ld.HasText() or self.InReportView():
if self.InReportView():
rect = self.GetLineRect(line)
else:
checkRect = self.GetLineCheckboxRect(line)
if checkRect.Contains((x, y)):
return self.GetParent().GetItem(line), ULC_HITTEST_ONITEMCHECK
rect = self.GetLineLabelRect(line)
if rect.Contains((x, y)):
return self.GetParent().GetItem(line), ULC_HITTEST_ONITEMLABEL
rect = self.GetLineRect(line)
if rect.Contains((x, y)):
return self.GetParent().GetItem(line), ULC_HITTEST_ONITEM
return None, 0
# ----------------------------------------------------------------------------
# highlight (selection) handling
# ----------------------------------------------------------------------------
[docs] def IsHighlighted(self, line):
"""
Returns ``True`` if the input line is highlighted.
:param `line`: an instance of L{UltimateListLineData}.
"""
if self.IsVirtual():
return self._selStore.IsSelected(line)
else: # !virtual
ld = self.GetLine(line)
return ld.IsHighlighted()
[docs] def HighlightLines(self, lineFrom, lineTo, highlight=True):
"""
Highlights a range of lines in L{UltimateListCtrl}.
:param `lineFrom`: an integer representing the first line to highlight;
:param `lineTo`: an integer representing the last line to highlight;
:param `highlight`: ``True`` to highlight the lines, ``False`` otherwise.
"""
if self.IsVirtual():
linesChanged = self._selStore.SelectRange(lineFrom, lineTo, highlight)
if not linesChanged:
# many items changed state, refresh everything
self.RefreshLines(lineFrom, lineTo)
else: # only a few items changed state, refresh only them
for n in xrange(len(linesChanged)):
self.RefreshLine(linesChanged[n])
else: # iterate over all items in non report view
for line in xrange(lineFrom, lineTo+1):
if self.HighlightLine(line, highlight):
self.RefreshLine(line)
[docs] def HighlightLine(self, line, highlight=True):
"""
Highlights a line in L{UltimateListCtrl}.
:param `line`: an instance of L{UltimateListLineData};
:param `highlight`: ``True`` to highlight the line, ``False`` otherwise.
"""
changed = False
if self.IsVirtual():
changed = self._selStore.SelectItem(line, highlight)
else: # !virtual
ld = self.GetLine(line)
changed = ld.Highlight(highlight)
dontNotify = self.HasAGWFlag(ULC_STICKY_HIGHLIGHT) and self.HasAGWFlag(ULC_STICKY_NOSELEVENT)
if changed and not dontNotify:
self.SendNotify(line, (highlight and [wxEVT_COMMAND_LIST_ITEM_SELECTED] or [wxEVT_COMMAND_LIST_ITEM_DESELECTED])[0])
return changed
[docs] def RefreshLine(self, line):
"""
Redraws the input line.
:param `line`: an instance of L{UltimateListLineData}.
"""
if self.InReportView():
visibleFrom, visibleTo = self.GetVisibleLinesRange()
if line < visibleFrom or line > visibleTo:
return
rect = self.GetLineRect(line)
rect.x, rect.y = self.CalcScrolledPosition(rect.x, rect.y)
self.RefreshRect(rect)
[docs] def RefreshLines(self, lineFrom, lineTo):
"""
Redraws a range of lines in L{UltimateListCtrl}.
:param `lineFrom`: an integer representing the first line to refresh;
:param `lineTo`: an integer representing the last line to refresh.
"""
if self.InReportView():
visibleFrom, visibleTo = self.GetVisibleLinesRange()
if lineFrom < visibleFrom:
lineFrom = visibleFrom
if lineTo > visibleTo:
lineTo = visibleTo
rect = wx.Rect()
rect.x = 0
rect.y = self.GetLineY(lineFrom)
rect.width = self.GetClientSize().x
rect.height = self.GetLineY(lineTo) - rect.y + self.GetLineHeight(lineTo)
rect.x, rect.y = self.CalcScrolledPosition(rect.x, rect.y)
self.RefreshRect(rect)
else: # !report
# TODO: this should be optimized...
for line in xrange(lineFrom, lineTo+1):
self.RefreshLine(line)
[docs] def RefreshAfter(self, lineFrom):
"""
Redraws all the lines after the input one.
:param `lineFrom`: an integer representing the first line to refresh.
"""
if self.InReportView():
visibleFrom, visibleTo = self.GetVisibleLinesRange()
if lineFrom < visibleFrom:
lineFrom = visibleFrom
elif lineFrom > visibleTo:
return
rect = wx.Rect()
rect.x = 0
rect.y = self.GetLineY(lineFrom)
rect.x, rect.y = self.CalcScrolledPosition(rect.x, rect.y)
size = self.GetClientSize()
rect.width = size.x
# refresh till the bottom of the window
rect.height = size.y - rect.y
self.RefreshRect(rect)
else: # !report
# TODO: how to do it more efficiently?
self._dirty = True
[docs] def RefreshSelected(self):
""" Redraws the selected lines. """
if self.IsEmpty():
return
if self.InReportView():
fromm, to = self.GetVisibleLinesRange()
else: # !virtual
fromm = 0
to = self.GetItemCount() - 1
if self.HasCurrent() and self._current >= fromm and self._current <= to:
self.RefreshLine(self._current)
for line in xrange(fromm, to+1):
# NB: the test works as expected even if self._current == -1
if line != self._current and self.IsHighlighted(line):
self.RefreshLine(line)
[docs] def HideWindows(self):
""" Hides the windows associated to the items. Used internally. """
for child in self._itemWithWindow:
wnd = child.GetWindow()
if wnd:
wnd.Hide()
[docs] def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.PaintEvent` event to be processed.
"""
# Note: a wxPaintDC must be constructed even if no drawing is
# done (a Windows requirement).
dc = wx.BufferedPaintDC(self)
dc.SetBackgroundMode(wx.TRANSPARENT)
self.PrepareDC(dc)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.SetPen(wx.TRANSPARENT_PEN)
dc.Clear()
self.TileBackground(dc)
self.PaintWaterMark(dc)
if self.IsEmpty():
# nothing to draw or not the moment to draw it
return
if self._dirty:
# delay the repainting until we calculate all the items positions
self.RecalculatePositions(False)
useVista, useGradient = self._vistaselection, self._usegradients
dev_x, dev_y = self.CalcScrolledPosition(0, 0)
dc.SetFont(self.GetFont())
if self.InReportView():
visibleFrom, visibleTo = self.GetVisibleLinesRange()
# mrcs: draw additional items
if visibleFrom > 0:
visibleFrom -= 1
if visibleTo < self.GetItemCount() - 1:
visibleTo += 1
xOrig = dc.LogicalToDeviceX(0)
yOrig = dc.LogicalToDeviceY(0)
# tell the caller cache to cache the data
if self.IsVirtual():
evCache = UltimateListEvent(wxEVT_COMMAND_LIST_CACHE_HINT, self.GetParent().GetId())
evCache.SetEventObject(self.GetParent())
evCache.m_oldItemIndex = visibleFrom
evCache.m_itemIndex = visibleTo
self.GetParent().GetEventHandler().ProcessEvent(evCache)
no_highlight = self.HasAGWFlag(ULC_NO_HIGHLIGHT)
for line in xrange(visibleFrom, visibleTo+1):
rectLine = self.GetLineRect(line)
if not self.IsExposed(rectLine.x + xOrig, rectLine.y + yOrig, rectLine.width, rectLine.height):
# don't redraw unaffected lines to avoid flicker
continue
theLine = self.GetLine(line)
enabled = theLine.GetItem(0, CreateListItem(line, 0)).IsEnabled()
oldPN, oldBR = dc.GetPen(), dc.GetBrush()
theLine.DrawInReportMode(dc, line, rectLine,
self.GetLineHighlightRect(line),
self.IsHighlighted(line) and not no_highlight,
line==self._current, enabled, oldPN, oldBR)
if self.HasAGWFlag(ULC_HRULES):
pen = wx.Pen(self.GetRuleColour(), 1, wx.SOLID)
clientSize = self.GetClientSize()
# Don't draw the first one
start = (visibleFrom > 0 and [visibleFrom] or [1])[0]
dc.SetPen(pen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
for i in xrange(start, visibleTo+1):
lineY = self.GetLineY(i)
dc.DrawLine(0 - dev_x, lineY, clientSize.x - dev_x, lineY)
# Draw last horizontal rule
if visibleTo == self.GetItemCount() - 1:
lineY = self.GetLineY(visibleTo) + self.GetLineHeight(visibleTo)
dc.SetPen(pen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawLine(0 - dev_x, lineY, clientSize.x - dev_x , lineY)
# Draw vertical rules if required
if self.HasAGWFlag(ULC_VRULES) and not self.IsEmpty():
pen = wx.Pen(self.GetRuleColour(), 1, wx.SOLID)
firstItemRect = self.GetItemRect(visibleFrom)
lastItemRect = self.GetItemRect(visibleTo)
x = firstItemRect.GetX()
dc.SetPen(pen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
for col in xrange(self.GetColumnCount()):
if not self.IsColumnShown(col):
continue
colWidth = self.GetColumnWidth(col)
x += colWidth
x_pos = x - dev_x
if col < self.GetColumnCount()-1:
x_pos -= 2
dc.DrawLine(x_pos, firstItemRect.GetY() - 1 - dev_y, x_pos, lastItemRect.GetBottom() + 1 - dev_y)
else: # !report
for i in xrange(self.GetItemCount()):
self.GetLine(i).Draw(i, dc)
if wx.Platform not in ["__WXMAC__", "__WXGTK__"]:
# Don't draw rect outline under Mac at all.
# Draw it elsewhere on GTK
if self.HasCurrent():
if self._hasFocus and not self.HasAGWFlag(ULC_NO_HIGHLIGHT) and not useVista and not useGradient \
and not self.HasAGWFlag(ULC_BORDER_SELECT) and not self.HasAGWFlag(ULC_NO_FULL_ROW_SELECT):
dc.SetPen(wx.BLACK_PEN)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangleRect(self.GetLineHighlightRect(self._current))
[docs] def OnEraseBackground(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.EraseEvent` event to be processed.
:note: This method is intentionally empty to reduce flicker.
"""
pass
[docs] def TileBackground(self, dc):
"""
Tiles the background image to fill all the available area.
:param `dc`: an instance of `wx.DC`.
:todo: Support background images also in stretch and centered modes.
"""
if not self._backgroundImage:
return
if self._imageStretchStyle != _StyleTile:
# Can we actually do something here (or in OnPaint()) To Handle
# background images that are stretchable or always centered?
# I tried but I get enormous flickering...
return
sz = self.GetClientSize()
w = self._backgroundImage.GetWidth()
h = self._backgroundImage.GetHeight()
x = 0
while x < sz.width:
y = 0
while y < sz.height:
dc.DrawBitmap(self._backgroundImage, x, y, True)
y = y + h
x = x + w
[docs] def PaintWaterMark(self, dc):
"""
Draws a watermark at the bottom right of L{UltimateListCtrl}.
:param `dc`: an instance of `wx.DC`.
:todo: Better support for this is needed.
"""
if not self._waterMark:
return
width, height = self.CalcUnscrolledPosition(*self.GetClientSize())
bitmapW = self._waterMark.GetWidth()
bitmapH = self._waterMark.GetHeight()
x = width - bitmapW - 5
y = height - bitmapH - 5
dc.DrawBitmap(self._waterMark, x, y, True)
[docs] def HighlightAll(self, on=True):
"""
Highlights/unhighlights all the lines in L{UltimateListCtrl}.
:param `on`: ``True`` to highlight all the lines, ``False`` to unhighlight them.
"""
if self.IsSingleSel():
if on:
raise Exception("can't do this in a single sel control")
# we just have one item to turn off
if self.HasCurrent() and self.IsHighlighted(self._current):
self.HighlightLine(self._current, False)
self.RefreshLine(self._current)
else: # multi sel
if not self.IsEmpty():
self.HighlightLines(0, self.GetItemCount() - 1, on)
[docs] def OnChildFocus(self, event):
"""
Handles the ``wx.EVT_CHILD_FOCUS`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.ChildFocusEvent` event to be processed.
:note: This method is intentionally empty to prevent the default handler in
`wx.PyScrolledWindow` from needlessly scrolling the window when the edit
control is dismissed.
"""
# Do nothing here. This prevents the default handler in wx.PyScrolledWindow
# from needlessly scrolling the window when the edit control is
# dismissed. See ticket #9563.
pass
[docs] def SendNotify(self, line, command, point=wx.DefaultPosition):
"""
Actually sends a L{UltimateListEvent}.
:param `line`: an instance of L{UltimateListLineData};
:param `command`: the event type to send;
:param `point`: an instance of `wx.Point`.
"""
bRet = True
le = UltimateListEvent(command, self.GetParent().GetId())
le.SetEventObject(self.GetParent())
le.m_itemIndex = line
# set only for events which have position
if point != wx.DefaultPosition:
le.m_pointDrag = point
# don't try to get the line info for virtual list controls: the main
# program has it anyhow and if we did it would result in accessing all
# the lines, even those which are not visible now and this is precisely
# what we're trying to avoid
if not self.IsVirtual():
if line != -1:
self.GetLine(line).GetItem(0, le.m_item)
#else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event
#else: there may be no more such item
self.GetParent().GetEventHandler().ProcessEvent(le)
bRet = le.IsAllowed()
return bRet
[docs] def ChangeCurrent(self, current):
"""
Changes the current line to the specified one.
:param `current`: an integer specifying the index of the current line.
"""
self._current = current
# as the current item changed, we shouldn't start editing it when the
# "slow click" timer expires as the click happened on another item
if self._renameTimer.IsRunning():
self._renameTimer.Stop()
self.SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED)
[docs] def EditLabel(self, item):
"""
Starts editing an item label.
:param `item`: an instance of L{UltimateListItem}.
"""
if item < 0 or item >= self.GetItemCount():
raise Exception("wrong index in UltimateListCtrl.EditLabel()")
le = UltimateListEvent(wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, self.GetParent().GetId())
le.SetEventObject(self.GetParent())
le.m_itemIndex = item
data = self.GetLine(item)
le.m_item = data.GetItem(0, le.m_item)
self._textctrl = UltimateListTextCtrl(self, item)
if self.GetParent().GetEventHandler().ProcessEvent(le) and not le.IsAllowed():
# vetoed by user code
return
# We have to call this here because the label in question might just have
# been added and no screen update taken place.
if self._dirty:
wx.SafeYield()
# Pending events dispatched by wx.SafeYield might have changed the item
# count
if item >= self.GetItemCount():
return None
# modified
self._textctrl.SetFocus()
return self._textctrl
[docs] def OnRenameTimer(self):
""" The timer for renaming has expired. Start editing. """
if not self.HasCurrent():
raise Exception("unexpected rename timer")
self.EditLabel(self._current)
[docs] def OnRenameAccept(self, itemEdit, value):
"""
Called by L{UltimateListTextCtrl}, to accept the changes and to send the
``EVT_LIST_END_LABEL_EDIT`` event.
:param `itemEdit`: an instance of L{UltimateListItem};
:param `value`: the new value of the item label.
"""
le = UltimateListEvent(wxEVT_COMMAND_LIST_END_LABEL_EDIT, self.GetParent().GetId())
le.SetEventObject(self.GetParent())
le.m_itemIndex = itemEdit
data = self.GetLine(itemEdit)
le.m_item = data.GetItem(0, le.m_item)
le.m_item._text = value
return not self.GetParent().GetEventHandler().ProcessEvent(le) or le.IsAllowed()
[docs] def OnRenameCancelled(self, itemEdit):
"""
Called by L{UltimateListTextCtrl}, to cancel the changes and to send the
``EVT_LIST_END_LABEL_EDIT`` event.
:param `item`: an instance of L{UltimateListItem}.
"""
# let owner know that the edit was cancelled
le = UltimateListEvent(wxEVT_COMMAND_LIST_END_LABEL_EDIT, self.GetParent().GetId())
le.SetEditCanceled(True)
le.SetEventObject(self.GetParent())
le.m_itemIndex = itemEdit
data = self.GetLine(itemEdit)
le.m_item = data.GetItem(0, le.m_item)
self.GetEventHandler().ProcessEvent(le)
[docs] def OnMouse(self, event):
"""
Handles the ``wx.EVT_MOUSE_EVENTS`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.MouseEvent` event to be processed.
"""
if wx.Platform == "__WXMAC__":
# On wxMac we can't depend on the EVT_KILL_FOCUS event to properly
# shutdown the edit control when the mouse is clicked elsewhere on the
# listctrl because the order of events is different (or something like
# that,) so explicitly end the edit if it is active.
if event.LeftDown() and self._textctrl:
self._textctrl.AcceptChanges()
self._textctrl.Finish()
if event.LeftDown():
self.SetFocusIgnoringChildren()
event.SetEventObject(self.GetParent())
if self.GetParent().GetEventHandler().ProcessEvent(event):
return
if event.GetEventType() == wx.wxEVT_MOUSEWHEEL:
# let the base handle mouse wheel events.
self.Refresh()
event.Skip()
return
if self.IsEmpty():
if event.RightDown():
self.SendNotify(-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition())
evtCtx = wx.ContextMenuEvent(wx.wxEVT_CONTEXT_MENU, self.GetParent().GetId(),
self.ClientToScreen(event.GetPosition()))
evtCtx.SetEventObject(self.GetParent())
self.GetParent().GetEventHandler().ProcessEvent(evtCtx)
return
if self._dirty:
return
if not (event.Dragging() or event.ButtonDown() or event.LeftUp() or \
event.ButtonDClick() or event.Moving() or event.RightUp()):
return
x = event.GetX()
y = event.GetY()
x, y = self.CalcUnscrolledPosition(x, y)
# where did we hit it (if we did)?
hitResult = 0
newItem = None
count = self.GetItemCount()
if self.InReportView():
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
current = y/self.GetLineHeight()
if current < count:
newItem, hitResult = self.HitTestLine(current, x, y)
else:
return
else:
for current in xrange(count):
newItem, hitResult = self.HitTestLine(current, x, y)
if hitResult:
break
else:
# TODO: optimize it too! this is less simple than for report view but
# enumerating all items is still not a way to do it!!
for current in xrange(count):
newItem, hitResult = self.HitTestLine(current, x, y)
if hitResult:
break
theItem = None
if not self.IsVirtual():
theItem = CreateListItem(current, 0)
theItem = self.GetItem(theItem)
if event.GetEventType() == wx.wxEVT_MOTION and not event.Dragging():
if current >= 0 and current < count and self.HasAGWFlag(ULC_TRACK_SELECT) and not self._hoverTimer.IsRunning():
self._hoverItem = current
self._hoverTimer.Start(HOVER_TIME, wx.TIMER_ONE_SHOT)
if newItem and newItem.IsHyperText() and (hitResult & ULC_HITTEST_ONITEMLABEL) and theItem and theItem.IsEnabled():
self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
self._isonhyperlink = True
else:
if self._isonhyperlink:
self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
self._isonhyperlink = False
if self.HasAGWFlag(ULC_STICKY_HIGHLIGHT) and hitResult:
if not self.IsHighlighted(current):
self.HighlightAll(False)
self.ChangeCurrent(current)
self.ReverseHighlight(self._current)
if self.HasAGWFlag(ULC_SHOW_TOOLTIPS):
if newItem and hitResult & ULC_HITTEST_ONITEMLABEL:
r,c = (newItem._itemId, newItem._col)
line = self.GetLine(r)
tt = line.GetToolTip(c)
if tt and not tt == "":
if self.GetToolTip() and self.GetToolTip().GetTip() != tt:
self.SetToolTipString(tt)
elif (r,c) in self._shortItems: # if the text didn't fit in the column
text = newItem.GetText()
if self.GetToolTip() and self.GetToolTip().GetTip() != text:
self.SetToolTipString(text)
else:
self.SetToolTipString("")
else:
self.SetToolTipString("")
if self.HasAGWFlag(ULC_HOT_TRACKING):
if hitResult:
if self._oldHotCurrent != current:
if self._oldHotCurrent is not None:
self.RefreshLine(self._oldHotCurrent)
self._newHotCurrent = current
self.RefreshLine(self._newHotCurrent)
self._oldHotCurrent = current
event.Skip()
return
if event.Dragging():
if not self._isDragging:
if self._lineLastClicked == -1 or not hitResult or not theItem or not theItem.IsEnabled():
return
if self._dragCount == 0:
# we have to report the raw, physical coords as we want to be
# able to call HitTest(event.m_pointDrag) from the user code to
# get the item being dragged
self._dragStart = event.GetPosition()
self._dragCount += 1
if self._dragCount != 3:
return
command = (event.RightIsDown() and [wxEVT_COMMAND_LIST_BEGIN_RDRAG] or [wxEVT_COMMAND_LIST_BEGIN_DRAG])[0]
le = UltimateListEvent(command, self.GetParent().GetId())
le.SetEventObject(self.GetParent())
le.m_itemIndex = self._lineLastClicked
le.m_pointDrag = self._dragStart
self.GetParent().GetEventHandler().ProcessEvent(le)
# we're going to drag this item
self._isDragging = True
self._dragItem = current
# remember the old cursor because we will change it while
# dragging
self._oldCursor = self._cursor
self.SetCursor(self._dragCursor)
else:
if current != self._dropTarget:
self.SetCursor(self._dragCursor)
# unhighlight the previous drop target
if self._dropTarget is not None:
self.RefreshLine(self._dropTarget)
move = current
if self._dropTarget:
move = (current > self._dropTarget and [current+1] or [current-1])[0]
self._dropTarget = current
self.MoveToItem(move)
else:
if self._dragItem == current:
self.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
if self.HasAGWFlag(ULC_REPORT) and self._dragItem != current:
self.DrawDnDArrow()
return
else:
self._dragCount = 0
if theItem and not theItem.IsEnabled():
self.DragFinish(event)
event.Skip()
return
if not hitResult:
# outside of any item
if event.RightDown():
self.SendNotify(-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition())
evtCtx = wx.ContextMenuEvent(wx.wxEVT_CONTEXT_MENU, self.GetParent().GetId(),
self.ClientToScreen(event.GetPosition()))
evtCtx.SetEventObject(self.GetParent())
self.GetParent().GetEventHandler().ProcessEvent(evtCtx)
else:
self.HighlightAll(False)
self.DragFinish(event)
return
forceClick = False
if event.ButtonDClick():
if self._renameTimer.IsRunning():
self._renameTimer.Stop()
self._lastOnSame = False
if current == self._lineLastClicked:
self.SendNotify(current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
if newItem and newItem.GetKind() in [1, 2] and (hitResult & ULC_HITTEST_ONITEMCHECK):
self.CheckItem(newItem, not self.IsItemChecked(newItem))
return
else:
# The first click was on another item, so don't interpret this as
# a double click, but as a simple click instead
forceClick = True
if event.LeftUp():
if self.DragFinish(event):
return
if self._lineSelectSingleOnUp != - 1:
# select single line
self.HighlightAll(False)
self.ReverseHighlight(self._lineSelectSingleOnUp)
if self._lastOnSame:
if (current == self._current) and (hitResult == ULC_HITTEST_ONITEMLABEL) and self.HasAGWFlag(ULC_EDIT_LABELS):
if not self.InReportView() or self.GetLineLabelRect(current).Contains((x, y)):
# This wx.SYS_DCLICK_MSEC is not yet wrapped in wxPython...
# dclick = wx.SystemSettings.GetMetric(wx.SYS_DCLICK_MSEC)
# m_renameTimer->Start(dclick > 0 ? dclick : 250, True)
self._renameTimer.Start(250, True)
self._lastOnSame = False
self._lineSelectSingleOnUp = -1
elif event.RightUp():
if self.DragFinish(event):
return
else:
# This is necessary, because after a DnD operation in
# from and to ourself, the up event is swallowed by the
# DnD code. So on next non-up event (which means here and
# now) self._lineSelectSingleOnUp should be reset.
self._lineSelectSingleOnUp = -1
if event.RightDown():
if self.SendNotify(current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition()):
self._lineBeforeLastClicked = self._lineLastClicked
self._lineLastClicked = current
# If the item is already selected, do not update the selection.
# Multi-selections should not be cleared if a selected item is clicked.
if not self.IsHighlighted(current):
self.HighlightAll(False)
self.ChangeCurrent(current)
self.ReverseHighlight(self._current)
# Allow generation of context menu event
event.Skip()
elif event.MiddleDown():
self.SendNotify(current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK)
elif event.LeftDown() or forceClick:
self._lineBeforeLastClicked = self._lineLastClicked
self._lineLastClicked = current
oldCurrent = self._current
oldWasSelected = self.IsHighlighted(self._current)
cmdModifierDown = event.CmdDown()
if self.IsSingleSel() or not (cmdModifierDown or event.ShiftDown()):
if self.IsSingleSel() or not self.IsHighlighted(current):
self.HighlightAll(False)
self.ChangeCurrent(current)
self.ReverseHighlight(self._current)
else: # multi sel & current is highlighted & no mod keys
self._lineSelectSingleOnUp = current
self.ChangeCurrent(current) # change focus
else: # multi sel & either ctrl or shift is down
if cmdModifierDown:
self.ChangeCurrent(current)
self.ReverseHighlight(self._current)
elif event.ShiftDown():
self.ChangeCurrent(current)
lineFrom, lineTo = oldCurrent, current
shift = 0
if lineTo < lineFrom:
lineTo = lineFrom
lineFrom = self._current
if not self.IsHighlighted(lineFrom):
shift = 1
for i in xrange(lineFrom+1, lineTo+1):
if self.IsHighlighted(i):
self.HighlightLine(i, False)
self.RefreshLine(i)
lineTo -= 1
self.HighlightLines(lineFrom, lineTo+shift)
else: # !ctrl, !shift
# test in the enclosing if should make it impossible
raise Exception("how did we get here?")
if newItem:
if event.LeftDown():
if newItem.GetKind() in [1, 2] and (hitResult & ULC_HITTEST_ONITEMCHECK):
self.CheckItem(newItem, not self.IsItemChecked(newItem))
if newItem.IsHyperText():
self.SetItemVisited(newItem, True)
self.HandleHyperLink(newItem)
if self._current != oldCurrent:
self.RefreshLine(oldCurrent)
# forceClick is only set if the previous click was on another item
self._lastOnSame = not forceClick and (self._current == oldCurrent) and oldWasSelected
if self.HasAGWFlag(ULC_STICKY_HIGHLIGHT) and self.HasAGWFlag(ULC_STICKY_NOSELEVENT) and self.HasAGWFlag(ULC_SEND_LEFTCLICK):
self.SendNotify(current, wxEVT_COMMAND_LIST_ITEM_LEFT_CLICK, event.GetPosition())
[docs] def DrawDnDArrow(self):
""" Draws a drag and drop visual representation of an arrow. """
dc = wx.ClientDC(self)
lineY = self.GetLineY(self._dropTarget)
width = self.GetTotalWidth()
dc.SetPen(wx.Pen(wx.BLACK, 2))
x, y = self.CalcScrolledPosition(HEADER_OFFSET_X, lineY+2*HEADER_OFFSET_Y)
tri1 = [wx.Point(x+1, y-2), wx.Point(x+1, y+4), wx.Point(x+4, y+1)]
tri2 = [wx.Point(x+width-1, y-2), wx.Point(x+width-1, y+4), wx.Point(x+width-4, y+1)]
dc.DrawPolygon(tri1)
dc.DrawPolygon(tri2)
dc.DrawLine(x, y+1, width, y+1)
[docs] def DragFinish(self, event):
"""
A drag and drop operation has just finished.
:param `event`: a `wx.MouseEvent` event to be processed.
"""
if not self._isDragging:
return False
self._isDragging = False
self._dragCount = 0
self._dragItem = None
self.SetCursor(self._oldCursor)
self.Refresh()
le = UltimateListEvent(wxEVT_COMMAND_LIST_END_DRAG, self.GetParent().GetId())
le.SetEventObject(self.GetParent())
le.m_itemIndex = self._dropTarget
le.m_pointDrag = event.GetPosition()
self.GetParent().GetEventHandler().ProcessEvent(le)
return True
[docs] def HandleHyperLink(self, item):
"""
Handles the hyperlink items, sending the ``EVT_LIST_ITEM_HYPERLINK`` event.
:param `item`: an instance of L{UltimateListItem}.
"""
if self.IsItemHyperText(item):
self.SendNotify(item._itemId, wxEVT_COMMAND_LIST_ITEM_HYPERLINK)
[docs] def OnHoverTimer(self, event):
"""
Handles the ``wx.EVT_TIMER`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.TimerEvent` event to be processed.
"""
x, y = self.ScreenToClient(wx.GetMousePosition())
x, y = self.CalcUnscrolledPosition(x, y)
item, hitResult = self.HitTestLine(self._hoverItem, x, y)
if item and item._itemId == self._hoverItem:
if not self.IsHighlighted(self._hoverItem):
dontNotify = self.HasAGWFlag(ULC_STICKY_HIGHLIGHT) and self.HasAGWFlag(ULC_STICKY_NOSELEVENT)
if not dontNotify:
self.SendNotify(self._hoverItem, wxEVT_COMMAND_LIST_ITEM_SELECTED)
self.HighlightAll(False)
self.ChangeCurrent(self._hoverItem)
self.ReverseHighlight(self._current)
[docs] def MoveToItem(self, item):
"""
Scrolls the input item into view.
:param `item`: an instance of L{UltimateListItem}.
"""
if item == -1:
return
if item >= self.GetItemCount():
item = self.GetItemCount() - 1
rect = self.GetLineRect(item)
client_w, client_h = self.GetClientSize()
hLine = self.GetLineHeight(item)
view_x = SCROLL_UNIT_X*self.GetScrollPos(wx.HORIZONTAL)
view_y = hLine*self.GetScrollPos(wx.VERTICAL)
if self.InReportView():
# the next we need the range of lines shown it might be different, so
# recalculate it
self.ResetVisibleLinesRange()
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
if rect.y < view_y:
self.Scroll(-1, rect.y/hLine)
if rect.y+rect.height+5 > view_y+client_h:
self.Scroll(-1, (rect.y+rect.height-client_h+hLine)/hLine)
if wx.Platform == "__WXMAC__":
# At least on Mac the visible lines value will get reset inside of
# Scroll *before* it actually scrolls the window because of the
# Update() that happens there, so it will still have the wrong value.
# So let's reset it again and wait for it to be recalculated in the
# next paint event. I would expect this problem to show up in wxGTK
# too but couldn't duplicate it there. Perhaps the order of events
# is different... --Robin
self.ResetVisibleLinesRange()
else:
view_y = SCROLL_UNIT_Y*self.GetScrollPos(wx.VERTICAL)
start_y, height = rect.y, rect.height
if start_y < view_y:
while start_y > view_y:
start_y -= SCROLL_UNIT_Y
self.Scroll(-1, start_y/SCROLL_UNIT_Y)
if start_y + height > view_y + client_h:
while start_y + height < view_y + client_h:
start_y += SCROLL_UNIT_Y
self.Scroll(-1, (start_y+height-client_h+SCROLL_UNIT_Y)/SCROLL_UNIT_Y)
else: # !report
sx = sy = -1
if rect.x-view_x < 5:
sx = (rect.x - 5)/SCROLL_UNIT_X
if rect.x+rect.width-5 > view_x+client_w:
sx = (rect.x + rect.width - client_w + SCROLL_UNIT_X)/SCROLL_UNIT_X
if rect.y-view_y < 5:
sy = (rect.y - 5)/hLine
if rect.y + rect.height - 5 > view_y + client_h:
sy = (rect.y + rect.height - client_h + hLine)/hLine
self.Scroll(sx, sy)
# ----------------------------------------------------------------------------
# keyboard handling
# ----------------------------------------------------------------------------
[docs] def GetNextActiveItem(self, item, down=True):
"""
Returns the next active item. Used Internally at present.
:param `item`: an instance of L{UltimateListItem};
:param `down`: ``True`` to search downwards for an active item, ``False``
to search upwards.
"""
count = self.GetItemCount()
initialItem = item
while 1:
if item >= count or item < 0:
return initialItem
listItem = CreateListItem(item, 0)
listItem = self.GetItem(listItem, 0)
if listItem.IsEnabled():
return item
item = (down and [item+1] or [item-1])[0]
[docs] def OnArrowChar(self, newCurrent, event):
"""
Handles the keyboard arrows key events.
:param `newCurrent`: an integer specifying the new current item;
:param `event`: a `wx.KeyEvent` event to be processed.
"""
oldCurrent = self._current
newCurrent = self.GetNextActiveItem(newCurrent, newCurrent > oldCurrent)
# in single selection we just ignore Shift as we can't select several
# items anyhow
if event.ShiftDown() and not self.IsSingleSel():
self.ChangeCurrent(newCurrent)
# refresh the old focus to remove it
self.RefreshLine(oldCurrent)
# select all the items between the old and the new one
if oldCurrent > newCurrent:
newCurrent = oldCurrent
oldCurrent = self._current
self.HighlightLines(oldCurrent, newCurrent)
else: # !shift
# all previously selected items are unselected unless ctrl is held
# in a multi-selection control
if not event.ControlDown() or self.IsSingleSel():
self.HighlightAll(False)
self.ChangeCurrent(newCurrent)
# refresh the old focus to remove it
self.RefreshLine(oldCurrent)
if not event.ControlDown() or self.IsSingleSel():
self.HighlightLine(self._current, True)
self.RefreshLine(self._current)
self.MoveToFocus()
[docs] def OnKeyDown(self, event):
"""
Handles the ``wx.EVT_KEY_DOWN`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.KeyEvent` event to be processed.
"""
parent = self.GetParent()
# we propagate the key event upwards
ke = wx.KeyEvent(event.GetEventType())
ke.SetEventObject(parent)
if parent.GetEventHandler().ProcessEvent(ke):
event.Skip()
return
event.Skip()
[docs] def OnKeyUp(self, event):
"""
Handles the ``wx.EVT_KEY_UP`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.KeyEvent` event to be processed.
"""
parent = self.GetParent()
# we propagate the key event upwards
ke = wx.KeyEvent(event.GetEventType())
ke.SetEventObject(parent)
if parent.GetEventHandler().ProcessEvent(ke):
return
event.Skip()
[docs] def OnChar(self, event):
"""
Handles the ``wx.EVT_CHAR`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.KeyEvent` event to be processed.
"""
parent = self.GetParent()
if self.IsVirtual() and self.GetItemCount() == 0:
event.Skip()
return
# we send a list_key event up
if self.HasCurrent():
le = UltimateListEvent(wxEVT_COMMAND_LIST_KEY_DOWN, self.GetParent().GetId())
le.m_itemIndex = self._current
le.m_item = self.GetLine(self._current).GetItem(0, le.m_item)
le.m_code = event.GetKeyCode()
le.SetEventObject(parent)
parent.GetEventHandler().ProcessEvent(le)
keyCode = event.GetKeyCode()
if keyCode not in [wx.WXK_UP, wx.WXK_DOWN, wx.WXK_RIGHT, wx.WXK_LEFT, \
wx.WXK_PAGEUP, wx.WXK_PAGEDOWN, wx.WXK_END, wx.WXK_HOME]:
# propagate the char event upwards
ke = wx.KeyEvent(event.GetEventType())
ke.SetEventObject(parent)
if parent.GetEventHandler().ProcessEvent(ke):
return
if event.GetKeyCode() == wx.WXK_TAB:
nevent = wx.NavigationKeyEvent()
nevent.SetWindowChange(event.ControlDown())
nevent.SetDirection(not event.ShiftDown())
nevent.SetEventObject(self.GetParent().GetParent())
nevent.SetCurrentFocus(self._parent)
if self.GetParent().GetParent().GetEventHandler().ProcessEvent(nevent):
return
# no item . nothing to do
if not self.HasCurrent():
event.Skip()
return
keyCode = event.GetKeyCode()
if keyCode == wx.WXK_UP:
if self._current > 0:
self.OnArrowChar(self._current - 1, event)
if self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
self._dirty = True
elif keyCode == wx.WXK_DOWN:
if self._current < self.GetItemCount() - 1:
self.OnArrowChar(self._current + 1, event)
if self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
self._dirty = True
elif keyCode == wx.WXK_END:
if not self.IsEmpty():
self.OnArrowChar(self.GetItemCount() - 1, event)
self._dirty = True
elif keyCode == wx.WXK_HOME:
if not self.IsEmpty():
self.OnArrowChar(0, event)
self._dirty = True
elif keyCode == wx.WXK_PRIOR:
steps = (self.InReportView() and [self._linesPerPage - 1] or [self._current % self._linesPerPage])[0]
index = self._current - steps
if index < 0:
index = 0
self.OnArrowChar(index, event)
self._dirty = True
elif keyCode == wx.WXK_NEXT:
steps = (self.InReportView() and [self._linesPerPage - 1] or [self._linesPerPage - (self._current % self._linesPerPage) - 1])[0]
index = self._current + steps
count = self.GetItemCount()
if index >= count:
index = count - 1
self.OnArrowChar(index, event)
self._dirty = True
elif keyCode == wx.WXK_LEFT:
if not self.InReportView():
index = self._current - self._linesPerPage
if index < 0:
index = 0
self.OnArrowChar(index, event)
elif keyCode == wx.WXK_RIGHT:
if not self.InReportView():
index = self._current + self._linesPerPage
count = self.GetItemCount()
if index >= count:
index = count - 1
self.OnArrowChar(index, event)
elif keyCode == wx.WXK_SPACE:
if self.IsSingleSel():
if event.ControlDown():
self.ReverseHighlight(self._current)
else: # normal space press
self.SendNotify(self._current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
else:
# select it in ReverseHighlight() below if unselected
self.ReverseHighlight(self._current)
elif keyCode in [wx.WXK_RETURN, wx.WXK_EXECUTE, wx.WXK_NUMPAD_ENTER]:
self.SendNotify(self._current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
else:
event.Skip()
# ----------------------------------------------------------------------------
# focus handling
# ----------------------------------------------------------------------------
[docs] def OnSetFocus(self, event):
"""
Handles the ``wx.EVT_SET_FOCUS`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.FocusEvent` event to be processed.
"""
if self.GetParent():
event = wx.FocusEvent(wx.wxEVT_SET_FOCUS, self.GetParent().GetId())
event.SetEventObject(self.GetParent())
if self.GetParent().GetEventHandler().ProcessEvent(event):
return
# wxGTK sends us EVT_SET_FOCUS events even if we had never got
# EVT_KILL_FOCUS before which means that we finish by redrawing the items
# which are already drawn correctly resulting in horrible flicker - avoid
# it
if not self._hasFocus:
self._hasFocus = True
self.Refresh()
[docs] def OnKillFocus(self, event):
"""
Handles the ``wx.EVT_KILL_FOCUS`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.FocusEvent` event to be processed.
"""
if self.GetParent():
event = wx.FocusEvent(wx.wxEVT_KILL_FOCUS, self.GetParent().GetId())
event.SetEventObject(self.GetParent())
if self.GetParent().GetEventHandler().ProcessEvent(event):
return
self._hasFocus = False
self.Refresh()
[docs] def DrawImage(self, index, dc, x, y, enabled):
"""
Draws one of the item images.
:param `index`: the index of the image inside the image list;
:param `dc`: an instance of `wx.DC`;
:param `x`: the x position where to draw the image;
:param `y`: the y position where to draw the image;
:param `enabled`: ``True`` if the item is enabled, ``False`` if it is disabled.
"""
if self.HasAGWFlag(ULC_ICON) and self._normal_image_list:
imgList = (enabled and [self._normal_image_list] or [self._normal_grayed_image_list])[0]
imgList.Draw(index, dc, x, y, wx.IMAGELIST_DRAW_TRANSPARENT)
elif self.HasAGWFlag(ULC_SMALL_ICON) and self._small_image_list:
imgList = (enabled and [self._small_image_list] or [self._small_grayed_image_list])[0]
imgList.Draw(index, dc, x, y, wx.IMAGELIST_DRAW_TRANSPARENT)
elif self.HasAGWFlag(ULC_LIST) and self._small_image_list:
imgList = (enabled and [self._small_image_list] or [self._small_grayed_image_list])[0]
imgList.Draw(index, dc, x, y, wx.IMAGELIST_DRAW_TRANSPARENT)
elif self.InReportView() and self._small_image_list:
imgList = (enabled and [self._small_image_list] or [self._small_grayed_image_list])[0]
imgList.Draw(index, dc, x, y, wx.IMAGELIST_DRAW_TRANSPARENT)
[docs] def DrawCheckbox(self, dc, x, y, kind, checked, enabled):
"""
Draws the item checkbox/radiobutton image.
:param `dc`: an instance of `wx.DC`;
:param `x`: the x position where to draw the image;
:param `y`: the y position where to draw the image;
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
:param `checked`: ``True`` if the item is checked, ``False`` otherwise;
:param `enabled`: ``True`` if the item is enabled, ``False`` if it is disabled.
"""
imgList = (enabled and [self._image_list_check] or [self._grayed_check_list])[0]
if kind == 1:
# checkbox
index = (checked and [0] or [1])[0]
else:
# radiobutton
index = (checked and [2] or [3])[0]
imgList.Draw(index, dc, x, y, wx.IMAGELIST_DRAW_TRANSPARENT)
[docs] def GetCheckboxImageSize(self):
""" Returns the checkbox/radiobutton image size. """
bmp = self._image_list_check.GetBitmap(0)
return bmp.GetWidth(), bmp.GetHeight()
[docs] def GetImageSize(self, index):
"""
Returns the image size for the item.
:param `index`: the image index.
"""
width = height = 0
if self.HasAGWFlag(ULC_ICON) and self._normal_image_list:
for indx in index:
w, h = self._normal_image_list.GetSize(indx)
width += w + MARGIN_BETWEEN_TEXT_AND_ICON
height = max(height, h)
elif self.HasAGWFlag(ULC_SMALL_ICON) and self._small_image_list:
for indx in index:
w, h = self._small_image_list.GetSize(indx)
width += w + MARGIN_BETWEEN_TEXT_AND_ICON
height = max(height, h)
elif self.HasAGWFlag(ULC_LIST) and self._small_image_list:
for indx in index:
w, h = self._small_image_list.GetSize(indx)
width += w + MARGIN_BETWEEN_TEXT_AND_ICON
height = max(height, h)
elif self.InReportView() and self._small_image_list:
for indx in index:
w, h = self._small_image_list.GetSize(indx)
width += w + MARGIN_BETWEEN_TEXT_AND_ICON
height = max(height, h)
return width, height
[docs] def GetTextLength(self, s):
"""
Returns the text width for the input string.
:param `s`: the string to measure.
"""
dc = wx.ClientDC(self)
dc.SetFont(self.GetFont())
lw, lh, dummy = dc.GetMultiLineTextExtent(s)
return lw + AUTOSIZE_COL_MARGIN
[docs] def SetImageList(self, imageList, which):
"""
Sets the image list associated with the control.
:param `imageList`: an instance of `wx.ImageList` or an instance of L{PyImageList};
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
:note: Using L{PyImageList} enables you to have images of different size inside the
image list. In your derived class, instead of doing this::
imageList = wx.ImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
You should do this::
imageList = PyImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
"""
self._dirty = True
if isinstance(imageList, PyImageList):
# We have a custom PyImageList with variable image sizes
cls = PyImageList
else:
cls = wx.ImageList
# calc the spacing from the icon size
width = height = 0
if imageList and imageList.GetImageCount():
width, height = imageList.GetSize(0)
if which == wx.IMAGE_LIST_NORMAL:
self._normal_image_list = imageList
self._normal_grayed_image_list = cls(width, height, True, 0)
for ii in xrange(imageList.GetImageCount()):
bmp = imageList.GetBitmap(ii)
newbmp = MakeDisabledBitmap(bmp)
self._normal_grayed_image_list.Add(newbmp)
self._normal_spacing = width + 8
if which == wx.IMAGE_LIST_SMALL:
self._small_image_list = imageList
self._small_spacing = width + 14
self._small_grayed_image_list = cls(width, height, True, 0)
for ii in xrange(imageList.GetImageCount()):
bmp = imageList.GetBitmap(ii)
newbmp = MakeDisabledBitmap(bmp)
self._small_grayed_image_list.Add(newbmp)
self._lineHeight = 0 # ensure that the line height will be recalc'd
self.ResetLineDimensions()
[docs] def SetImageListCheck(self, sizex, sizey, imglist=None):
"""
Sets the checkbox/radiobutton image list.
:param `sizex`: the width of the bitmaps in the `imglist`;
:param `sizey`: the height of the bitmaps in the `imglist`;
:param `imglist`: an instance of `wx.ImageList`.
"""
# Image list to hold disabled versions of each control
self._grayed_check_list = wx.ImageList(sizex, sizey, True, 0)
if imglist is None:
self._image_list_check = wx.ImageList(sizex, sizey)
# Get the Checkboxes
self._image_list_check.Add(self.GetControlBmp(checkbox=True,
checked=True,
enabled=True,
x=sizex, y=sizey))
self._grayed_check_list.Add(self.GetControlBmp(checkbox=True,
checked=True,
enabled=False,
x=sizex, y=sizey))
self._image_list_check.Add(self.GetControlBmp(checkbox=True,
checked=False,
enabled=True,
x=sizex, y=sizey))
self._grayed_check_list.Add(self.GetControlBmp(checkbox=True,
checked=False,
enabled=False,
x=sizex, y=sizey))
# Get the Radio Buttons
self._image_list_check.Add(self.GetControlBmp(checkbox=False,
checked=True,
enabled=True,
x=sizex, y=sizey))
self._grayed_check_list.Add(self.GetControlBmp(checkbox=False,
checked=True,
enabled=False,
x=sizex, y=sizey))
self._image_list_check.Add(self.GetControlBmp(checkbox=False,
checked=False,
enabled=True,
x=sizex, y=sizey))
self._grayed_check_list.Add(self.GetControlBmp(checkbox=False,
checked=False,
enabled=False,
x=sizex, y=sizey))
else:
sizex, sizey = imglist.GetSize(0)
self._image_list_check = imglist
for ii in xrange(self._image_list_check.GetImageCount()):
bmp = self._image_list_check.GetBitmap(ii)
newbmp = MakeDisabledBitmap(bmp)
self._grayed_check_list.Add(newbmp)
self._dirty = True
if imglist:
self.RecalculatePositions()
[docs] def GetControlBmp(self, checkbox=True, checked=False, enabled=True, x=16, y=16):
"""
Returns a native looking checkbox or radio button bitmap.
:param `checkbox`: ``True`` to get a checkbox image, ``False`` for a radiobutton
one;
:param `checked`: ``True`` if the control is marked, ``False`` if it is not;
:param `enabled`: ``True`` if the control is enabled, ``False`` if it is not;
:param `x`: the width of the bitmap, in pixels;
:param `y`: the height of the bitmap, in pixels.
"""
bmp = wx.EmptyBitmap(x, y)
mdc = wx.MemoryDC(bmp)
dc = wx.GCDC(mdc)
render = wx.RendererNative.Get()
if checked:
flag = wx.CONTROL_CHECKED
else:
flag = 0
if not enabled:
flag |= wx.CONTROL_DISABLED
if checkbox:
render.DrawCheckBox(self, mdc, (0, 0, x, y), flag)
else:
if _VERSION_STRING < "2.9":
render.DrawRadioButton(self, mdc, (0, 0, x, y), flag)
else:
render.DrawRadioBitmap(self, mdc, (0, 0, x, y), flag)
mdc.SelectObject(wx.NullBitmap)
return bmp
[docs] def SetItemSpacing(self, spacing, isSmall=False):
"""
Sets the spacing between item texts and icons.
:param `spacing`: the spacing between item texts and icons, in pixels;
:param `isSmall`: ``True`` if using a ``wx.IMAGE_LIST_SMALL`` image list,
``False`` if using a ``wx.IMAGE_LIST_NORMAL`` image list.
"""
self._dirty = True
if isSmall:
self._small_spacing = spacing
else:
self._normal_spacing = spacing
[docs] def GetItemSpacing(self, isSmall=False):
"""
Returns the spacing between item texts and icons, in pixels.
:param `isSmall`: ``True`` if using a ``wx.IMAGE_LIST_SMALL`` image list,
``False`` if using a ``wx.IMAGE_LIST_NORMAL`` image list.
"""
return (isSmall and [self._small_spacing] or [self._normal_spacing])[0]
# ----------------------------------------------------------------------------
# columns
# ----------------------------------------------------------------------------
[docs] def SetColumn(self, col, item):
"""
Sets information about this column.
:param `col`: an integer specifying the column index;
:param `item`: an instance of L{UltimateListItem}.
"""
column = self._columns[col]
if item._width == ULC_AUTOSIZE_USEHEADER:
item._width = self.GetTextLength(item._text)
column.SetItem(item)
headerWin = self.GetListCtrl()._headerWin
if headerWin:
headerWin._dirty = True
self._dirty = True
# invalidate it as it has to be recalculated
self._headerWidth = 0
[docs] def SetColumnWidth(self, col, width):
"""
Sets the column width.
:param `width`: can be a width in pixels or ``wx.LIST_AUTOSIZE`` (-1) or
``wx.LIST_AUTOSIZE_USEHEADER`` (-2) or ``ULC_AUTOSIZE_FILL`` (-3).
``wx.LIST_AUTOSIZE`` will resize the column to the length of its longest
item. ``wx.LIST_AUTOSIZE_USEHEADER`` will resize the column to the
length of the header (Win32) or 80 pixels (other platforms).
``ULC_AUTOSIZE_FILL`` will resize the column fill the remaining width
of the window.
:note: In small or normal icon view, col must be -1, and the column width
is set for all columns.
"""
if col < 0:
raise Exception("invalid column index")
if not self.InReportView() and not self.InTileView() and not self.HasAGWFlag(ULC_HEADER_IN_ALL_VIEWS):
raise Exception("SetColumnWidth() can only be called in report/tile modes or with the ULC_HEADER_IN_ALL_VIEWS flag set.")
self._dirty = True
headerWin = self.GetListCtrl()._headerWin
footerWin = self.GetListCtrl()._footerWin
if headerWin:
headerWin._dirty = True
if footerWin:
footerWin._dirty = True
column = self._columns[col]
count = self.GetItemCount()
if width == ULC_AUTOSIZE_FILL:
width = self.GetColumnWidth(col)
if width == 0:
width = WIDTH_COL_DEFAULT
self._resizeColumn = col
elif width == ULC_AUTOSIZE_USEHEADER:
width = self.GetTextLength(column.GetText())
width += 2*EXTRA_WIDTH
if column.GetKind() in [1, 2]:
ix, iy = self._owner.GetCheckboxImageSize()
width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
# check for column header's image availability
images = column.GetImage()
for img in images:
if self._small_image_list:
ix, iy = self._small_image_list.GetSize(img)
width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
elif width == ULC_AUTOSIZE:
if self.IsVirtual() or not self.InReportView():
# TODO: determine the max width somehow...
width = WIDTH_COL_DEFAULT
else: # !virtual
maxW = AUTOSIZE_COL_MARGIN
# if the cached column width isn't valid then recalculate it
if self._aColWidths[col]._bNeedsUpdate:
for i in xrange(count):
line = self.GetLine(i)
itemData = line._items[col]
item = UltimateListItem()
item = itemData.GetItem(item)
itemWidth = self.GetItemWidthWithImage(item)
if itemWidth > maxW and not item._overFlow:
maxW = itemWidth
self._aColWidths[col]._bNeedsUpdate = False
self._aColWidths[col]._nMaxWidth = maxW
maxW = self._aColWidths[col]._nMaxWidth
width = maxW + AUTOSIZE_COL_MARGIN
column.SetWidth(width)
# invalidate it as it has to be recalculated
self._headerWidth = 0
self._footerWidth = 0
if footerWin:
footerWin.Refresh()
[docs] def GetHeaderWidth(self):
""" Returns the header window width, in pixels. """
if not self._headerWidth:
count = self.GetColumnCount()
for col in xrange(count):
if not self.IsColumnShown(col):
continue
self._headerWidth += self.GetColumnWidth(col)
if self.HasAGWFlag(ULC_FOOTER):
self._footerWidth = self._headerWidth
return self._headerWidth
[docs] def GetColumn(self, col):
"""
Returns information about this column.
:param `col`: an integer specifying the column index.
"""
item = UltimateListItem()
column = self._columns[col]
item = column.GetItem(item)
return item
[docs] def GetColumnWidth(self, col):
"""
Returns the column width for the input column.
:param `col`: an integer specifying the column index.
"""
column = self._columns[col]
return column.GetWidth()
[docs] def GetTotalWidth(self):
""" Returns the total width of the columns in L{UltimateListCtrl}. """
width = 0
for column in self._columns:
width += column.GetWidth()
return width
# ----------------------------------------------------------------------------
# item state
# ----------------------------------------------------------------------------
[docs] def SetItem(self, item):
"""
Sets information about the item.
:param `item`: an instance of L{UltimateListItemData}.
"""
id = item._itemId
if id < 0 or id >= self.GetItemCount():
raise Exception("invalid item index in SetItem")
if not self.IsVirtual():
line = self.GetLine(id)
line.SetItem(item._col, item)
# Set item state if user wants
if item._mask & ULC_MASK_STATE:
self.SetItemState(item._itemId, item._state, item._state)
if self.InReportView():
# update the Max Width Cache if needed
width = self.GetItemWidthWithImage(item)
if width > self._aColWidths[item._col]._nMaxWidth:
self._aColWidths[item._col]._nMaxWidth = width
self._aColWidths[item._col]._bNeedsUpdate = True
if self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
line.ResetDimensions()
# update the item on screen
if self.InReportView():
rectItem = self.GetItemRect(id)
self.RefreshRect(rectItem)
[docs] def SetItemStateAll(self, state, stateMask):
"""
Sets the item state flags for all the items.
:param `state`: any combination of the following bits:
============================ ========= ==============================
State Bits Hex Value Description
============================ ========= ==============================
``ULC_STATE_DONTCARE`` 0x0 Don't care what the state is
``ULC_STATE_DROPHILITED`` 0x1 The item is highlighted to receive a drop event
``ULC_STATE_FOCUSED`` 0x2 The item has the focus
``ULC_STATE_SELECTED`` 0x4 The item is selected
``ULC_STATE_CUT`` 0x8 The item is in the cut state
``ULC_STATE_DISABLED`` 0x10 The item is disabled
``ULC_STATE_FILTERED`` 0x20 The item has been filtered
``ULC_STATE_INUSE`` 0x40 The item is in use
``ULC_STATE_PICKED`` 0x80 The item has been picked
``ULC_STATE_SOURCE`` 0x100 The item is a drag and drop source
============================ ========= ==============================
:param `stateMask`: the bitmask for the state flag.
:note: The valid state flags are influenced by the value of the state mask.
"""
if self.IsEmpty():
return
# first deal with selection
if stateMask & ULC_STATE_SELECTED:
# set/clear select state
if self.IsVirtual():
# optimized version for virtual listctrl.
self._selStore.SelectRange(0, self.GetItemCount() - 1, state==ULC_STATE_SELECTED)
self.Refresh()
elif state & ULC_STATE_SELECTED:
count = self.GetItemCount()
for i in xrange(count):
self.SetItemState(i, ULC_STATE_SELECTED, ULC_STATE_SELECTED)
else:
# clear for non virtual (somewhat optimized by using GetNextItem())
i = -1
while 1:
i += 1
if self.GetNextItem(i, ULC_NEXT_ALL, ULC_STATE_SELECTED) == -1:
break
self.SetItemState(i, 0, ULC_STATE_SELECTED)
if self.HasCurrent() and state == 0 and stateMask & ULC_STATE_FOCUSED:
# unfocus all: only one item can be focussed, so clearing focus for
# all items is simply clearing focus of the focussed item.
self.SetItemState(self._current, state, stateMask)
#(setting focus to all items makes no sense, so it is not handled here.)
[docs] def SetItemState(self, litem, state, stateMask):
"""
Sets the item state flags for the input item.
:param `litem`: the index of the item; if defaulted to -1, the state flag
will be set for all the items;
:param `state`: the item state flag;
:param `stateMask`: the bitmask for the state flag.
:see: L{SetItemStateAll} for a list of valid state flags.
"""
if litem == -1:
self.SetItemStateAll(state, stateMask)
return
if litem < 0 or litem >= self.GetItemCount():
raise Exception("invalid item index in SetItemState")
oldCurrent = self._current
item = litem # safe because of the check above
# do we need to change the focus?
if stateMask & ULC_STATE_FOCUSED:
if state & ULC_STATE_FOCUSED:
# don't do anything if this item is already focused
if item != self._current:
self.ChangeCurrent(item)
if oldCurrent != - 1:
if self.IsSingleSel():
self.HighlightLine(oldCurrent, False)
self.RefreshLine(oldCurrent)
self.RefreshLine(self._current)
else: # unfocus
# don't do anything if this item is not focused
if item == self._current:
self.ResetCurrent()
if self.IsSingleSel():
# we must unselect the old current item as well or we
# might end up with more than one selected item in a
# single selection control
self.HighlightLine(oldCurrent, False)
self.RefreshLine(oldCurrent)
# do we need to change the selection state?
if stateMask & ULC_STATE_SELECTED:
on = (state & ULC_STATE_SELECTED) != 0
if self.IsSingleSel():
if on:
# selecting the item also makes it the focused one in the
# single sel mode
if self._current != item:
self.ChangeCurrent(item)
if oldCurrent != - 1:
self.HighlightLine(oldCurrent, False)
self.RefreshLine(oldCurrent)
else: # off
# only the current item may be selected anyhow
if item != self._current:
return
if self.HighlightLine(item, on):
self.RefreshLine(item)
[docs] def GetItemState(self, item, stateMask):
"""
Returns the item state flags for the input item.
:param `item`: the index of the item;
:param `stateMask`: the bitmask for the state flag.
:see: L{SetItemStateAll} for a list of valid state flags.
"""
if item < 0 or item >= self.GetItemCount():
raise Exception("invalid item index in GetItemState")
ret = ULC_STATE_DONTCARE
if stateMask & ULC_STATE_FOCUSED:
if item == self._current:
ret |= ULC_STATE_FOCUSED
if stateMask & ULC_STATE_SELECTED:
if self.IsHighlighted(item):
ret |= ULC_STATE_SELECTED
return ret
[docs] def GetItem(self, item, col=0):
"""
Returns the information about the input item.
:param `item`: an instance of L{UltimateListItem};
:param `col`: the column to which the item belongs to.
"""
if item._itemId < 0 or item._itemId >= self.GetItemCount():
raise Exception("invalid item index in GetItem")
line = self.GetLine(item._itemId)
item = line.GetItem(col, item)
# Get item state if user wants it
if item._mask & ULC_MASK_STATE:
item._state = self.GetItemState(item._itemId, ULC_STATE_SELECTED | ULC_STATE_FOCUSED)
return item
[docs] def CheckItem(self, item, checked=True, sendEvent=True):
"""
Actually checks/uncheks an item, sending (eventually) the two
events ``EVT_LIST_ITEM_CHECKING`` / ``EVT_LIST_ITEM_CHECKED``.
:param `item`: an instance of L{UltimateListItem};
:param `checked`: ``True`` to check an item, ``False`` to uncheck it;
:param `sendEvent`: ``True`` to send a {UltimateListEvent}, ``False`` otherwise.
:note: This method is meaningful only for checkbox-like and radiobutton-like items.
"""
# Should we raise an error here?!?
if item.GetKind() == 0 or not item.IsEnabled():
return
if sendEvent:
parent = self.GetParent()
le = UltimateListEvent(wxEVT_COMMAND_LIST_ITEM_CHECKING, parent.GetId())
le.m_itemIndex = item._itemId
le.m_item = item
le.SetEventObject(parent)
if parent.GetEventHandler().ProcessEvent(le):
# Blocked by user
return
item.Check(checked)
self.SetItem(item)
self.RefreshLine(item._itemId)
if not sendEvent:
return
le = UltimateListEvent(wxEVT_COMMAND_LIST_ITEM_CHECKED, parent.GetId())
le.m_itemIndex = item._itemId
le.m_item = item
le.SetEventObject(parent)
parent.GetEventHandler().ProcessEvent(le)
[docs] def AutoCheckChild(self, isChecked, column):
"""
Checks/unchecks all the items.
:param `isChecked`: ``True`` to check the items, ``False`` to uncheck them;
:param `column`: the column to which the items belongs to.
:note: This method is meaningful only for checkbox-like and radiobutton-like items.
"""
for indx in xrange(self.GetItemCount()):
item = CreateListItem(indx, column)
newItem = self.GetItem(item, column)
self.CheckItem(newItem, not isChecked, False)
[docs] def AutoToggleChild(self, column):
"""
Toggles all the items.
:param `column`: the column to which the items belongs to.
:note: This method is meaningful only for checkbox-like and radiobutton-like items.
"""
for indx in xrange(self.GetItemCount()):
item = CreateListItem(indx, column)
newItem = self.GetItem(item, column)
if newItem.GetKind() != 1:
continue
self.CheckItem(newItem, not item.IsChecked(), False)
[docs] def IsItemChecked(self, item):
"""
Returns whether an item is checked or not.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.IsChecked()
[docs] def IsItemEnabled(self, item):
"""
Returns whether an item is enabled or not.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.IsEnabled()
[docs] def EnableItem(self, item, enable=True):
"""
Enables/disables an item.
:param `item`: an instance of L{UltimateListItem};
:param `enable`: ``True`` to enable the item, ``False`` otherwise.
"""
item = self.GetItem(item, 0)
if item.IsEnabled() == enable:
return False
item.Enable(enable)
wnd = item.GetWindow()
# Handles the eventual window associated to the item
if wnd:
wnd.Enable(enable)
self.SetItem(item)
return True
[docs] def GetItemKind(self, item):
"""
Returns the item kind.
:param `item`: an instance of L{UltimateListItem}.
:see: L{SetItemKind} for a list of valid item kinds.
"""
item = self.GetItem(item, item._col)
return item.GetKind()
[docs] def SetItemKind(self, item, kind):
"""
Sets the item kind.
:param `item`: an instance of L{UltimateListItem};
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
item = self.GetItem(item, item._col)
item.SetKind(kind)
self.SetItem(item)
return True
[docs] def IsItemHyperText(self, item):
"""
Returns whether an item is hypertext or not.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.IsHyperText()
[docs] def SetItemHyperText(self, item, hyper=True):
"""
Sets whether the item is hypertext or not.
:param `item`: an instance of L{UltimateListItem};
:param `hyper`: ``True`` to have an item with hypertext behaviour, ``False`` otherwise.
"""
item = self.GetItem(item, item._col)
item.SetHyperText(hyper)
self.SetItem(item)
return True
[docs] def GetHyperTextFont(self):
"""Returns the font used to render an hypertext item."""
return self._hypertextfont
[docs] def SetHyperTextFont(self, font):
"""
Sets the font used to render hypertext items.
:param `font`: a valid `wx.Font` instance.
"""
self._hypertextfont = font
self._dirty = True
[docs] def SetHyperTextNewColour(self, colour):
"""
Sets the colour used to render a non-visited hypertext item.
:param `colour`: a valid `wx.Colour` instance.
"""
self._hypertextnewcolour = colour
self._dirty = True
[docs] def GetHyperTextNewColour(self):
""" Returns the colour used to render a non-visited hypertext item. """
return self._hypertextnewcolour
[docs] def SetHyperTextVisitedColour(self, colour):
"""
Sets the colour used to render a visited hypertext item.
:param `colour`: a valid `wx.Colour` instance.
"""
self._hypertextvisitedcolour = colour
self._dirty = True
[docs] def GetHyperTextVisitedColour(self):
""" Returns the colour used to render a visited hypertext item. """
return self._hypertextvisitedcolour
[docs] def SetItemVisited(self, item, visited=True):
"""
Sets whether an hypertext item was visited.
:param `item`: an instance of L{UltimateListItem};
:param `visited`: ``True`` to mark an hypertext item as visited, ``False`` otherwise.
"""
newItem = self.GetItem(item, item._col)
newItem.SetVisited(visited)
self.SetItem(newItem)
self.RefreshLine(item)
return True
[docs] def GetItemVisited(self, item):
"""
Returns whether an hypertext item was visited.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.GetVisited()
[docs] def GetItemWindow(self, item):
"""
Returns the window associated to the item (if any).
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.GetWindow()
[docs] def SetItemWindow(self, item, wnd, expand=False):
"""
Sets the window for the given item.
:param `item`: an instance of L{UltimateListItem};
:param `wnd`: if not ``None``, a non-toplevel window to be displayed next to
the item;
:param `expand`: ``True`` to expand the column where the item/subitem lives,
so that the window will be fully visible.
"""
if not self.InReportView() or not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("Widgets are only allowed in report mode and with the ULC_HAS_VARIABLE_ROW_HEIGHT style.")
item = self.GetItem(item, item._col)
if wnd is not None:
self._hasWindows = True
if item not in self._itemWithWindow:
self._itemWithWindow.append(item)
else:
self.DeleteItemWindow(item)
else:
self.DeleteItemWindow(item)
item.SetWindow(wnd, expand)
self.SetItem(item)
self.RecalculatePositions()
self.Refresh()
[docs] def DeleteItemWindow(self, item):
"""
Deletes the window associated to an item (if any).
:param `item`: an instance of L{UltimateListItem}.
"""
if item.GetWindow() is None:
return
item.DeleteWindow()
if item in self._itemWithWindow:
self._itemWithWindow.remove(item)
self.SetItem(item)
self.RecalculatePositions()
[docs] def GetItemWindowEnabled(self, item):
"""
Returns whether the window associated to the item is enabled.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.GetWindowEnabled()
[docs] def SetItemWindowEnabled(self, item, enable=True):
"""
Enables/disables the window associated to the item.
:param `item`: an instance of L{UltimateListItem};
:param `enable`: ``True`` to enable the associated window, ``False`` to
disable it.
"""
item = self.GetItem(item, item._col)
item.SetWindowEnabled(enable)
self.SetItem(item)
self.Refresh()
[docs] def SetColumnCustomRenderer(self, col=0, renderer=None):
"""
Associate a custom renderer to this column's header
:param `col`: the column index.
:param `renderer`: a class able to correctly render the input item.
:note: the renderer class **must** implement the methods `DrawHeaderButton`
and `GetForegroundColor`.
"""
self._columns[col].SetCustomRenderer(renderer)
[docs] def GetColumnCustomRenderer(self, col):
"""
Returns the custom renderer used to draw the column header
:param `col`: the column index.
"""
return self._columns[col].GetCustomRenderer()
[docs] def GetItemCustomRenderer(self, item):
"""
Returns the custom renderer used to draw the input item (if any).
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.GetCustomRenderer()
[docs] def SetItemCustomRenderer(self, item, renderer=None):
"""
Associate a custom renderer to this item.
:param `item`: an instance of L{UltimateListItem};
:param `renderer`: a class able to correctly render the item.
:note: the renderer class **must** implement the methods `DrawSubItem`,
`GetLineHeight` and `GetSubItemWidth`.
"""
item = self.GetItem(item, item._col)
item.SetCustomRenderer(renderer)
self.SetItem(item)
self.ResetLineDimensions()
self.Refresh()
[docs] def GetItemOverFlow(self, item):
"""
Returns if the item is in the overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `item`: an instance of L{UltimateListItem}.
"""
item = self.GetItem(item, item._col)
return item.GetOverFlow()
[docs] def SetItemOverFlow(self, item, over=True):
"""
Sets the item in the overflow/non overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `item`: an instance of L{UltimateListItem};
:param `over`: ``True`` to set the item in a overflow state, ``False`` otherwise.
"""
item = self.GetItem(item, item._col)
item.SetOverFlow(over)
self.SetItem(item)
self.Refresh()
# ----------------------------------------------------------------------------
# item count
# ----------------------------------------------------------------------------
[docs] def GetItemCount(self):
""" Returns the number of items in the L{UltimateListCtrl}. """
return (self.IsVirtual() and [self._countVirt] or [len(self._lines)])[0]
[docs] def SetItemCount(self, count):
"""
This method can only be used with virtual L{UltimateListCtrl}. It is used to
indicate to the control the number of items it contains. After calling it,
the main program should be ready to handle calls to various item callbacks
(such as L{UltimateListCtrl.OnGetItemText}) for all items in the range from 0 to `count`.
:param `count`: the total number of items in L{UltimateListCtrl}.
"""
self._selStore.SetItemCount(count)
self._countVirt = count
self.ResetVisibleLinesRange()
# scrollbars must be reset
self._dirty = True
[docs] def GetSelectedItemCount(self):
""" Returns the number of selected items in L{UltimateListCtrl}. """
# deal with the quick case first
if self.IsSingleSel():
return (self.HasCurrent() and [self.IsHighlighted(self._current)] or [False])[0]
# virtual controls remmebers all its selections itself
if self.IsVirtual():
return self._selStore.GetSelectedCount()
# TODO: we probably should maintain the number of items selected even for
# non virtual controls as enumerating all lines is really slow...
countSel = 0
count = self.GetItemCount()
for line in xrange(count):
if self.GetLine(line).IsHighlighted():
countSel += 1
return countSel
# ----------------------------------------------------------------------------
# item position/size
# ----------------------------------------------------------------------------
[docs] def GetViewRect(self):
"""
Returns the rectangle taken by all items in the control. In other words,
if the controls client size were equal to the size of this rectangle, no
scrollbars would be needed and no free space would be left.
:note: This function only works in the icon and small icon views, not in
list or report views.
"""
if self.HasAGWFlag(ULC_LIST):
raise Exception("UltimateListCtrl.GetViewRect() not implemented for list view")
# we need to find the longest/tallest label
xMax = yMax = 0
count = self.GetItemCount()
if count:
for i in xrange(count):
# we need logical, not physical, coordinates here, so use
# GetLineRect() instead of GetItemRect()
r = self.GetLineRect(i)
x, y = r.GetRight(), r.GetBottom()
if x > xMax:
xMax = x
if y > yMax:
yMax = y
# some fudge needed to make it look prettier
xMax += 2*EXTRA_BORDER_X
yMax += 2*EXTRA_BORDER_Y
# account for the scrollbars if necessary
sizeAll = self.GetClientSize()
if xMax > sizeAll.x:
yMax += wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y)
if yMax > sizeAll.y:
xMax += wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
return wx.Rect(0, 0, xMax, yMax)
[docs] def GetSubItemRect(self, item, subItem):
"""
Returns the rectangle representing the size and position, in physical coordinates,
of the given subitem, i.e. the part of the row `item` in the column `subItem`.
:param `item`: the row in which the item lives;
:param `subItem`: the column in which the item lives. If set equal to the special
value ``ULC_GETSUBITEMRECT_WHOLEITEM`` the return value is the same as for
L{GetItemRect}.
:note: This method is only meaningful when the L{UltimateListCtrl} is in the
report mode.
"""
if not self.InReportView() and subItem == ULC_GETSUBITEMRECT_WHOLEITEM:
raise Exception("GetSubItemRect only meaningful in report view")
if item < 0 or item >= self.GetItemCount():
raise Exception("invalid item in GetSubItemRect")
# ensure that we're laid out, otherwise we could return nonsense
if self._dirty:
self.RecalculatePositions(True)
rect = self.GetLineRect(item)
# Adjust rect to specified column
if subItem != ULC_GETSUBITEMRECT_WHOLEITEM:
if subItem < 0 or subItem >= self.GetColumnCount():
raise Exception("invalid subItem in GetSubItemRect")
for i in xrange(subItem):
rect.x += self.GetColumnWidth(i)
rect.width = self.GetColumnWidth(subItem)
rect.x, rect.y = self.CalcScrolledPosition(rect.x, rect.y)
return rect
[docs] def GetItemRect(self, item):
"""
Returns the rectangle representing the item's size and position, in physical
coordinates.
:param `item`: the row in which the item lives.
"""
return self.GetSubItemRect(item, ULC_GETSUBITEMRECT_WHOLEITEM)
[docs] def GetItemPosition(self, item):
"""
Returns the position of the item, in icon or small icon view.
:param `item`: the row in which the item lives.
"""
rect = self.GetItemRect(item)
return wx.Point(rect.x, rect.y)
# ----------------------------------------------------------------------------
# geometry calculation
# ----------------------------------------------------------------------------
[docs] def RecalculatePositions(self, noRefresh=False):
"""
Recalculates all the items positions, and sets the scrollbars positions
too.
:param `noRefresh`: ``True`` to avoid calling `Refresh`, ``False`` otherwise.
"""
count = self.GetItemCount()
if self.HasAGWFlag(ULC_ICON) and self._normal_image_list:
iconSpacing = self._normal_spacing
elif self.HasAGWFlag(ULC_SMALL_ICON) and self._small_image_list:
iconSpacing = self._small_spacing
else:
iconSpacing = 0
# Note that we do not call GetClientSize() here but
# GetSize() and subtract the border size for sunken
# borders manually. This is technically incorrect,
# but we need to know the client area's size WITHOUT
# scrollbars here. Since we don't know if there are
# any scrollbars, we use GetSize() instead. Another
# solution would be to call SetScrollbars() here to
# remove the scrollbars and call GetClientSize() then,
# but this might result in flicker and - worse - will
# reset the scrollbars to 0 which is not good at all
# if you resize a dialog/window, but don't want to
# reset the window scrolling. RR.
# Furthermore, we actually do NOT subtract the border
# width as 2 pixels is just the extra space which we
# need around the actual content in the window. Other-
# wise the text would e.g. touch the upper border. RR.
clientWidth, clientHeight = self.GetSize()
if self.InReportView():
self.ResetVisibleLinesRange()
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
# all lines have the same height and we scroll one line per step
lineHeight = self.GetLineHeight()
entireHeight = count*lineHeight + LINE_SPACING
decrement = 0
if entireHeight > self.GetClientSize()[1]:
decrement = SCROLL_UNIT_X
self._linesPerPage = clientHeight/lineHeight
self.SetScrollbars(SCROLL_UNIT_X, lineHeight,
(self.GetHeaderWidth()-decrement)/SCROLL_UNIT_X,
(entireHeight + lineHeight - 1)/lineHeight,
self.GetScrollPos(wx.HORIZONTAL),
self.GetScrollPos(wx.VERTICAL),
True)
else:
if count > 0:
entireHeight = self.GetLineY(count-1) + self.GetLineHeight(count-1) + LINE_SPACING
lineFrom, lineTo = self.GetVisibleLinesRange()
self._linesPerPage = lineTo - lineFrom + 1
else:
lineHeight = self.GetLineHeight()
entireHeight = count*lineHeight + LINE_SPACING
self._linesPerPage = clientHeight/lineHeight
decrement = 0
if entireHeight > self.GetClientSize()[1]:
decrement = SCROLL_UNIT_X
self.SetScrollbars(SCROLL_UNIT_X, SCROLL_UNIT_Y,
(self.GetHeaderWidth()-decrement)/SCROLL_UNIT_X,
(entireHeight + SCROLL_UNIT_Y - 1)/SCROLL_UNIT_Y,
self.GetScrollPos(wx.HORIZONTAL),
self.GetScrollPos(wx.VERTICAL),
True)
else: # !report
dc = wx.ClientDC(self)
dc.SetFont(self.GetFont())
lineHeight = self.GetLineHeight()
# we have 3 different layout strategies: either layout all items
# horizontally/vertically (ULC_ALIGN_XXX styles explicitly given) or
# to arrange them in top to bottom, left to right (don't ask me why
# not the other way round...) order
if self.HasAGWFlag(ULC_ALIGN_LEFT | ULC_ALIGN_TOP):
x = EXTRA_BORDER_X
y = EXTRA_BORDER_Y
widthMax = 0
for i in xrange(count):
line = self.GetLine(i)
line.CalculateSize(dc, iconSpacing)
line.SetPosition(x, y, iconSpacing)
sizeLine = self.GetLineSize(i)
if self.HasAGWFlag(ULC_ALIGN_TOP):
if sizeLine.x > widthMax:
widthMax = sizeLine.x
y += sizeLine.y
else: # ULC_ALIGN_LEFT
x += sizeLine.x + MARGIN_BETWEEN_ROWS
if self.HasAGWFlag(ULC_ALIGN_TOP):
# traverse the items again and tweak their sizes so that they are
# all the same in a row
for i in xrange(count):
line = self.GetLine(i)
line._gi.ExtendWidth(widthMax)
self.SetScrollbars(SCROLL_UNIT_X, lineHeight,
(x + SCROLL_UNIT_X)/SCROLL_UNIT_X,
(y + lineHeight)/lineHeight,
self.GetScrollPos(wx.HORIZONTAL),
self.GetScrollPos(wx.VERTICAL),
True)
else: # "flowed" arrangement, the most complicated case
# at first we try without any scrollbars, if the items don't fit into
# the window, we recalculate after subtracting the space taken by the
# scrollbar
entireWidth = 0
for tries in xrange(2):
entireWidth = 2*EXTRA_BORDER_X
if tries == 1:
# Now we have decided that the items do not fit into the
# client area, so we need a scrollbar
entireWidth += SCROLL_UNIT_X
x = EXTRA_BORDER_X
y = EXTRA_BORDER_Y
maxWidthInThisRow = 0
self._linesPerPage = 0
currentlyVisibleLines = 0
for i in xrange(count):
currentlyVisibleLines += 1
line = self.GetLine(i)
line.CalculateSize(dc, iconSpacing)
line.SetPosition(x, y, iconSpacing)
sizeLine = self.GetLineSize(i)
if maxWidthInThisRow < sizeLine.x:
maxWidthInThisRow = sizeLine.x
y += sizeLine.y
if currentlyVisibleLines > self._linesPerPage:
self._linesPerPage = currentlyVisibleLines
if y + sizeLine.y >= clientHeight:
currentlyVisibleLines = 0
y = EXTRA_BORDER_Y
maxWidthInThisRow += MARGIN_BETWEEN_ROWS
x += maxWidthInThisRow
entireWidth += maxWidthInThisRow
maxWidthInThisRow = 0
# We have reached the last item.
if i == count - 1:
entireWidth += maxWidthInThisRow
if tries == 0 and entireWidth + SCROLL_UNIT_X > clientWidth:
clientHeight -= wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y)
self._linesPerPage = 0
break
if i == count - 1:
break # Everything fits, no second try required.
self.SetScrollbars(SCROLL_UNIT_X, lineHeight,
(entireWidth + SCROLL_UNIT_X)/SCROLL_UNIT_X,
0,
self.GetScrollPos(wx.HORIZONTAL),
0,
True)
self._dirty = False
if not noRefresh:
# FIXME: why should we call it from here?
self.UpdateCurrent()
self.RefreshAll()
[docs] def RefreshAll(self):
""" Refreshes the entire L{UltimateListCtrl}. """
self._dirty = False
self.Refresh()
headerWin = self.GetListCtrl()._headerWin
if headerWin and headerWin._dirty:
headerWin._dirty = False
headerWin.Refresh()
[docs] def UpdateCurrent(self):
""" Updates the current line selection. """
if not self.HasCurrent() and not self.IsEmpty():
self.ChangeCurrent(0)
[docs] def GetNextItem(self, item, geometry=ULC_NEXT_ALL, state=ULC_STATE_DONTCARE):
"""
Searches for an item with the given `geometry` or `state`, starting from `item`
but excluding the `item` itself.
:param `item`: the item at which starting the search. If set to -1, the first
item that matches the specified flags will be returned.
:param `geometry`: can be one of:
=================== ========= =================================
Geometry Flag Hex Value Description
=================== ========= =================================
``ULC_NEXT_ABOVE`` 0x0 Searches for an item above the specified item
``ULC_NEXT_ALL`` 0x1 Searches for subsequent item by index
``ULC_NEXT_BELOW`` 0x2 Searches for an item below the specified item
``ULC_NEXT_LEFT`` 0x3 Searches for an item to the left of the specified item
``ULC_NEXT_RIGHT`` 0x4 Searches for an item to the right of the specified item
=================== ========= =================================
:param `state`: any combination of the following bits:
============================ ========= ==============================
State Bits Hex Value Description
============================ ========= ==============================
``ULC_STATE_DONTCARE`` 0x0 Don't care what the state is
``ULC_STATE_DROPHILITED`` 0x1 The item is highlighted to receive a drop event
``ULC_STATE_FOCUSED`` 0x2 The item has the focus
``ULC_STATE_SELECTED`` 0x4 The item is selected
``ULC_STATE_CUT`` 0x8 The item is in the cut state
``ULC_STATE_DISABLED`` 0x10 The item is disabled
``ULC_STATE_FILTERED`` 0x20 The item has been filtered
``ULC_STATE_INUSE`` 0x40 The item is in use
``ULC_STATE_PICKED`` 0x80 The item has been picked
``ULC_STATE_SOURCE`` 0x100 The item is a drag and drop source
============================ ========= ==============================
:return: The first item with given `state` following `item` or -1 if no such item found.
:note: This function may be used to find all selected items in the
control like this::
item = -1
while 1:
item = listctrl.GetNextItem(item, ULC_NEXT_ALL, ULC_STATE_SELECTED)
if item == -1:
break
# This item is selected - do whatever is needed with it
wx.LogMessage("Item %ld is selected."%item)
"""
ret = item
maxI = self.GetItemCount()
# notice that we start with the next item (or the first one if item == -1)
# and this is intentional to allow writing a simple loop to iterate over
# all selected items
ret += 1
if ret == maxI:
# this is not an error because the index was ok initially, just no
# such item
return -1
if not state:
# any will do
return ret
for line in xrange(ret, maxI):
if state & ULC_STATE_FOCUSED and line == self._current:
return line
if state & ULC_STATE_SELECTED and self.IsHighlighted(line):
return line
return -1
# ----------------------------------------------------------------------------
# deleting stuff
# ----------------------------------------------------------------------------
[docs] def DeleteItem(self, lindex):
"""
Deletes the specified item.
:param `lindex`: the index of the item to delete.
:note: This function sends the ``EVT_LIST_DELETE_ITEM`` event for the item
being deleted.
"""
count = self.GetItemCount()
if lindex < 0 or lindex >= self.GetItemCount():
raise Exception("invalid item index in DeleteItem")
# we don't need to adjust the index for the previous items
if self.HasCurrent() and self._current >= lindex:
# if the current item is being deleted, we want the next one to
# become selected - unless there is no next one - so don't adjust
# self._current in this case
if self._current != lindex or self._current == count - 1:
self._current -= 1
if self.InReportView():
# mark the Column Max Width cache as dirty if the items in the line
# we're deleting contain the Max Column Width
line = self.GetLine(lindex)
item = UltimateListItem()
for i in xrange(len(self._columns)):
itemData = line._items[i]
item = itemData.GetItem(item)
itemWidth = self.GetItemWidthWithImage(item)
if itemWidth >= self._aColWidths[i]._nMaxWidth:
self._aColWidths[i]._bNeedsUpdate = True
if item.GetWindow():
self.DeleteItemWindow(item)
self.ResetVisibleLinesRange(True)
self._current = -1
self.SendNotify(lindex, wxEVT_COMMAND_LIST_DELETE_ITEM)
if self.IsVirtual():
self._countVirt -= 1
self._selStore.OnItemDelete(lindex)
else:
self._lines.pop(lindex)
# we need to refresh the (vert) scrollbar as the number of items changed
self._dirty = True
self._lineHeight = 0
self.ResetLineDimensions(True)
self.RecalculatePositions()
self.RefreshAfter(lindex)
[docs] def DeleteColumn(self, col):
"""
Deletes the specified column.
:param `col`: the index of the column to delete.
"""
self._columns.pop(col)
self._dirty = True
if not self.IsVirtual():
# update all the items
for i in xrange(len(self._lines)):
line = self.GetLine(i)
line._items.pop(col)
if self.InReportView(): # we only cache max widths when in Report View
self._aColWidths.pop(col)
# invalidate it as it has to be recalculated
self._headerWidth = 0
[docs] def DoDeleteAllItems(self):
""" Actually performs the deletion of all the items. """
if self.IsEmpty():
# nothing to do - in particular, don't send the event
return
self.ResetCurrent()
# to make the deletion of all items faster, we don't send the
# notifications for each item deletion in this case but only one event
# for all of them: this is compatible with wxMSW and documented in
# DeleteAllItems() description
event = UltimateListEvent(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, self.GetParent().GetId())
event.SetEventObject(self.GetParent())
self.GetParent().GetEventHandler().ProcessEvent(event)
if self.IsVirtual():
self._countVirt = 0
self._selStore.Clear()
if self.InReportView():
self.ResetVisibleLinesRange(True)
for i in xrange(len(self._aColWidths)):
self._aColWidths[i]._bNeedsUpdate = True
for item in self._itemWithWindow:
if item.GetWindow():
self.DeleteItemWindow(item)
self._lines = []
self._itemWithWindow = []
self._hasWindows = False
[docs] def DeleteAllItems(self):
"""
Deletes all items in the L{UltimateListCtrl}.
:note: This function does not send the ``EVT_LIST_DELETE_ITEM`` event because
deleting many items from the control would be too slow then (unlike L{DeleteItem}).
"""
self.DoDeleteAllItems()
self.RecalculatePositions()
[docs] def DeleteEverything(self):
""" Deletes all items in the L{UltimateListCtrl}, resetting column widths to zero. """
self._columns = []
self._aColWidths = []
self.DeleteAllItems()
# ----------------------------------------------------------------------------
# scanning for an item
# ----------------------------------------------------------------------------
[docs] def EnsureVisible(self, index):
"""
Ensures this item is visible.
:param `index`: the index of the item to scroll into view.
"""
if index < 0 or index >= self.GetItemCount():
raise Exception("invalid item index in EnsureVisible")
# We have to call this here because the label in question might just have
# been added and its position is not known yet
if self._dirty:
self.RecalculatePositions(True)
self.MoveToItem(index)
[docs] def FindItem(self, start, string, partial=False):
"""
Find an item whose label matches this string.
:param `start`: the starting point of the input `string` or the beginning
if `start` is -1;
:param `string`: the string to look for matches;
:param `partial`: if ``True`` then this method will look for items which
begin with `string`.
:note: The string comparison is case insensitive.
"""
if start < 0:
start = 0
str_upper = string.upper()
count = self.GetItemCount()
for i in xrange(start, count):
line = self.GetLine(i)
text = line.GetText(0)
line_upper = text.upper()
if not partial:
if line_upper == str_upper:
return i
else:
if line_upper.find(str_upper) == 0:
return i
return wx.NOT_FOUND
[docs] def FindItemData(self, start, data):
"""
Find an item whose data matches this data.
:param `start`: the starting point of the input `data` or the beginning
if `start` is -1;
:param `data`: the data to look for matches.
"""
if start < 0:
start = 0
count = self.GetItemCount()
for i in xrange(start, count):
line = self.GetLine(i)
item = UltimateListItem()
item = line.GetItem(0, item)
if item._data == data:
return i
return wx.NOT_FOUND
[docs] def FindItemAtPos(self, pt):
"""
Find an item nearest this position.
:param `pt`: an instance of `wx.Point`.
"""
topItem, dummy = self.GetVisibleLinesRange()
p = self.GetItemPosition(self.GetItemCount()-1)
if p.y == 0:
return topItem
id = int(math.floor(pt.y*float(self.GetItemCount()-topItem-1)/p.y+topItem))
if id >= 0 and id < self.GetItemCount():
return id
return wx.NOT_FOUND
[docs] def HitTest(self, x, y):
"""
HitTest method for a L{UltimateListCtrl}.
:param `x`: the mouse `x` position;
:param `y`: the mouse `y` position.
:see: L{HitTestLine} for a list of return flags.
"""
x, y = self.CalcUnscrolledPosition(x, y)
count = self.GetItemCount()
if self.InReportView():
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
current = y/self.GetLineHeight()
if current < count:
newItem, flags = self.HitTestLine(current, x, y)
if flags:
return current, flags
else:
for current in xrange(self._lineFrom, count):
newItem, flags = self.HitTestLine(current, x, y)
if flags:
return current, flags
else:
# TODO: optimize it too! this is less simple than for report view but
# enumerating all items is still not a way to do it!!
for current in xrange(count):
newItem, flags = self.HitTestLine(current, x, y)
if flags:
return current, flags
return wx.NOT_FOUND, None
# ----------------------------------------------------------------------------
# adding stuff
# ----------------------------------------------------------------------------
[docs] def InsertItem(self, item):
"""
Inserts an item into L{UltimateListCtrl}.
:param `item`: an instance of L{UltimateListItem}.
"""
if self.IsVirtual():
raise Exception("can't be used with virtual control")
count = self.GetItemCount()
if item._itemId < 0:
raise Exception("invalid item index")
CheckVariableRowHeight(self, item._text)
if item._itemId > count:
item._itemId = count
id = item._itemId
self._dirty = True
if self.InReportView():
self.ResetVisibleLinesRange(True)
# calculate the width of the item and adjust the max column width
pWidthInfo = self._aColWidths[item.GetColumn()]
width = self.GetItemWidthWithImage(item)
item.SetWidth(width)
if width > pWidthInfo._nMaxWidth:
pWidthInfo._nMaxWidth = width
line = UltimateListLineData(self)
line.SetItem(item._col, item)
self._lines.insert(id, line)
self._dirty = True
# If an item is selected at or below the point of insertion, we need to
# increment the member variables because the current row's index has gone
# up by one
if self.HasCurrent() and self._current >= id:
self._current += 1
self.SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM)
self.RefreshLines(id, self.GetItemCount() - 1)
[docs] def InsertColumn(self, col, item):
"""
Inserts a column into L{UltimateListCtrl}.
:param `col`: the column index at which we wish to insert a new column;
:param `item`: an instance of L{UltimateListItem}.
:note: This method is meaningful only if L{UltimateListCtrl} has the ``ULC_REPORT``
or the ``ULC_TILE`` styles set.
"""
self._dirty = True
if self.InReportView() or self.InTileView() or self.HasAGWFlag(ULC_HEADER_IN_ALL_VIEWS):
if item._width == ULC_AUTOSIZE_USEHEADER:
item._width = self.GetTextLength(item._text)
column = UltimateListHeaderData(item)
colWidthInfo = ColWidthInfo()
insert = (col >= 0) and (col < len(self._columns))
if insert:
self._columns.insert(col, column)
self._aColWidths.insert(col, colWidthInfo)
else:
self._columns.append(column)
self._aColWidths.append(colWidthInfo)
if not self.IsVirtual():
# update all the items
for i in xrange(len(self._lines)):
line = self.GetLine(i)
data = UltimateListItemData(self)
if insert:
line._items.insert(col, data)
else:
line._items.append(data)
# invalidate it as it has to be recalculated
self._headerWidth = 0
[docs] def GetItemWidthWithImage(self, item):
"""
Returns the item width, in pixels, considering the item text and its images.
:param `item`: an instance of L{UltimateListItem}.
"""
if item.GetCustomRenderer():
return item.GetCustomRenderer().GetSubItemWidth()
width = 0
dc = wx.ClientDC(self)
if item.GetFont().IsOk():
font = item.GetFont()
else:
font = self.GetFont()
dc.SetFont(font)
if item.GetKind() in [1, 2]:
ix, iy = self.GetCheckboxImageSize()
width += ix
if item.GetImage():
ix, iy = self.GetImageSize(item.GetImage())
width += ix + IMAGE_MARGIN_IN_REPORT_MODE
if item.GetText():
w, h, dummy = dc.GetMultiLineTextExtent(item.GetText())
width += w
if item.GetWindow():
width += item._windowsize.x + 5
return width
[docs] def GetItemTextSize(self, item):
"""
Returns the item width, in pixels, considering only the item text.
:param `item`: an instance of L{UltimateListItem}.
"""
width = ix = iy = start = end = 0
dc = wx.ClientDC(self)
if item.HasFont():
font = item.GetFont()
else:
font = self.GetFont()
dc.SetFont(font)
if item.GetKind() in [1, 2]:
ix, iy = self.GetCheckboxImageSize()
start += ix
if item.GetImage():
ix, iy = self.GetImageSize(item.GetImage())
start += ix + IMAGE_MARGIN_IN_REPORT_MODE
if item.GetText():
w, h, dummy = dc.GetMultiLineTextExtent(item.GetText())
end = w
return start, end
# ----------------------------------------------------------------------------
# sorting
# ----------------------------------------------------------------------------
[docs] def OnCompareItems(self, line1, line2):
"""
Returns whether 2 lines have the same index.
Override this function in the derived class to change the sort order of the items
in the L{UltimateListCtrl}. The function should return a negative, zero or positive
value if the first line is less than, equal to or greater than the second one.
:param `line1`: an instance of L{UltimateListItem};
:param `line2`: another instance of L{UltimateListItem}.
:note: The base class version compares lines by their index.
"""
item = UltimateListItem()
item1 = line1.GetItem(0, item)
item = UltimateListItem()
item2 = line2.GetItem(0, item)
data1 = item1._data
data2 = item2._data
if self.__func:
return self.__func(data1, data2)
else:
return cmp(data1, data2)
[docs] def SortItems(self, func):
"""
Call this function to sort the items in the L{UltimateListCtrl}. Sorting is done
using the specified function `func`. This function must have the
following prototype::
def OnCompareItems(self, line1, line2):
DoSomething(line1, line2)
# function code
It is called each time when the two items must be compared and should return 0
if the items are equal, negative value if the first item is less than the second
one and positive value if the first one is greater than the second one.
:param `func`: the method to use to sort the items. The default is to use the
L{OnCompareItems} method.
"""
self.HighlightAll(False)
self.ResetCurrent()
if self._hasWindows:
self.HideWindows()
if not func:
self.__func = None
else:
self.__func = func
self._lines.sort(self.OnCompareItems)
if self.IsShownOnScreen():
self._dirty = True
self._lineHeight = 0
self.ResetLineDimensions(True)
self.RecalculatePositions(True)
# ----------------------------------------------------------------------------
# scrolling
# ----------------------------------------------------------------------------
[docs] def OnScroll(self, event):
"""
Handles the ``wx.EVT_SCROLLWIN`` event for L{UltimateListMainWindow}.
:param `event`: a `wx.ScrollEvent` event to be processed.
"""
event.Skip()
# update our idea of which lines are shown when we redraw the window the
# next time
self.ResetVisibleLinesRange()
if self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
wx.CallAfter(self.RecalculatePositions, True)
if event.GetOrientation() == wx.HORIZONTAL:
lc = self.GetListCtrl()
if self.HasHeader():
lc._headerWin.Refresh()
lc._headerWin.Update()
if self.HasFooter():
lc._footerWin.Refresh()
lc._footerWin.Update()
[docs] def GetCountPerPage(self):
"""
Returns the number of items that can fit vertically in the visible area
of the L{UltimateListCtrl} (list or report view) or the total number of
items in the list control (icon or small icon view).
"""
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
if not self._linesPerPage:
self._linesPerPage = self.GetClientSize().y/self.GetLineHeight()
return self._linesPerPage
visibleFrom, visibleTo = self.GetVisibleLinesRange()
self._linesPerPage = visibleTo - visibleFrom + 1
return self._linesPerPage
[docs] def GetVisibleLinesRange(self):
"""
Returns the range of visible items on screen.
:note: This method can be used only if L{UltimateListCtrl} has the ``ULC_REPORT``
style set.
"""
if not self.InReportView():
raise Exception("this is for report mode only")
if self._lineFrom == -1:
count = self.GetItemCount()
if count:
if self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
view_x, view_y = self.GetViewStart()
view_y *= SCROLL_UNIT_Y
for i in xrange(0, count):
rc = self.GetLineY(i)
if rc > view_y:
self._lineFrom = i - 1
break
if self._lineFrom < 0:
self._lineFrom = 0
self._lineTo = self._lineFrom
clientWidth, clientHeight = self.GetClientSize()
for i in xrange(self._lineFrom, count):
rc = self.GetLineY(i) + self.GetLineHeight(i)
if rc > view_y + clientHeight - 5:
break
self._lineTo += 1
else:
# No variable row height
self._lineFrom = self.GetScrollPos(wx.VERTICAL)
# this may happen if SetScrollbars() hadn't been called yet
if self._lineFrom >= count:
self._lineFrom = count - 1
self._lineTo = self._lineFrom + self._linesPerPage
# we redraw one extra line but this is needed to make the redrawing
# logic work when there is a fractional number of lines on screen
if self._lineTo >= count:
self._lineTo = count - 1
else: # empty control
self._lineFrom = -1
self._lineTo = -1
return self._lineFrom, self._lineTo
[docs] def ResetTextControl(self):
""" Called by L{UltimateListTextCtrl} when it marks itself for deletion."""
self._textctrl.Destroy()
self._textctrl = None
self.RecalculatePositions()
self.Refresh()
[docs] def SetFirstGradientColour(self, colour=None):
"""
Sets the first gradient colour for gradient-style selections.
:param `colour`: if not ``None``, a valid `wx.Colour` instance. Otherwise,
the colour is taken from the system value ``wx.SYS_COLOUR_HIGHLIGHT``.
"""
if colour is None:
colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
self._firstcolour = colour
if self._usegradients:
self.RefreshSelected()
[docs] def SetSecondGradientColour(self, colour=None):
"""
Sets the second gradient colour for gradient-style selections.
:param `colour`: if not ``None``, a valid `wx.Colour` instance. Otherwise,
the colour generated is a slightly darker version of the L{UltimateListCtrl}
background colour.
"""
if colour is None:
# No colour given, generate a slightly darker from the
# UltimateListCtrl background colour
colour = self.GetBackgroundColour()
r, g, b = int(colour.Red()), int(colour.Green()), int(colour.Blue())
colour = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
colour = wx.Colour(colour[0], colour[1], colour[2])
self._secondcolour = colour
if self._usegradients:
self.RefreshSelected()
[docs] def GetFirstGradientColour(self):
""" Returns the first gradient colour for gradient-style selections. """
return self._firstcolour
[docs] def GetSecondGradientColour(self):
""" Returns the second gradient colour for gradient-style selections. """
return self._secondcolour
[docs] def EnableSelectionGradient(self, enable=True):
"""
Globally enables/disables drawing of gradient selections.
:param `enable`: ``True`` to enable gradient-style selections, ``False``
to disable it.
:note: Calling this method disables any Vista-style selection previously
enabled.
"""
self._usegradients = enable
self._vistaselection = False
self.RefreshSelected()
[docs] def SetGradientStyle(self, vertical=0):
"""
Sets the gradient style for gradient-style selections.
:param `vertical`: 0 for horizontal gradient-style selections, 1 for vertical
gradient-style selections.
"""
# 0 = Horizontal, 1 = Vertical
self._gradientstyle = vertical
if self._usegradients:
self.RefreshSelected()
[docs] def GetGradientStyle(self):
"""
Returns the gradient style for gradient-style selections.
:return: 0 for horizontal gradient-style selections, 1 for vertical
gradient-style selections.
"""
return self._gradientstyle
[docs] def EnableSelectionVista(self, enable=True):
"""
Globally enables/disables drawing of Windows Vista selections.
:param `enable`: ``True`` to enable Vista-style selections, ``False`` to
disable it.
:note: Calling this method disables any gradient-style selection previously
enabled.
"""
self._usegradients = False
self._vistaselection = enable
self.RefreshSelected()
[docs] def SetBackgroundImage(self, image):
"""
Sets the L{UltimateListCtrl} background image.
:param `image`: if not ``None``, an instance of `wx.Bitmap`.
:note: At present, the background image can only be used in "tile" mode.
:todo: Support background images also in stretch and centered modes.
"""
self._backgroundImage = image
self.Refresh()
[docs] def GetBackgroundImage(self):
"""
Returns the L{UltimateListCtrl} background image (if any).
:note: At present, the background image can only be used in "tile" mode.
:todo: Support background images also in stretch and centered modes.
"""
return self._backgroundImage
[docs] def SetWaterMark(self, watermark):
"""
Sets the L{UltimateListCtrl} watermark image to be displayed in the bottom
right part of the window.
:param `watermark`: if not ``None``, an instance of `wx.Bitmap`.
:todo: Better support for this is needed.
"""
self._waterMark = watermark
self.Refresh()
[docs] def GetWaterMark(self):
"""
Returns the L{UltimateListCtrl} watermark image (if any), displayed in the
bottom right part of the window.
:todo: Better support for this is needed.
"""
return self._waterMark
[docs] def SetDisabledTextColour(self, colour):
"""
Sets the items disabled colour.
:param `colour`: an instance of `wx.Colour`.
"""
# Disabled items colour
self._disabledColour = colour
self.Refresh()
[docs] def GetDisabledTextColour(self):
""" Returns the items disabled colour. """
return self._disabledColour
[docs] def ScrollList(self, dx, dy):
"""
Scrolls the L{UltimateListCtrl}.
:param `dx`: if in icon, small icon or report view mode, specifies the number
of pixels to scroll. If in list view mode, `dx` specifies the number of
columns to scroll.
:param `dy`: always specifies the number of pixels to scroll vertically.
"""
if not self.InReportView():
# TODO: this should work in all views but is not implemented now
return False
top, bottom = self.GetVisibleLinesRange()
if bottom == -1:
return 0
self.ResetVisibleLinesRange()
if not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
hLine = self.GetLineHeight()
self.Scroll(-1, top + dy/hLine)
else:
self.Scroll(-1, top + dy/SCROLL_UNIT_Y)
if wx.Platform == "__WXMAC__":
# see comment in MoveToItem() for why we do this
self.ResetVisibleLinesRange()
return True
# -------------------------------------------------------------------------------------
# UltimateListCtrl
# -------------------------------------------------------------------------------------
[docs]class UltimateListCtrl(wx.PyControl):
"""
UltimateListCtrl is a class that mimics the behaviour of `wx.ListCtrl`, with almost
the same base functionalities plus some more enhancements. This class does
not rely on the native control, as it is a full owner-drawn list control.
"""
[docs] def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
style=0, agwStyle=0, validator=wx.DefaultValidator, name="UltimateListCtrl"):
"""
Default class constructor.
:param `parent`: parent window. Must not be ``None``;
:param `id`: window identifier. A value of -1 indicates a default value;
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
chosen by either the windowing system or wxPython, depending on platform;
:param `size`: the control size. A value of (-1, -1) indicates a default size,
chosen by either the windowing system or wxPython, depending on platform;
:param `style`: the underlying `wx.PyControl` window style;
:param `agwStyle`: the AGW-specific window style; can be almost any combination of the following
bits:
=============================== =========== ====================================================================================================
Window Styles Hex Value Description
=============================== =========== ====================================================================================================
``ULC_VRULES`` 0x1 Draws light vertical rules between rows in report mode.
``ULC_HRULES`` 0x2 Draws light horizontal rules between rows in report mode.
``ULC_ICON`` 0x4 Large icon view, with optional labels.
``ULC_SMALL_ICON`` 0x8 Small icon view, with optional labels.
``ULC_LIST`` 0x10 Multicolumn list view, with optional small icons. Columns are computed automatically, i.e. you don't set columns as in ``ULC_REPORT``. In other words, the list wraps, unlike a `wx.ListBox`.
``ULC_REPORT`` 0x20 Single or multicolumn report view, with optional header.
``ULC_ALIGN_TOP`` 0x40 Icons align to the top. Win32 default, Win32 only.
``ULC_ALIGN_LEFT`` 0x80 Icons align to the left.
``ULC_AUTOARRANGE`` 0x100 Icons arrange themselves. Win32 only.
``ULC_VIRTUAL`` 0x200 The application provides items text on demand. May only be used with ``ULC_REPORT``.
``ULC_EDIT_LABELS`` 0x400 Labels are editable: the application will be notified when editing starts.
``ULC_NO_HEADER`` 0x800 No header in report mode.
``ULC_NO_SORT_HEADER`` 0x1000 No Docs.
``ULC_SINGLE_SEL`` 0x2000 Single selection (default is multiple).
``ULC_SORT_ASCENDING`` 0x4000 Sort in ascending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_TILE`` 0x10000 Each item appears as a full-sized icon with a label of one or more lines beside it (partially implemented).
``ULC_NO_HIGHLIGHT`` 0x20000 No highlight when an item is selected.
``ULC_STICKY_HIGHLIGHT`` 0x40000 Items are selected by simply hovering on them, with no need to click on them.
``ULC_STICKY_NOSELEVENT`` 0x80000 Don't send a selection event when using ``ULC_STICKY_HIGHLIGHT`` style.
``ULC_SEND_LEFTCLICK`` 0x100000 Send a left click event when an item is selected.
``ULC_HAS_VARIABLE_ROW_HEIGHT`` 0x200000 The list has variable row heights.
``ULC_AUTO_CHECK_CHILD`` 0x400000 When a column header has a checkbox associated, auto-check all the subitems in that column.
``ULC_AUTO_TOGGLE_CHILD`` 0x800000 When a column header has a checkbox associated, toggle all the subitems in that column.
``ULC_AUTO_CHECK_PARENT`` 0x1000000 Only meaningful foe checkbox-type items: when an item is checked/unchecked its column header item is checked/unchecked as well.
``ULC_SHOW_TOOLTIPS`` 0x2000000 Show tooltips for ellipsized items/subitems (text too long to be shown in the available space) containing the full item/subitem text.
``ULC_HOT_TRACKING`` 0x4000000 Enable hot tracking of items on mouse motion.
``ULC_BORDER_SELECT`` 0x8000000 Changes border colour whan an item is selected, instead of highlighting the item.
``ULC_TRACK_SELECT`` 0x10000000 Enables hot-track selection in a list control. Hot track selection means that an item is automatically selected when the cursor remains over the item for a certain period of time. The delay is retrieved on Windows using the `win32api` call `win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)`, and is defaulted to 400ms on other platforms. This style applies to all views of `UltimateListCtrl`.
``ULC_HEADER_IN_ALL_VIEWS`` 0x20000000 Show column headers in all view modes.
``ULC_NO_FULL_ROW_SELECT`` 0x40000000 When an item is selected, the only the item in the first column is highlighted.
``ULC_FOOTER`` 0x80000000 Show a footer too (only when header is present).
``ULC_USER_ROW_HEIGHT`` 0x100000000 Allows to set a custom row height (one value for all the items, only in report mode).
=============================== =========== ====================================================================================================
:param `validator`: the window validator;
:param `name`: the window name.
"""
self._imageListNormal = None
self._imageListSmall = None
self._imageListState = None
if not agwStyle & ULC_MASK_TYPE:
raise Exception("UltimateListCtrl style should have exactly one mode bit set")
if not (agwStyle & ULC_REPORT) and agwStyle & ULC_HAS_VARIABLE_ROW_HEIGHT:
raise Exception("Style ULC_HAS_VARIABLE_ROW_HEIGHT can only be used in report, non-virtual mode")
if agwStyle & ULC_STICKY_HIGHLIGHT and agwStyle & ULC_TRACK_SELECT:
raise Exception("Styles ULC_STICKY_HIGHLIGHT and ULC_TRACK_SELECT can not be combined")
if agwStyle & ULC_NO_HEADER and agwStyle & ULC_HEADER_IN_ALL_VIEWS:
raise Exception("Styles ULC_NO_HEADER and ULC_HEADER_IN_ALL_VIEWS can not be combined")
if agwStyle & ULC_USER_ROW_HEIGHT and (agwStyle & ULC_REPORT) == 0:
raise Exception("Style ULC_USER_ROW_HEIGHT can be used only with ULC_REPORT")
wx.PyControl.__init__(self, parent, id, pos, size, style|wx.CLIP_CHILDREN, validator, name)
self._mainWin = None
self._headerWin = None
self._footerWin = None
self._headerHeight = wx.RendererNative.Get().GetHeaderButtonHeight(self)
self._footerHeight = self._headerHeight
if wx.Platform == "__WXGTK__":
style &= ~wx.BORDER_MASK
style |= wx.BORDER_THEME
else:
if style & wx.BORDER_THEME:
style -= wx.BORDER_THEME
self._agwStyle = agwStyle
if style & wx.SUNKEN_BORDER:
style -= wx.SUNKEN_BORDER
self._mainWin = UltimateListMainWindow(self, wx.ID_ANY, wx.Point(0, 0), wx.DefaultSize, style, agwStyle)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self._mainWin, 1, wx.GROW)
self.SetSizer(sizer)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self.CreateOrDestroyHeaderWindowAsNeeded()
self.CreateOrDestroyFooterWindowAsNeeded()
self.SetInitialSize(size)
wx.CallAfter(self.Layout)
[docs] def GetDefaultBorder(self):
""" Returns the default window border. """
return wx.BORDER_THEME
[docs] def SetSingleStyle(self, style, add=True):
"""
Adds or removes a single window style.
:param `style`: can be one of the following bits:
=============================== =========== ====================================================================================================
Window Styles Hex Value Description
=============================== =========== ====================================================================================================
``ULC_VRULES`` 0x1 Draws light vertical rules between rows in report mode.
``ULC_HRULES`` 0x2 Draws light horizontal rules between rows in report mode.
``ULC_ICON`` 0x4 Large icon view, with optional labels.
``ULC_SMALL_ICON`` 0x8 Small icon view, with optional labels.
``ULC_LIST`` 0x10 Multicolumn list view, with optional small icons. Columns are computed automatically, i.e. you don't set columns as in ``ULC_REPORT``. In other words, the list wraps, unlike a `wx.ListBox`.
``ULC_REPORT`` 0x20 Single or multicolumn report view, with optional header.
``ULC_ALIGN_TOP`` 0x40 Icons align to the top. Win32 default, Win32 only.
``ULC_ALIGN_LEFT`` 0x80 Icons align to the left.
``ULC_AUTOARRANGE`` 0x100 Icons arrange themselves. Win32 only.
``ULC_VIRTUAL`` 0x200 The application provides items text on demand. May only be used with ``ULC_REPORT``.
``ULC_EDIT_LABELS`` 0x400 Labels are editable: the application will be notified when editing starts.
``ULC_NO_HEADER`` 0x800 No header in report mode.
``ULC_NO_SORT_HEADER`` 0x1000 No Docs.
``ULC_SINGLE_SEL`` 0x2000 Single selection (default is multiple).
``ULC_SORT_ASCENDING`` 0x4000 Sort in ascending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_TILE`` 0x10000 Each item appears as a full-sized icon with a label of one or more lines beside it (partially implemented).
``ULC_NO_HIGHLIGHT`` 0x20000 No highlight when an item is selected.
``ULC_STICKY_HIGHLIGHT`` 0x40000 Items are selected by simply hovering on them, with no need to click on them.
``ULC_STICKY_NOSELEVENT`` 0x80000 Don't send a selection event when using ``ULC_STICKY_HIGHLIGHT`` style.
``ULC_SEND_LEFTCLICK`` 0x100000 Send a left click event when an item is selected.
``ULC_HAS_VARIABLE_ROW_HEIGHT`` 0x200000 The list has variable row heights.
``ULC_AUTO_CHECK_CHILD`` 0x400000 When a column header has a checkbox associated, auto-check all the subitems in that column.
``ULC_AUTO_TOGGLE_CHILD`` 0x800000 When a column header has a checkbox associated, toggle all the subitems in that column.
``ULC_AUTO_CHECK_PARENT`` 0x1000000 Only meaningful foe checkbox-type items: when an item is checked/unchecked its column header item is checked/unchecked as well.
``ULC_SHOW_TOOLTIPS`` 0x2000000 Show tooltips for ellipsized items/subitems (text too long to be shown in the available space) containing the full item/subitem text.
``ULC_HOT_TRACKING`` 0x4000000 Enable hot tracking of items on mouse motion.
``ULC_BORDER_SELECT`` 0x8000000 Changes border colour whan an item is selected, instead of highlighting the item.
``ULC_TRACK_SELECT`` 0x10000000 Enables hot-track selection in a list control. Hot track selection means that an item is automatically selected when the cursor remains over the item for a certain period of time. The delay is retrieved on Windows using the `win32api` call `win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)`, and is defaulted to 400ms on other platforms. This style applies to all views of `UltimateListCtrl`.
``ULC_HEADER_IN_ALL_VIEWS`` 0x20000000 Show column headers in all view modes.
``ULC_NO_FULL_ROW_SELECT`` 0x40000000 When an item is selected, the only the item in the first column is highlighted.
``ULC_FOOTER`` 0x80000000 Show a footer too (only when header is present).
=============================== =========== ====================================================================================================
:param `add`: ``True`` to add the window style, ``False`` to remove it.
:note: The style ``ULC_VIRTUAL`` can not be set/unset after construction.
"""
if style & ULC_VIRTUAL:
raise Exception("ULC_VIRTUAL can't be [un]set")
flag = self.GetAGWWindowStyleFlag()
if add:
if style & ULC_MASK_TYPE:
flag &= ~(ULC_MASK_TYPE | ULC_VIRTUAL)
if style & ULC_MASK_ALIGN:
flag &= ~ULC_MASK_ALIGN
if style & ULC_MASK_SORT:
flag &= ~ULC_MASK_SORT
if add:
flag |= style
else:
flag &= ~style
# some styles can be set without recreating everything (as happens in
# SetAGWWindowStyleFlag() which calls ListMainWindow.DeleteEverything())
if not style & ~(ULC_HRULES | ULC_VRULES):
self.Refresh()
self.SetAGWWindowStyleFlag(self, flag)
else:
self.SetAGWWindowStyleFlag(flag)
[docs] def GetAGWWindowStyleFlag(self):
"""
Returns the L{UltimateListCtrl} AGW-specific style flag.
:see: L{SetAGWWindowStyleFlag} for a list of possible style flags.
"""
return self._agwStyle
[docs] def SetAGWWindowStyleFlag(self, style):
"""
Sets the L{UltimateListCtrl} AGW-specific style flag.
:param `style`: the AGW-specific window style; can be almost any combination of the following
bits:
=============================== =========== ====================================================================================================
Window Styles Hex Value Description
=============================== =========== ====================================================================================================
``ULC_VRULES`` 0x1 Draws light vertical rules between rows in report mode.
``ULC_HRULES`` 0x2 Draws light horizontal rules between rows in report mode.
``ULC_ICON`` 0x4 Large icon view, with optional labels.
``ULC_SMALL_ICON`` 0x8 Small icon view, with optional labels.
``ULC_LIST`` 0x10 Multicolumn list view, with optional small icons. Columns are computed automatically, i.e. you don't set columns as in ``ULC_REPORT``. In other words, the list wraps, unlike a `wx.ListBox`.
``ULC_REPORT`` 0x20 Single or multicolumn report view, with optional header.
``ULC_ALIGN_TOP`` 0x40 Icons align to the top. Win32 default, Win32 only.
``ULC_ALIGN_LEFT`` 0x80 Icons align to the left.
``ULC_AUTOARRANGE`` 0x100 Icons arrange themselves. Win32 only.
``ULC_VIRTUAL`` 0x200 The application provides items text on demand. May only be used with ``ULC_REPORT``.
``ULC_EDIT_LABELS`` 0x400 Labels are editable: the application will be notified when editing starts.
``ULC_NO_HEADER`` 0x800 No header in report mode.
``ULC_NO_SORT_HEADER`` 0x1000 No Docs.
``ULC_SINGLE_SEL`` 0x2000 Single selection (default is multiple).
``ULC_SORT_ASCENDING`` 0x4000 Sort in ascending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in `wx.ListCtrl.SortItems`.)
``ULC_TILE`` 0x10000 Each item appears as a full-sized icon with a label of one or more lines beside it (partially implemented).
``ULC_NO_HIGHLIGHT`` 0x20000 No highlight when an item is selected.
``ULC_STICKY_HIGHLIGHT`` 0x40000 Items are selected by simply hovering on them, with no need to click on them.
``ULC_STICKY_NOSELEVENT`` 0x80000 Don't send a selection event when using ``ULC_STICKY_HIGHLIGHT`` style.
``ULC_SEND_LEFTCLICK`` 0x100000 Send a left click event when an item is selected.
``ULC_HAS_VARIABLE_ROW_HEIGHT`` 0x200000 The list has variable row heights.
``ULC_AUTO_CHECK_CHILD`` 0x400000 When a column header has a checkbox associated, auto-check all the subitems in that column.
``ULC_AUTO_TOGGLE_CHILD`` 0x800000 When a column header has a checkbox associated, toggle all the subitems in that column.
``ULC_AUTO_CHECK_PARENT`` 0x1000000 Only meaningful foe checkbox-type items: when an item is checked/unchecked its column header item is checked/unchecked as well.
``ULC_SHOW_TOOLTIPS`` 0x2000000 Show tooltips for ellipsized items/subitems (text too long to be shown in the available space) containing the full item/subitem text.
``ULC_HOT_TRACKING`` 0x4000000 Enable hot tracking of items on mouse motion.
``ULC_BORDER_SELECT`` 0x8000000 Changes border colour whan an item is selected, instead of highlighting the item.
``ULC_TRACK_SELECT`` 0x10000000 Enables hot-track selection in a list control. Hot track selection means that an item is automatically selected when the cursor remains over the item for a certain period of time. The delay is retrieved on Windows using the `win32api` call `win32gui.SystemParametersInfo(win32con.SPI_GETMOUSEHOVERTIME)`, and is defaulted to 400ms on other platforms. This style applies to all views of `UltimateListCtrl`.
``ULC_HEADER_IN_ALL_VIEWS`` 0x20000000 Show column headers in all view modes.
``ULC_NO_FULL_ROW_SELECT`` 0x40000000 When an item is selected, the only the item in the first column is highlighted.
``ULC_FOOTER`` 0x80000000 Show a footer too (only when header is present).
``ULC_USER_ROW_HEIGHT`` 0x100000000 Allows to set a custom row height (one value for all the items, only in report mode).
=============================== =========== ====================================================================================================
"""
if style & ULC_HAS_VARIABLE_ROW_HEIGHT and not self.HasAGWFlag(ULC_REPORT):
raise Exception("ULC_HAS_VARIABLE_ROW_HEIGHT style can be used only in report mode")
wasInReportView = self.HasAGWFlag(ULC_REPORT)
self._agwStyle = style
if self._mainWin:
inReportView = (style & ULC_REPORT) != 0
if inReportView != wasInReportView:
# we need to notify the main window about this change as it must
# update its data structures
self._mainWin.SetReportView(inReportView)
self.CreateOrDestroyHeaderWindowAsNeeded()
self.CreateOrDestroyFooterWindowAsNeeded()
self.GetSizer().Layout()
if style & ULC_HAS_VARIABLE_ROW_HEIGHT:
self._mainWin.ResetLineDimensions()
self._mainWin.ResetVisibleLinesRange()
self.Refresh()
[docs] def HasAGWFlag(self, flag):
"""
Returns ``True`` if the window has the given flag bit set.
:param `flag`: the window style to check.
:see: L{SetAGWWindowStyleFlag} for a list of valid window styles.
"""
return self._agwStyle & flag
[docs] def SetUserLineHeight(self, height):
"""
Sets a custom value for the L{UltimateListCtrl} item height.
:param `height`: the custom height for all the items, in pixels.
:note: This method can be used only with ``ULC_REPORT`` and ``ULC_USER_ROW_HEIGHT`` styles set.
"""
if self._mainWin:
self._mainWin.SetUserLineHeight(height)
[docs] def GetUserLineHeight(self):
"""
Returns the custom value for the L{UltimateListCtrl} item height, if previously set with
L{SetUserLineHeight}.
:note: This method can be used only with ``ULC_REPORT`` and ``ULC_USER_ROW_HEIGHT`` styles set.
"""
if self._mainWin:
return self._mainWin.GetUserLineHeight()
[docs] def GetColumn(self, col):
"""
Returns information about this column.
:param `col`: an integer specifying the column index.
"""
return self._mainWin.GetColumn(col)
[docs] def SetColumn(self, col, item):
"""
Sets information about this column.
:param `col`: an integer specifying the column index;
:param `item`: an instance of L{UltimateListItem}.
"""
self._mainWin.SetColumn(col, item)
return True
[docs] def GetColumnWidth(self, col):
"""
Returns the column width for the input column.
:param `col`: an integer specifying the column index.
"""
return self._mainWin.GetColumnWidth(col)
[docs] def SetColumnWidth(self, col, width):
"""
Sets the column width.
:param `width`: can be a width in pixels or ``wx.LIST_AUTOSIZE`` (-1) or
``wx.LIST_AUTOSIZE_USEHEADER`` (-2) or ``LIST_AUTOSIZE_FILL`` (-3).
``wx.LIST_AUTOSIZE`` will resize the column to the length of its longest
item. ``wx.LIST_AUTOSIZE_USEHEADER`` will resize the column to the
length of the header (Win32) or 80 pixels (other platforms).
``LIST_AUTOSIZE_FILL`` will resize the column fill the remaining width
of the window.
:note: In small or normal icon view, col must be -1, and the column width
is set for all columns.
"""
self._mainWin.SetColumnWidth(col, width)
return True
[docs] def GetCountPerPage(self):
"""
Returns the number of items that can fit vertically in the visible area
of the L{UltimateListCtrl} (list or report view) or the total number of
items in the list control (icon or small icon view).
"""
return self._mainWin.GetCountPerPage() # different from Windows ?
[docs] def GetItem(self, itemOrId, col=0):
"""
Returns the information about the input item.
:param `itemOrId`: an instance of L{UltimateListItem} or an integer specifying
the item index;
:param `col`: the column to which the item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItem(item, col)
[docs] def SetItem(self, info):
"""
Sets the information about the input item.
:param `info`: an instance of L{UltimateListItem}.
"""
self._mainWin.SetItem(info)
return True
[docs] def SetStringItem(self, index, col, label, imageIds=[], it_kind=0):
"""
Sets a string or image at the given location.
:param `index`: the item index;
:param `col`: the column to which the item belongs to;
:param `label`: the item text;
:param `imageIds`: a Python list containing the image indexes for the
images associated to this item;
:param `it_kind`: the item kind. May be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
info = UltimateListItem()
info._text = label
info._mask = ULC_MASK_TEXT
if it_kind:
info._mask |= ULC_MASK_KIND
info._kind = it_kind
info._itemId = index
info._col = col
for ids in to_list(imageIds):
if ids > -1:
info._image.append(ids)
if info._image:
info._mask |= ULC_MASK_IMAGE
self._mainWin.SetItem(info)
return index
[docs] def GetItemState(self, item, stateMask):
"""
Returns the item state flags for the input item.
:param `item`: the index of the item;
:param `stateMask`: the bitmask for the state flag.
:see: L{SetItemState} for a list of valid state flags.
"""
return self._mainWin.GetItemState(item, stateMask)
[docs] def SetItemState(self, item, state, stateMask):
"""
Sets the item state flags for the input item.
:param `item`: the index of the item; if defaulted to -1, the state flag
will be set for all the items;
:param `state`: any combination of the following bits:
============================ ========= ==============================
State Bits Hex Value Description
============================ ========= ==============================
``ULC_STATE_DONTCARE`` 0x0 Don't care what the state is
``ULC_STATE_DROPHILITED`` 0x1 The item is highlighted to receive a drop event
``ULC_STATE_FOCUSED`` 0x2 The item has the focus
``ULC_STATE_SELECTED`` 0x4 The item is selected
``ULC_STATE_CUT`` 0x8 The item is in the cut state
``ULC_STATE_DISABLED`` 0x10 The item is disabled
``ULC_STATE_FILTERED`` 0x20 The item has been filtered
``ULC_STATE_INUSE`` 0x40 The item is in use
``ULC_STATE_PICKED`` 0x80 The item has been picked
``ULC_STATE_SOURCE`` 0x100 The item is a drag and drop source
============================ ========= ==============================
:param `stateMask`: the bitmask for the state flag.
"""
self._mainWin.SetItemState(item, state, stateMask)
return True
[docs] def SetItemImage(self, item, image, selImage=-1):
"""
Sets a Python list of image indexes associated with the item.
:param `item`: an integer specifying the item index;
:param `image`: a Python list of indexes into the image list associated
with the L{UltimateListCtrl}. In report view, this only sets the images
for the first column;
:param `selImage`: not used at present.
"""
return self.SetItemColumnImage(item, 0, image)
[docs] def SetItemColumnImage(self, item, column, image):
"""
Sets a Python list of image indexes associated with the item in the input
column.
:param `item`: an integer specifying the item index;
:param `column`: the column to which the item belongs to;
:param `image`: a Python list of indexes into the image list associated
with the L{UltimateListCtrl}.
"""
info = UltimateListItem()
info._image = to_list(image)
info._mask = ULC_MASK_IMAGE
info._itemId = item
info._col = column
self._mainWin.SetItem(info)
return True
[docs] def GetItemText(self, item):
"""
Returns the item text.
:param `item`: an instance of L{UltimateListItem} or an integer specifying
the item index.
"""
return self._mainWin.GetItemText(item)
[docs] def SetItemText(self, item, text):
"""
Sets the item text.
:param `item`: an instance of L{UltimateListItem} or an integer specifying
the item index;
:param `text`: the new item text.
"""
self._mainWin.SetItemText(item, text)
[docs] def GetItemData(self, item):
"""
Gets the application-defined data associated with this item.
:param `item`: an integer specifying the item index.
"""
info = UltimateListItem()
info._mask = ULC_MASK_DATA
info._itemId = item
self._mainWin.GetItem(info)
return info._data
[docs] def SetItemData(self, item, data):
"""
Sets the application-defined data associated with this item.
:param `item`: an integer specifying the item index;
:param `data`: the data to be associated with the input item.
:note: This function cannot be used to associate pointers with
the control items, use L{SetItemPyData} instead.
"""
info = UltimateListItem()
info._mask = ULC_MASK_DATA
info._itemId = item
info._data = data
self._mainWin.SetItem(info)
return True
[docs] def GetItemPyData(self, item):
"""
Returns the data for the item, which can be any Python object.
:param `item`: an integer specifying the item index.
:note: Please note that Python data is associated with the item and not
with subitems.
"""
info = UltimateListItem()
info._mask = ULC_MASK_PYDATA
info._itemId = item
self._mainWin.GetItem(info)
return info._pyData
[docs] def SetItemPyData(self, item, pyData):
"""
Sets the data for the item, which can be any Python object.
:param `item`: an integer specifying the item index;
:param `pyData`: any Python object.
:note: Please note that Python data is associated with the item and not
with subitems.
"""
info = UltimateListItem()
info._mask = ULC_MASK_PYDATA
info._itemId = item
info._pyData = pyData
self._mainWin.SetItem(info)
return True
SetPyData = SetItemPyData
GetPyData = GetItemPyData
[docs] def GetViewRect(self):
"""
Returns the rectangle taken by all items in the control. In other words,
if the controls client size were equal to the size of this rectangle, no
scrollbars would be needed and no free space would be left.
:note: This function only works in the icon and small icon views, not in
list or report views.
"""
return self._mainWin.GetViewRect()
[docs] def GetItemRect(self, item, code=ULC_RECT_BOUNDS):
"""
Returns the rectangle representing the item's size and position, in physical
coordinates.
:param `item`: the row in which the item lives;
:param `code`: one of ``ULC_RECT_BOUNDS``, ``ULC_RECT_ICON``, ``ULC_RECT_LABEL``.
"""
return self.GetSubItemRect(item, ULC_GETSUBITEMRECT_WHOLEITEM, code)
[docs] def GetSubItemRect(self, item, subItem, code):
"""
Returns the rectangle representing the size and position, in physical coordinates,
of the given subitem, i.e. the part of the row `item` in the column `subItem`.
:param `item`: the row in which the item lives;
:param `subItem`: the column in which the item lives. If set equal to the special
value ``ULC_GETSUBITEMRECT_WHOLEITEM`` the return value is the same as for
L{GetItemRect};
:param `code`: one of ``ULC_RECT_BOUNDS``, ``ULC_RECT_ICON``, ``ULC_RECT_LABEL``.
:note: This method is only meaningful when the L{UltimateListCtrl} is in the
report mode.
"""
rect = self._mainWin.GetSubItemRect(item, subItem)
if self._mainWin.HasHeader():
rect.y += self._headerHeight + 1
return rect
[docs] def GetItemPosition(self, item):
"""
Returns the position of the item, in icon or small icon view.
:param `item`: the row in which the item lives.
"""
return self._mainWin.GetItemPosition(item)
[docs] def SetItemPosition(self, item, pos):
"""
Sets the position of the item, in icon or small icon view.
:param `item`: the row in which the item lives;
:param `pos`: the item position.
:note: This method is currently unimplemented and does nothing.
"""
return False
[docs] def GetItemCount(self):
""" Returns the number of items in the L{UltimateListCtrl}. """
return self._mainWin.GetItemCount()
[docs] def GetColumnCount(self):
""" Returns the total number of columns in the L{UltimateListCtrl}. """
return self._mainWin.GetColumnCount()
[docs] def SetItemSpacing(self, spacing, isSmall=False):
"""
Sets the spacing between item texts and icons.
:param `spacing`: the spacing between item texts and icons, in pixels;
:param `isSmall`: ``True`` if using a ``wx.IMAGE_LIST_SMALL`` image list,
``False`` if using a ``wx.IMAGE_LIST_NORMAL`` image list.
"""
self._mainWin.SetItemSpacing(spacing, isSmall)
[docs] def GetItemSpacing(self, isSmall=False):
"""
Returns the spacing between item texts and icons, in pixels.
:param `isSmall`: ``True`` if using a ``wx.IMAGE_LIST_SMALL`` image list,
``False`` if using a ``wx.IMAGE_LIST_NORMAL`` image list.
"""
return self._mainWin.GetItemSpacing(isSmall)
[docs] def SetItemTextColour(self, item, col):
"""
Sets the item text colour.
:param `item`: the index of the item;
:param `col`: a valid `wx.Colour` object.
"""
info = UltimateListItem()
info._itemId = item
info.SetTextColour(col)
self._mainWin.SetItem(info)
[docs] def GetItemTextColour(self, item):
"""
Returns the item text colour.
:param `item`: the index of the item.
"""
info = UltimateListItem()
info._itemId = item
info = self._mainWin.GetItem(info)
return info.GetTextColour()
[docs] def SetItemBackgroundColour(self, item, col):
"""
Sets the item background colour.
:param `item`: the index of the item;
:param `col`: a valid `wx.Colour` object.
"""
info = UltimateListItem()
info._itemId = item
info.SetBackgroundColour(col)
self._mainWin.SetItem(info)
[docs] def GetItemBackgroundColour(self, item):
"""
Returns the item background colour.
:param `item`: the index of the item.
"""
info = UltimateListItem()
info._itemId = item
info = self._mainWin.GetItem(info)
return info.GetBackgroundColour()
[docs] def SetItemFont(self, item, f):
"""
Sets the item font.
:param `item`: the index of the item;
:param `f`: a valid `wx.Font` object.
"""
info = UltimateListItem()
info._itemId = item
info.SetFont(f)
self._mainWin.SetItem(info)
[docs] def GetItemFont(self, item):
"""
Returns the item font.
:param `item`: the index of the item.
"""
info = UltimateListItem()
info._itemId = item
info = self._mainWin.GetItem(info)
return info.GetFont()
[docs] def GetSelectedItemCount(self):
""" Returns the number of selected items in L{UltimateListCtrl}. """
return self._mainWin.GetSelectedItemCount()
[docs] def GetTextColour(self):
""" Returns the L{UltimateListCtrl} foreground colour. """
return self.GetForegroundColour()
[docs] def SetTextColour(self, col):
"""
Sets the L{UltimateListCtrl} foreground colour.
:param `col`: a valid `wx.Colour` object.
"""
self.SetForegroundColour(col)
[docs] def GetTopItem(self):
""" Gets the index of the topmost visible item when in list or report view. """
top, dummy = self._mainWin.GetVisibleLinesRange()
return top
[docs] def GetNextItem(self, item, geometry=ULC_NEXT_ALL, state=ULC_STATE_DONTCARE):
"""
Searches for an item with the given `geometry` or `state`, starting from `item`
but excluding the `item` itself.
:param `item`: the item at which starting the search. If set to -1, the first
item that matches the specified flags will be returned.
:param `geometry`: can be one of:
=================== ========= =================================
Geometry Flag Hex Value Description
=================== ========= =================================
``ULC_NEXT_ABOVE`` 0x0 Searches for an item above the specified item
``ULC_NEXT_ALL`` 0x1 Searches for subsequent item by index
``ULC_NEXT_BELOW`` 0x2 Searches for an item below the specified item
``ULC_NEXT_LEFT`` 0x3 Searches for an item to the left of the specified item
``ULC_NEXT_RIGHT`` 0x4 Searches for an item to the right of the specified item
=================== ========= =================================
:param `state`: any combination of the following bits:
============================ ========= ==============================
State Bits Hex Value Description
============================ ========= ==============================
``ULC_STATE_DONTCARE`` 0x0 Don't care what the state is
``ULC_STATE_DROPHILITED`` 0x1 The item is highlighted to receive a drop event
``ULC_STATE_FOCUSED`` 0x2 The item has the focus
``ULC_STATE_SELECTED`` 0x4 The item is selected
``ULC_STATE_CUT`` 0x8 The item is in the cut state
``ULC_STATE_DISABLED`` 0x10 The item is disabled
``ULC_STATE_FILTERED`` 0x20 The item has been filtered
``ULC_STATE_INUSE`` 0x40 The item is in use
``ULC_STATE_PICKED`` 0x80 The item has been picked
``ULC_STATE_SOURCE`` 0x100 The item is a drag and drop source
============================ ========= ==============================
:return: The first item with given `state` following `item` or -1 if no such item found.
:note: This function may be used to find all selected items in the
control like this::
item = -1
while 1:
item = listctrl.GetNextItem(item, ULC_NEXT_ALL, ULC_STATE_SELECTED)
if item == -1:
break
# This item is selected - do whatever is needed with it
wx.LogMessage("Item %ld is selected."%item)
"""
return self._mainWin.GetNextItem(item, geometry, state)
[docs] def GetImageList(self, which):
"""
Returns the image list associated with the control.
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
:note:
As L{UltimateListCtrl} allows you to use a standard `wx.ImageList` or
L{PyImageList}, the returned object depends on which kind of image list you
chose.
"""
if which == wx.IMAGE_LIST_NORMAL:
return self._imageListNormal
elif which == wx.IMAGE_LIST_SMALL:
return self._imageListSmall
elif which == wx.IMAGE_LIST_STATE:
return self._imageListState
return None
[docs] def SetImageList(self, imageList, which):
"""
Sets the image list associated with the control.
:param `imageList`: an instance of `wx.ImageList` or an instance of L{PyImageList};
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
:note: Using L{PyImageList} enables you to have images of different size inside the
image list. In your derived class, instead of doing this::
imageList = wx.ImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
You should do this::
imageList = PyImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
"""
if which == wx.IMAGE_LIST_NORMAL:
self._imageListNormal = imageList
elif which == wx.IMAGE_LIST_SMALL:
self._imageListSmall = imageList
elif which == wx.IMAGE_LIST_STATE:
self._imageListState = imageList
self._mainWin.SetImageList(imageList, which)
[docs] def AssignImageList(self, imageList, which):
"""
Assigns the image list associated with the control.
:param `imageList`: an instance of `wx.ImageList` or an instance of L{PyImageList};
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
:note: Using L{PyImageList} enables you to have images of different size inside the
image list. In your derived class, instead of doing this::
imageList = wx.ImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
You should do this::
imageList = PyImageList(16, 16)
imageList.Add(someBitmap)
self.SetImageList(imageList, wx.IMAGE_LIST_SMALL)
"""
self.SetImageList(imageList, which)
[docs] def Arrange(self, flag):
"""
Arranges the items in icon or small icon view.
:param `flag`: one of the following bits:
========================== ========= ===============================
Alignment Flag Hex Value Description
========================== ========= ===============================
``ULC_ALIGN_DEFAULT`` 0x0 Default alignment
``ULC_ALIGN_SNAP_TO_GRID`` 0x3 Snap to grid
========================== ========= ===============================
:note: This method is currently unimplemented and does nothing.
"""
return 0
[docs] def DeleteItem(self, item):
"""
Deletes the specified item.
:param `item`: the index of the item to delete.
:note: This function sends the ``EVT_LIST_DELETE_ITEM`` event for the item
being deleted.
"""
self._mainWin.DeleteItem(item)
return True
[docs] def DeleteAllItems(self):
"""
Deletes all items in the L{UltimateListCtrl}.
:note: This function does not send the ``EVT_LIST_DELETE_ITEM`` event because
deleting many items from the control would be too slow then (unlike L{DeleteItem}).
"""
self._mainWin.DeleteAllItems()
return True
[docs] def DeleteAllColumns(self):
""" Deletes all the column in L{UltimateListCtrl}. """
count = len(self._mainWin._columns)
for n in xrange(count):
self.DeleteColumn(0)
return True
[docs] def ClearAll(self):
""" Deletes everything in L{UltimateListCtrl}. """
self._mainWin.DeleteEverything()
[docs] def DeleteColumn(self, col):
"""
Deletes the specified column.
:param `col`: the index of the column to delete.
"""
self._mainWin.DeleteColumn(col)
return True
[docs] def EditLabel(self, item):
"""
Starts editing an item label.
:param `item`: the index of the item to edit.
"""
self._mainWin.EditLabel(item)
[docs] def EnsureVisible(self, item):
"""
Ensures this item is visible.
:param `index`: the index of the item to scroll into view.
"""
self._mainWin.EnsureVisible(item)
return True
[docs] def FindItem(self, start, str, partial=False):
"""
Find an item whose label matches this string.
:param `start`: the starting point of the input `string` or the beginning
if `start` is -1;
:param `string`: the string to look for matches;
:param `partial`: if ``True`` then this method will look for items which
begin with `string`.
:note: The string comparison is case insensitive.
"""
return self._mainWin.FindItem(start, str, partial)
[docs] def FindItemData(self, start, data):
"""
Find an item whose data matches this data.
:param `start`: the starting point of the input `data` or the beginning
if `start` is -1;
:param `data`: the data to look for matches.
"""
return self._mainWin.FindItemData(start, data)
[docs] def FindItemAtPos(self, start, pt):
"""
Find an item nearest this position.
:param `pt`: an instance of `wx.Point`.
"""
return self._mainWin.FindItemAtPos(pt)
[docs] def HitTest(self, pointOrTuple):
"""
HitTest method for a L{UltimateListCtrl}.
:param `pointOrTuple`: an instance of `wx.Point` or a tuple representing
the mouse `x`, `y` position.
:see: L{UltimateListMainWindow.HitTestLine} for a list of return flags.
"""
if isinstance(pointOrTuple, wx.Point):
x, y = pointOrTuple.x, pointOrTuple.y
else:
x, y = pointOrTuple
return self._mainWin.HitTest(x, y)
[docs] def InsertItem(self, info):
"""
Inserts an item into L{UltimateListCtrl}.
:param `info`: an instance of L{UltimateListItem}.
"""
self._mainWin.InsertItem(info)
return info._itemId
[docs] def InsertStringItem(self, index, label, it_kind=0):
"""
Inserts a string item at the given location.
:param `index`: the index at which we wish to insert the item;
:param `label`: the item text;
:param `it_kind`: the item kind.
:see: L{SetStringItem} for a list of valid item kinds.
"""
info = UltimateListItem()
info._text = label
info._mask = ULC_MASK_TEXT
if it_kind:
info._mask |= ULC_MASK_KIND
info._kind = it_kind
info._itemId = index
return self.InsertItem(info)
[docs] def InsertImageItem(self, index, imageIds, it_kind=0):
"""
Inserts an image item at the given location.
:param `index`: the index at which we wish to insert the item;
:param `imageIds`: a Python list containing the image indexes for the
images associated to this item;
:param `it_kind`: the item kind.
:see: L{SetStringItem} for a list of valid item kinds.
"""
info = UltimateListItem()
info._mask = ULC_MASK_IMAGE
if it_kind:
info._mask |= ULC_MASK_KIND
info._kind = it_kind
info._image = to_list(imageIds)
info._itemId = index
return self.InsertItem(info)
[docs] def InsertImageStringItem(self, index, label, imageIds, it_kind=0):
"""
Inserts an image+string item at the given location.
:param `index`: the index at which we wish to insert the item;
:param `label`: the item text;
:param `imageIds`: a Python list containing the image indexes for the
images associated to this item;
:param `it_kind`: the item kind.
:see: L{SetStringItem} for a list of valid item kinds.
"""
info = UltimateListItem()
info._text = label
info._image = to_list(imageIds)
info._mask = ULC_MASK_TEXT | ULC_MASK_IMAGE
if it_kind:
info._mask |= ULC_MASK_KIND
info._kind = it_kind
info._itemId = index
return self.InsertItem(info)
[docs] def InsertColumnInfo(self, col, item):
"""
Inserts a column into L{UltimateListCtrl}.
:param `col`: the column index at which we wish to insert a column;
:param `item`: an instance of L{UltimateListItem}.
"""
if not self._mainWin.InReportView() and not self.HasAGWFlag(ULC_HEADER_IN_ALL_VIEWS) and \
not self._mainWin.InTileView():
raise Exception("Can't add column in non report/tile modes or without the ULC_HEADER_IN_ALL_VIEWS style set")
self._mainWin.InsertColumn(col, item)
if self._headerWin:
self._headerWin.Refresh()
return 0
[docs] def InsertColumn(self, col, heading, format=ULC_FORMAT_LEFT, width=-1):
"""
Inserts a column into L{UltimateListCtrl}.
:param `col`: the column index at which we wish to insert a column;
:param `heading`: the header text;
:param `format`: the column alignment flag. This can be one of the following
bits:
============================ ========= ==============================
Alignment Bits Hex Value Description
============================ ========= ==============================
``ULC_FORMAT_LEFT`` 0x0 The item is left-aligned
``ULC_FORMAT_RIGHT`` 0x1 The item is right-aligned
``ULC_FORMAT_CENTRE`` 0x2 The item is centre-aligned
``ULC_FORMAT_CENTER`` 0x2 The item is center-aligned
============================ ========= ==============================
:param `width`: can be a width in pixels or ``wx.LIST_AUTOSIZE`` (-1) or
``wx.LIST_AUTOSIZE_USEHEADER`` (-2) or ``LIST_AUTOSIZE_FILL`` (-3).
``wx.LIST_AUTOSIZE`` will resize the column to the length of its longest
item. ``wx.LIST_AUTOSIZE_USEHEADER`` will resize the column to the
length of the header (Win32) or 80 pixels (other platforms).
``LIST_AUTOSIZE_FILL`` will resize the column fill the remaining width
of the window.
"""
item = UltimateListItem()
item._mask = ULC_MASK_TEXT | ULC_MASK_FORMAT | ULC_MASK_FONT
item._text = heading
if width >= -2:
item._mask |= ULC_MASK_WIDTH
item._width = width
item._format = format
return self.InsertColumnInfo(col, item)
[docs] def IsColumnShown(self, column):
"""
Returns ``True`` if the input column is shown, ``False`` if it is hidden.
:param `column`: an integer specifying the column index.
"""
if self._headerWin:
return self._mainWin.IsColumnShown(column)
raise Exception("Showing/hiding columns works only with the header shown")
[docs] def SetColumnShown(self, column, shown=True):
"""
Sets the specified column as shown or hidden.
:param `column`: an integer specifying the column index;
:param `shown`: ``True`` to show the column, ``False`` to hide it.
"""
col = self.GetColumn(column)
col._mask |= ULC_MASK_SHOWN
col.SetShown(shown)
self._mainWin.SetColumn(column, col)
self.Update()
[docs] def SortItems(self, func=None):
"""
Call this function to sort the items in the L{UltimateListCtrl}. Sorting is done
using the specified function `func`. This function must have the
following prototype::
def OnCompareItems(self, line1, line2):
DoSomething(line1, line2)
# function code
It is called each time when the two items must be compared and should return 0
if the items are equal, negative value if the first item is less than the second
one and positive value if the first one is greater than the second one.
:param `func`: the method to use to sort the items. The default is to use the
L{UltimateListMainWindow.OnCompareItems} method.
"""
self._mainWin.SortItems(func)
wx.CallAfter(self.Refresh)
return True
# ----------------------------------------------------------------------------
# event handlers
# ----------------------------------------------------------------------------
[docs] def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` event for L{UltimateListCtrl}.
:param `event`: a `wx.SizeEvent` event to be processed.
"""
if not self.IsShownOnScreen():
# We don't have the proper column sizes until we are visible so
# use CallAfter to resize the columns on the first display
if self._mainWin:
wx.CallAfter(self._mainWin.ResizeColumns)
if not self._mainWin:
return
# We need to override OnSize so that our scrolled
# window a) does call Layout() to use sizers for
# positioning the controls but b) does not query
# the sizer for their size and use that for setting
# the scrollable area as set that ourselves by
# calling SetScrollbar() further down.
self.DoLayout()
[docs] def OnSetFocus(self, event):
"""
Handles the ``wx.EVT_SET_FOCUS`` event for L{UltimateListCtrl}.
:param `event`: a `wx.FocusEvent` event to be processed.
"""
if self._mainWin:
self._mainWin.SetFocusIgnoringChildren()
self._mainWin.Update()
event.Skip()
[docs] def OnInternalIdle(self):
"""
This method is normally only used internally, but sometimes an application
may need it to implement functionality that should not be disabled by an
application defining an `OnIdle` handler in a derived class.
This method may be used to do delayed painting, for example, and most
implementations call `wx.Window.UpdateWindowUI` in order to send update events
to the window in idle time.
"""
wx.PyControl.OnInternalIdle(self)
# do it only if needed
if self._mainWin and self._mainWin._dirty:
self._mainWin._shortItems = []
self._mainWin.RecalculatePositions()
# ----------------------------------------------------------------------------
# font/colours
# ----------------------------------------------------------------------------
[docs] def SetBackgroundColour(self, colour):
"""
Changes the background colour of L{UltimateListCtrl}.
:param `colour`: the colour to be used as the background colour, pass
`wx.NullColour` to reset to the default colour.
:note: The background colour is usually painted by the default `wx.EraseEvent`
event handler function under Windows and automatically under GTK.
:note: Setting the background colour does not cause an immediate refresh, so
you may wish to call `wx.Window.ClearBackground` or `wx.Window.Refresh` after
calling this function.
:note: Overridden from `wx.PyControl`.
"""
if self._mainWin:
self._mainWin.SetBackgroundColour(colour)
self._mainWin._dirty = True
return True
[docs] def SetForegroundColour(self, colour):
"""
Changes the foreground colour of L{UltimateListCtrl}.
:param `colour`: the colour to be used as the foreground colour, pass
`wx.NullColour` to reset to the default colour.
:note: Overridden from `wx.PyControl`.
"""
if not wx.PyControl.SetForegroundColour(self, colour):
return False
if self._mainWin:
self._mainWin.SetForegroundColour(colour)
self._mainWin._dirty = True
if self._headerWin:
self._headerWin.SetForegroundColour(colour)
return True
[docs] def SetFont(self, font):
"""
Sets the L{UltimateListCtrl} font.
:param `font`: a valid `wx.Font` instance.
:note: Overridden from `wx.PyControl`.
"""
if not wx.PyControl.SetFont(self, font):
return False
if self._mainWin:
self._mainWin.SetFont(font)
self._mainWin._dirty = True
if self._headerWin:
self._headerWin.SetFont(font)
self.Refresh()
return True
[docs] def GetClassDefaultAttributes(self, variant):
"""
Returns the default font and colours which are used by the control. This is
useful if you want to use the same font or colour in your own control as in
a standard control -- which is a much better idea than hard coding specific
colours or fonts which might look completely out of place on the users system,
especially if it uses themes.
This static method is "overridden'' in many derived classes and so calling,
for example, `wx.Button.GetClassDefaultAttributes()` will typically return the
values appropriate for a button which will be normally different from those
returned by, say, `wx.ListCtrl.GetClassDefaultAttributes()`.
:note: The `wx.VisualAttributes` structure has at least the fields `font`,
`colFg` and `colBg`. All of them may be invalid if it was not possible to
determine the default control appearance or, especially for the background
colour, if the field doesn't make sense as is the case for `colBg` for the
controls with themed background.
:note: Overridden from `wx.PyControl`.
"""
attr = wx.VisualAttributes()
attr.colFg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOXTEXT)
attr.colBg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX)
attr.font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
return attr
[docs] def SetDropTarget(self, dropTarget):
"""
Associates a drop target with this window.
If the window already has a drop target, it is deleted.
:param `dropTarget`: an instance of `wx.DropTarget`.
:note: Overridden from `wx.PyControl`.
"""
self._mainWin.SetDropTarget(dropTarget)
[docs] def GetDropTarget(self):
"""
Returns the associated drop target, which may be ``None``.
:note: Overridden from `wx.PyControl`.
"""
return self._mainWin.GetDropTarget()
[docs] def SetCursor(self, cursor):
"""
Sets the window's cursor.
:param `cursor`: specifies the cursor that the window should normally display.
The `cursor` may be `wx.NullCursor` in which case the window cursor will be
reset back to default.
:note: The window cursor also sets it for the children of the window implicitly.
:note: Overridden from `wx.PyControl`.
"""
return (self._mainWin and [self._mainWin.SetCursor(cursor)] or [False])[0]
[docs] def GetBackgroundColour(self):
"""
Returns the background colour of the window.
:note: Overridden from `wx.PyControl`.
"""
return (self._mainWin and [self._mainWin.GetBackgroundColour()] or [wx.NullColour])[0]
[docs] def GetForegroundColour(self):
"""
Returns the foreground colour of the window.
:note: Overridden from `wx.PyControl`.
"""
return (self._mainWin and [self._mainWin.GetForegroundColour()] or [wx.NullColour])[0]
[docs] def ClientToScreen(self, pointOrTuple):
"""
Converts to screen coordinates from coordinates relative to this window.
:param `pointOrTuple`: an instance of `wx.Point` or a tuple representing the
`x`, `y` coordinates for this point.
:return: the coordinates relative to the screen.
:note: Overridden from `wx.PyControl`.
"""
return self._mainWin.ClientToScreen(*pointOrTuple)
[docs] def ClientToScreenXY(self, x, y):
"""
Converts to screen coordinates from coordinates relative to this window.
:param `x`: an integer specifying the `x` client coordinate;
:param `y`: an integer specifying the `y` client coordinate.
:return: the coordinates relative to the screen.
:note: Overridden from `wx.PyControl`.
"""
return self._mainWin.ClientToScreen(x, y)
[docs] def ScreenToClient(self, pointOrTuple):
"""
Converts from screen to client window coordinates.
:param `pointOrTuple`: an instance of `wx.Point` or a tuple representing the
`x`, `y` coordinates for this point.
:return: the coordinates relative to this window.
:note: Overridden from `wx.PyControl`.
"""
return self._mainWin.ScreenToClient(*pointOrTuple)
[docs] def ScreenToClientXY(self, x, y):
"""
Converts from screen to client window coordinates.
:param `x`: an integer specifying the `x` screen coordinate;
:param `y`: an integer specifying the `y` screen coordinate.
:return: the coordinates relative to this window.
:note: Overridden from `wx.PyControl`.
"""
return self._mainWin.ScreenToClient(x, y)
[docs] def SetFocus(self):
""" This sets the window to receive keyboard input. """
# The test in window.cpp fails as we are a composite
# window, so it checks against "this", but not self._mainWin.
if wx.Window.FindFocus() != self:
self._mainWin.SetFocusIgnoringChildren()
[docs] def DoGetBestSize(self):
"""
Gets the size which best suits the window: for a control, it would be the
minimal size which doesn't truncate the control, for a panel - the same size
as it would have after a call to `Fit()`.
"""
# Something is better than nothing...
# 100x80 is what the MSW version will get from the default
# wx.Control.DoGetBestSize
return wx.Size(100, 80)
# ----------------------------------------------------------------------------
# virtual list control support
# ----------------------------------------------------------------------------
[docs] def OnGetItemText(self, item, col):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return the string containing the text of
the given column for the specified item.
:param `item`: an integer specifying the item index;
:param `col`: the column index to which the item belongs to.
"""
# this is a pure virtual function, in fact - which is not really pure
# because the controls which are not virtual don't need to implement it
raise Exception("UltimateListCtrl.OnGetItemText not supposed to be called")
[docs] def OnGetItemTextColour(self, item, col):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return a `wx.Colour` object or ``None`` for
the default color.
:param `item`: an integer specifying the item index;
:param `col`: the column index to which the item belongs to.
"""
# this is a pure virtual function, in fact - which is not really pure
# because the controls which are not virtual don't need to implement it
raise Exception("UltimateListCtrl.OnGetItemTextColour not supposed to be called")
[docs] def OnGetItemImage(self, item):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style having an image list (if the control doesn't have an
image list, it is not necessary to overload it). It should return a Python
list of indexes representing the images associated to the input item or an
empty list for no images.
:param `item`: an integer specifying the item index;
:note: In a control with ``ULC_REPORT`` style, L{OnGetItemImage} only gets called
for the first column of each line.
:note: The base class version always returns an empty Python list.
"""
if self.GetImageList(wx.IMAGE_LIST_SMALL):
raise Exception("List control has an image list, OnGetItemImage should be overridden.")
return []
[docs] def OnGetItemColumnImage(self, item, column=0):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` and ``ULC_REPORT`` style. It should return a Python list of
indexes representing the images associated to the input item or an empty list
for no images.
:param `item`: an integer specifying the item index.
:note: The base class version always returns an empty Python list.
"""
if column == 0:
return self.OnGetItemImage(item)
return []
[docs] def OnGetItemAttr(self, item):
"""
This function may be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return the attribute for the specified
item or ``None`` to use the default appearance parameters.
:param `item`: an integer specifying the item index.
:note:
L{UltimateListCtrl} will not delete the pointer or keep a reference of it.
You can return the same L{UltimateListItemAttr} pointer for every
L{OnGetItemAttr} call.
:note: The base class version always returns ``None``.
"""
if item < 0 or item > self.GetItemCount():
raise Exception("Invalid item index in OnGetItemAttr()")
# no attributes by default
return None
[docs] def OnGetItemCheck(self, item):
"""
This function may be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return whether a checkbox-like item or
a radiobutton-like item is checked or unchecked.
:param `item`: an integer specifying the item index.
:note: The base class version always returns an empty list.
"""
return []
[docs] def OnGetItemColumnCheck(self, item, column=0):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` and ``ULC_REPORT`` style. It should return whether a
checkbox-like item or a radiobutton-like item in the column header is checked
or unchecked.
:param `item`: an integer specifying the item index.
:note: The base class version always returns an empty Python list.
"""
if column == 0:
return self.OnGetItemCheck(item)
return []
[docs] def OnGetItemKind(self, item):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return the item kind for the input item.
:param `item`: an integer specifying the item index.
:note: The base class version always returns 0 (a standard item).
:see: L{SetItemKind} for a list of valid item kinds.
"""
return 0
[docs] def OnGetItemColumnKind(self, item, column=0):
"""
This function **must** be overloaded in the derived class for a control with
``ULC_VIRTUAL`` style. It should return the item kind for the input item in
the header window.
:param `item`: an integer specifying the item index;
:param `column`: the column index.
:note: The base class version always returns 0 (a standard item).
:see: L{SetItemKind} for a list of valid item kinds.
"""
if column == 0:
return self.OnGetItemKind(item)
return 0
[docs] def SetItemCount(self, count):
"""
Sets the total number of items we handle.
:param `count`: the total number of items we handle.
"""
if not self._mainWin.IsVirtual():
raise Exception("This is for virtual controls only")
self._mainWin.SetItemCount(count)
[docs] def RefreshItem(self, item):
"""
Redraws the given item.
:param `item`: an integer specifying the item index;
:note: This is only useful for the virtual list controls as without calling
this function the displayed value of the item doesn't change even when the
underlying data does change.
"""
self._mainWin.RefreshLine(item)
[docs] def RefreshItems(self, itemFrom, itemTo):
"""
Redraws the items between `itemFrom` and `itemTo`.
The starting item must be less than or equal to the ending one.
Just as L{RefreshItem} this is only useful for virtual list controls
:param `itemFrom`: the first index of the refresh range;
:param `itemTo`: the last index of the refresh range.
"""
self._mainWin.RefreshLines(itemFrom, itemTo)
#
# Generic UltimateListCtrl is more or less a container for two other
# windows which drawings are done upon. These are namely
# 'self._headerWin' and 'self._mainWin'.
# Here we override 'virtual wxWindow::Refresh()' to mimic the
# behaviour UltimateListCtrl has under wxMSW.
#
[docs] def Refresh(self, eraseBackground=True, rect=None):
"""
Causes this window, and all of its children recursively (except under wxGTK1
where this is not implemented), to be repainted.
:param `eraseBackground`: If ``True``, the background will be erased;
:param `rect`: If not ``None``, only the given rectangle will be treated as damaged.
:note: Note that repainting doesn't happen immediately but only during the next
event loop iteration, if you need to update the window immediately you should
use L{Update} instead.
:note: Overridden from `wx.PyControl`.
"""
if not rect:
# The easy case, no rectangle specified.
if self._headerWin:
self._headerWin.Refresh(eraseBackground)
if self._mainWin:
self._mainWin.Refresh(eraseBackground)
else:
# Refresh the header window
if self._headerWin:
rectHeader = self._headerWin.GetRect()
rectHeader.Intersect(rect)
if rectHeader.GetWidth() and rectHeader.GetHeight():
x, y = self._headerWin.GetPosition()
rectHeader.OffsetXY(-x, -y)
self._headerWin.Refresh(eraseBackground, rectHeader)
# Refresh the main window
if self._mainWin:
rectMain = self._mainWin.GetRect()
rectMain.Intersect(rect)
if rectMain.GetWidth() and rectMain.GetHeight():
x, y = self._mainWin.GetPosition()
rectMain.OffsetXY(-x, -y)
self._mainWin.Refresh(eraseBackground, rectMain)
[docs] def Update(self):
"""
Calling this method immediately repaints the invalidated area of the window
and all of its children recursively while this would usually only happen when
the flow of control returns to the event loop.
:note: This function doesn't invalidate any area of the window so nothing
happens if nothing has been invalidated (i.e. marked as requiring a redraw).
Use L{Refresh} first if you want to immediately redraw the window unconditionally.
:note: Overridden from `wx.PyControl`.
"""
self._mainWin.ResetVisibleLinesRange(True)
wx.PyControl.Update(self)
[docs] def GetEditControl(self):
"""
Returns a pointer to the edit L{UltimateListTextCtrl} if the item is being edited or
``None`` otherwise (it is assumed that no more than one item may be edited
simultaneously).
"""
retval = None
if self._mainWin:
retval = self._mainWin.GetEditControl()
return retval
[docs] def Select(self, idx, on=True):
"""
Selects/deselects an item.
:param `idx`: the index of the item to select;
:param `on`: ``True`` to select the item, ``False`` to deselect it.
"""
item = CreateListItem(idx, 0)
item = self._mainWin.GetItem(item, 0)
if not item.IsEnabled():
return
if on:
state = ULC_STATE_SELECTED
else:
state = 0
self.SetItemState(idx, state, ULC_STATE_SELECTED)
[docs] def Focus(self, idx):
"""
Focus and show the given item.
:param `idx`: the index of the item to be focused.
"""
self.SetItemState(idx, ULC_STATE_FOCUSED, ULC_STATE_FOCUSED)
self.EnsureVisible(idx)
[docs] def GetFocusedItem(self):
""" Returns the currently focused item or -1 if none is focused. """
return self.GetNextItem(-1, ULC_NEXT_ALL, ULC_STATE_FOCUSED)
[docs] def GetFirstSelected(self):
""" Return first selected item, or -1 when none is selected. """
return self.GetNextSelected(-1)
[docs] def GetNextSelected(self, item):
"""
Returns subsequent selected items, or -1 when no more are selected.
:param `item`: the index of the item.
"""
return self.GetNextItem(item, ULC_NEXT_ALL, ULC_STATE_SELECTED)
[docs] def IsSelected(self, idx):
"""
Returns ``True`` if the item is selected.
:param `idx`: the index of the item to check for selection.
"""
return (self.GetItemState(idx, ULC_STATE_SELECTED) & ULC_STATE_SELECTED) != 0
[docs] def IsItemChecked(self, itemOrId, col=0):
"""
Returns whether an item is checked or not.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.IsItemChecked(item)
[docs] def IsItemEnabled(self, itemOrId, col=0):
"""
Returns whether an item is enabled or not.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.IsItemEnabled(item)
[docs] def GetItemKind(self, itemOrId, col=0):
"""
Returns the item kind.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
:see: L{SetItemKind} for a list of valid item kinds.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemKind(item)
[docs] def SetItemKind(self, itemOrId, col=0, kind=0):
"""
Sets the item kind.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `kind`: may be one of the following integers:
=============== ==========================
Item Kind Description
=============== ==========================
0 A normal item
1 A checkbox-like item
2 A radiobutton-type item
=============== ==========================
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemKind(item, kind)
[docs] def EnableItem(self, itemOrId, col=0, enable=True):
"""
Enables/disables an item.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `enable`: ``True`` to enable the item, ``False`` otherwise.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.EnableItem(item, enable)
[docs] def IsItemHyperText(self, itemOrId, col=0):
"""
Returns whether an item is hypertext or not.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.IsItemHyperText(item)
[docs] def SetItemHyperText(self, itemOrId, col=0, hyper=True):
"""
Sets whether the item is hypertext or not.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `hyper`: ``True`` to have an item with hypertext behaviour, ``False`` otherwise.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemHyperText(item, hyper)
[docs] def SetColumnImage(self, col, image):
"""
Sets one or more images to the specified column.
:param `col`: the column index;
:param `image`: a Python list containing the image indexes for the
images associated to this column item.
"""
item = self.GetColumn(col)
# preserve all other attributes too
item.SetMask(ULC_MASK_STATE |
ULC_MASK_TEXT |
ULC_MASK_IMAGE |
ULC_MASK_DATA |
ULC_SET_ITEM |
ULC_MASK_WIDTH |
ULC_MASK_FORMAT |
ULC_MASK_FONTCOLOUR |
ULC_MASK_FONT |
ULC_MASK_BACKCOLOUR |
ULC_MASK_KIND |
ULC_MASK_CHECK
)
item.SetImage(image)
self.SetColumn(col, item)
[docs] def ClearColumnImage(self, col):
"""
Clears all the images in the specified column.
:param `col`: the column index;
"""
self.SetColumnImage(col, -1)
[docs] def Append(self, entry):
"""
Append an item to the L{UltimateListCtrl}.
:param `entry`: should be a sequence with an item for each column.
"""
if entry:
if wx.USE_UNICODE:
cvtfunc = unicode
else:
cvtfunc = str
pos = self.GetItemCount()
self.InsertStringItem(pos, cvtfunc(entry[0]))
for i in range(1, len(entry)):
self.SetStringItem(pos, i, cvtfunc(entry[i]))
return pos
[docs] def SetFirstGradientColour(self, colour=None):
"""
Sets the first gradient colour for gradient-style selections.
:param `colour`: if not ``None``, a valid `wx.Colour` instance. Otherwise,
the colour is taken from the system value ``wx.SYS_COLOUR_HIGHLIGHT``.
"""
self._mainWin.SetFirstGradientColour(colour)
[docs] def SetSecondGradientColour(self, colour=None):
"""
Sets the second gradient colour for gradient-style selections.
:param `colour`: if not ``None``, a valid `wx.Colour` instance. Otherwise,
the colour generated is a slightly darker version of the L{UltimateListCtrl}
background colour.
"""
self._mainWin.SetSecondGradientColour(colour)
[docs] def GetFirstGradientColour(self):
""" Returns the first gradient colour for gradient-style selections. """
return self._mainWin.GetFirstGradientColour()
[docs] def GetSecondGradientColour(self):
""" Returns the second gradient colour for gradient-style selections. """
return self._mainWin.GetSecondGradientColour()
[docs] def EnableSelectionGradient(self, enable=True):
"""
Globally enables/disables drawing of gradient selections.
:param `enable`: ``True`` to enable gradient-style selections, ``False``
to disable it.
:note: Calling this method disables any Vista-style selection previously
enabled.
"""
self._mainWin.EnableSelectionGradient(enable)
[docs] def SetGradientStyle(self, vertical=0):
"""
Sets the gradient style for gradient-style selections.
:param `vertical`: 0 for horizontal gradient-style selections, 1 for vertical
gradient-style selections.
"""
self._mainWin.SetGradientStyle(vertical)
[docs] def GetGradientStyle(self):
"""
Returns the gradient style for gradient-style selections.
:return: 0 for horizontal gradient-style selections, 1 for vertical
gradient-style selections.
"""
return self._mainWin.GetGradientStyle()
[docs] def EnableSelectionVista(self, enable=True):
"""
Globally enables/disables drawing of Windows Vista selections.
:param `enable`: ``True`` to enable Vista-style selections, ``False`` to
disable it.
:note: Calling this method disables any gradient-style selection previously
enabled.
"""
self._mainWin.EnableSelectionVista(enable)
[docs] def SetBackgroundImage(self, image=None):
"""
Sets the L{UltimateListCtrl} background image.
:param `image`: if not ``None``, an instance of `wx.Bitmap`.
:note: At present, the background image can only be used in "tile" mode.
:todo: Support background images also in stretch and centered modes.
"""
self._mainWin.SetBackgroundImage(image)
[docs] def GetBackgroundImage(self):
"""
Returns the L{UltimateListCtrl} background image (if any).
:note: At present, the background image can only be used in "tile" mode.
:todo: Support background images also in stretch and centered modes.
"""
return self._mainWin.GetBackgroundImage()
[docs] def SetWaterMark(self, watermark=None):
"""
Sets the L{UltimateListCtrl} watermark image to be displayed in the bottom
right part of the window.
:param `watermark`: if not ``None``, an instance of `wx.Bitmap`.
:todo: Better support for this is needed.
"""
self._mainWin.SetWaterMark(watermark)
[docs] def GetWaterMark(self):
"""
Returns the L{UltimateListCtrl} watermark image (if any), displayed in the
bottom right part of the window.
:todo: Better support for this is needed.
"""
return self._mainWin.GetWaterMark()
[docs] def SetDisabledTextColour(self, colour):
"""
Sets the items disabled colour.
:param `colour`: an instance of `wx.Colour`.
"""
self._mainWin.SetDisabledTextColour(colour)
[docs] def GetDisabledTextColour(self):
""" Returns the items disabled colour. """
return self._mainWin.GetDisabledTextColour()
[docs] def GetHyperTextFont(self):
""" Returns the font used to render an hypertext item. """
return self._mainWin.GetHyperTextFont()
[docs] def SetHyperTextFont(self, font):
"""
Sets the font used to render hypertext items.
:param `font`: a valid `wx.Font` instance.
"""
self._mainWin.SetHyperTextFont(font)
[docs] def SetHyperTextNewColour(self, colour):
"""
Sets the colour used to render a non-visited hypertext item.
:param `colour`: a valid `wx.Colour` instance.
"""
self._mainWin.SetHyperTextNewColour(colour)
[docs] def GetHyperTextNewColour(self):
""" Returns the colour used to render a non-visited hypertext item. """
return self._mainWin.GetHyperTextNewColour()
[docs] def SetHyperTextVisitedColour(self, colour):
"""
Sets the colour used to render a visited hypertext item.
:param `colour`: a valid `wx.Colour` instance.
"""
self._mainWin.SetHyperTextVisitedColour(colour)
[docs] def GetHyperTextVisitedColour(self):
""" Returns the colour used to render a visited hypertext item. """
return self._mainWin.GetHyperTextVisitedColour()
[docs] def SetItemVisited(self, itemOrId, col=0, visited=True):
"""
Sets whether an hypertext item was visited or not.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `visited`: ``True`` to mark an hypertext item as visited, ``False`` otherwise.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemVisited(item, visited)
[docs] def GetItemVisited(self, itemOrId, col=0):
"""
Returns whether an hypertext item was visited.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemVisited(item)
[docs] def GetItemWindow(self, itemOrId, col=0):
"""
Returns the window associated to the item (if any).
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemWindow(item)
[docs] def SetItemWindow(self, itemOrId, col=0, wnd=None, expand=False):
"""
Sets the window for the given item.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `wnd`: a non-toplevel window to be displayed next to the item;
:param `expand`: ``True`` to expand the column where the item/subitem lives,
so that the window will be fully visible.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemWindow(item, wnd, expand)
[docs] def DeleteItemWindow(self, itemOrId, col=0):
"""
Deletes the window associated to an item (if any).
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.DeleteItemWindow(item)
[docs] def GetItemWindowEnabled(self, itemOrId, col=0):
"""
Returns whether the window associated to the item is enabled.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemWindowEnabled(item)
[docs] def SetItemWindowEnabled(self, itemOrId, col=0, enable=True):
"""
Enables/disables the window associated to the item.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `enable`: ``True`` to enable the associated window, ``False`` to disable it.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemWindowEnabled(item, enable)
[docs] def GetItemCustomRenderer(self, itemOrId, col=0):
"""
Returns the custom renderer used to draw the input item (if any).
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemCustomRenderer(item)
[docs] def SetColumnCustomRenderer(self, col=0, renderer=None):
"""
Associate a custom renderer to this column's header.
:param `col`: the column index.
:param `renderer`: a class able to correctly render the input item.
:note: the renderer class **must** implement the methods `DrawHeaderButton`
and `GetForegroundColor`.
"""
if not self.HasAGWFlag(ULC_REPORT):
raise Exception("Custom renderers can be used on with style = ULC_REPORT")
return self._mainWin.SetCustomRenderer(col, renderer)
[docs] def SetItemCustomRenderer(self, itemOrId, col=0, renderer=None):
"""
Associate a custom renderer to this item.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `renderer`: a class able to correctly render the input item.
:note: the renderer class **must** implement the methods `DrawSubItem`,
`GetLineHeight` and `GetSubItemWidth`.
"""
if not self.HasAGWFlag(ULC_REPORT) or not self.HasAGWFlag(ULC_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("Custom renderers can be used on with style = ULC_REPORT | ULC_HAS_VARIABLE_ROW_HEIGHT")
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemCustomRenderer(item, renderer)
[docs] def SetItemOverFlow(self, itemOrId, col=0, over=True):
"""
Sets the item in the overflow/non overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to;
:param `over`: ``True`` to set the item in a overflow state, ``False`` otherwise.
"""
if not self.HasAGWFlag(ULC_REPORT) or self._mainWin.IsVirtual():
raise Exception("Overflowing items can be used only in report, non-virtual mode")
item = CreateListItem(itemOrId, col)
return self._mainWin.SetItemOverFlow(item, over)
[docs] def GetItemOverFlow(self, itemOrId, col=0):
"""
Returns if the item is in the overflow state.
An item/subitem may overwrite neighboring items/subitems if its text would
not normally fit in the space allotted to it.
:param `itemOrId`: an instance of L{UltimateListItem} or the item index;
:param `col`: the column index to which the input item belongs to.
"""
item = CreateListItem(itemOrId, col)
return self._mainWin.GetItemOverFlow(item)
[docs] def IsVirtual(self):
""" Returns ``True`` if the L{UltimateListCtrl} has the ``ULC_VIRTUAL`` style set. """
return self._mainWin.IsVirtual()
[docs] def DoLayout(self):
"""
Layouts the header, main and footer windows. This is an auxiliary method to avoid code
duplication.
"""
self.Layout()
self._mainWin.ResizeColumns()
self._mainWin.ResetVisibleLinesRange(True)
self._mainWin.RecalculatePositions()
self._mainWin.AdjustScrollbars()
if self._headerWin:
self._headerWin.Refresh()
if self._footerWin:
self._footerWin.Refresh()