mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-04 19:10:09 +01:00
This is a possible simple fix for #2239 (SetHeaderHeight does not work in UltimateListCtrl). Only tested on Windows.
13672 lines
447 KiB
Python
13672 lines
447 KiB
Python
# --------------------------------------------------------------------------------- #
|
|
# ULTIMATELISTCTRL wxPython IMPLEMENTATION
|
|
# Inspired by and heavily based on the wxWidgets C++ generic version of wxListCtrl.
|
|
#
|
|
# Andrea Gavana, @ 08 May 2009
|
|
# Latest Revision: 27 Dec 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!!!
|
|
#
|
|
# Tags: phoenix-port, documented, unittest, py3-port
|
|
#
|
|
# End Of Comments
|
|
# --------------------------------------------------------------------------------- #
|
|
|
|
|
|
"""
|
|
Description
|
|
===========
|
|
|
|
UltimateListCtrl is a class that mimics the behaviour of :class:`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 :class:`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 :class:`wx.ImageList`. Simply use :class:`PyImageList` instead of :class:`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 :meth:`~UltimateListCtrl.SetItemPyData` and :meth:`~UltimateListCtrl.GetItemPyData`:
|
|
the data can be any Python object and not necessarily an integer as in :class:`ListCtrl`;
|
|
* CheckBox-type items and subitems;
|
|
* RadioButton-type items and subitems;
|
|
* Overflowing items/subitems, a la :class:`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, parent):
|
|
|
|
wx.Frame.__init__(self, parent, -1, "UltimateListCtrl Demo")
|
|
|
|
list = ULC.UltimateListCtrl(self, wx.ID_ANY, agwStyle=ULC.ULC_REPORT | ULC.ULC_VRULES | ULC.ULC_HRULES | ULC.ULC_SINGLE_SEL | ULC.ULC_HAS_VARIABLE_ROW_HEIGHT)
|
|
|
|
list.InsertColumn(0, "Column 1")
|
|
list.InsertColumn(1, "Column 2")
|
|
|
|
index = list.InsertStringItem(sys.maxsize, "Item 1")
|
|
list.SetStringItem(index, 1, "Sub-item 1")
|
|
|
|
index = list.InsertStringItem(sys.maxsize, "Item 2")
|
|
list.SetStringItem(index, 1, "Sub-item 2")
|
|
|
|
choice = wx.Choice(list, -1, choices=["one", "two"])
|
|
index = list.InsertStringItem(sys.maxsize, "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
|
|
|
|
if __name__ == "__main__":
|
|
app = wx.App(0)
|
|
|
|
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 :class:`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 :meth:`ListCtrl.SortItems`.)
|
|
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in :meth:`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 when 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).
|
|
``ULC_NO_ITEM_DRAG`` 0x200000000 Disable item dragging
|
|
=============================== =========== ====================================================================================================
|
|
|
|
|
|
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 @ 27 Dec 2012, 21.00 GMT
|
|
|
|
Version 0.8
|
|
|
|
"""
|
|
|
|
import wx
|
|
import math
|
|
import bisect
|
|
import io
|
|
import zlib
|
|
from functools import cmp_to_key
|
|
|
|
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
|
|
""" Draws light vertical rules between rows in report mode. """
|
|
ULC_HRULES = wx.LC_HRULES
|
|
""" Draws light horizontal rules between rows in report mode. """
|
|
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 when 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_NO_ITEM_DRAG = 0x200000000 # Disable item dragging
|
|
|
|
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
|
|
|
|
# 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_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
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Functions
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# Utility method
|
|
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, list):
|
|
return input
|
|
elif isinstance(input, int):
|
|
return [input]
|
|
else:
|
|
raise Exception("Invalid parameter passed to `to_list`: only integers and list are accepted.")
|
|
|
|
|
|
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 :class:`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.")
|
|
|
|
|
|
def CreateListItem(itemOrId, col):
|
|
"""
|
|
Creates a new instance of :class:`UltimateListItem`.
|
|
|
|
:param `itemOrId`: can be an instance of :class:`UltimateListItem` or an integer;
|
|
:param `col`: the item column.
|
|
"""
|
|
|
|
if isinstance(itemOrId, int):
|
|
item = UltimateListItem()
|
|
item._itemId = itemOrId
|
|
item._col = col
|
|
else:
|
|
item = itemOrId
|
|
|
|
return item
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def MakeDisabledBitmap(original):
|
|
"""
|
|
Creates a disabled-looking bitmap starting from the input one.
|
|
|
|
:param `original`: an instance of :class:`wx.Bitmap` to be greyed-out.
|
|
"""
|
|
|
|
img = original.ConvertToImage()
|
|
return wx.Bitmap(img.ConvertToGreyscale())
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
def GetdragcursorData():
|
|
""" Returns the drag and drop cursor image as a decompressed stream of characters. """
|
|
|
|
return zlib.decompress(
|
|
b"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" )
|
|
|
|
|
|
def GetdragcursorBitmap():
|
|
""" Returns the drag and drop cursor image as a :class:`wx.Bitmap`. """
|
|
|
|
return wx.Bitmap(GetdragcursorImage())
|
|
|
|
|
|
def GetdragcursorImage():
|
|
""" Returns the drag and drop cursor image as a :class:`wx.Image`. """
|
|
|
|
stream = io.BytesIO(GetdragcursorData())
|
|
return wx.Image(stream)
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# PyImageList
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class PyImageList(object):
|
|
"""
|
|
A :class:`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.
|
|
|
|
:class:`PyImageList` is used in conjunction with :class:`UltimateListCtrl`.
|
|
|
|
:note: The main improvements that :class:`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).
|
|
"""
|
|
|
|
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
|
|
:class:`PyImageList`);
|
|
:param `initialCount`: the initial size of the list (unused in :class:`PyImageList`);
|
|
:param `style`: can be one of the following bits:
|
|
|
|
==================== ===== =================================
|
|
Style Flag Value Description
|
|
==================== ===== =================================
|
|
``IL_FIXED_SIZE`` 0 All the images in :class:`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 = []
|
|
|
|
|
|
def GetImageCount(self):
|
|
""" Returns the number of images in the list. """
|
|
|
|
return len(self._images)
|
|
|
|
|
|
|
|
def Add(self, bitmap):
|
|
"""
|
|
Adds a new image or images using a bitmap.
|
|
|
|
:param `bitmap`: a valid :class:`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 range(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
|
|
|
|
|
|
def AddIcon(self, icon):
|
|
"""
|
|
Adds a new image using an icon.
|
|
|
|
:param `icon`: a valid :class:`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.Bitmap(icon))
|
|
|
|
|
|
def AddWithColourMask(self, bitmap, maskColour):
|
|
"""
|
|
Adds a new image or images using a bitmap and a colour mask.
|
|
|
|
:param `bitmap`: a valid :class:`wx.Bitmap` object;
|
|
:param `colour`: an instance of :class:`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.Bitmap(img))
|
|
|
|
|
|
def GetBitmap(self, index):
|
|
"""
|
|
Returns the bitmap corresponding to the given `index`, or :class:`NullBitmap`
|
|
if the index is invalid.
|
|
|
|
:param `index`: the bitmap index.
|
|
"""
|
|
|
|
if index >= len(self._images):
|
|
return wx.NullBitmap
|
|
|
|
return self._images[index]
|
|
|
|
|
|
def GetIcon(self, index):
|
|
"""
|
|
Returns the icon corresponding to the given `index`, or :class:`NullIcon`
|
|
if the index is invalid.
|
|
|
|
:param `index`: the icon index.
|
|
"""
|
|
|
|
if index >= len(self._images):
|
|
return wx.NullIcon
|
|
|
|
icon = wx.Icon()
|
|
icon.CopyFromBitmap(self.GetBitmap(index))
|
|
return icon
|
|
|
|
|
|
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
|
|
:class:`wx.Bitmap`.
|
|
"""
|
|
|
|
if index >= len(self._images):
|
|
raise Exception("Wrong index in image list")
|
|
|
|
self._images[index] = bitmap
|
|
|
|
return True
|
|
|
|
|
|
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
|
|
:class:`Icon`.
|
|
"""
|
|
|
|
return self.Replace(index, wx.Bitmap(icon))
|
|
|
|
|
|
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
|
|
|
|
|
|
def RemoveAll(self):
|
|
""" Removes all the images in the list. """
|
|
|
|
self._images = []
|
|
return True
|
|
|
|
|
|
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()
|
|
|
|
|
|
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 :class:`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, int(y), (flags & wx.IMAGELIST_DRAW_TRANSPARENT) > 0)
|
|
|
|
return True
|
|
|
|
|
|
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.
|
|
"""
|
|
|
|
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)
|
|
def Clear(self):
|
|
""" Clears the number of selected items. """
|
|
|
|
self._itemsSel = []
|
|
self._count = 0
|
|
self._defaultState = False
|
|
|
|
# return the total number of selected items
|
|
def GetSelectedCount(self):
|
|
""" Return the total number of selected items. """
|
|
|
|
return (self._defaultState and [self._count - len(self._itemsSel)] or [len(self._itemsSel)])[0]
|
|
|
|
|
|
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]
|
|
|
|
|
|
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
|
|
|
|
|
|
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 range(itemFrom):
|
|
if item not in selOld:
|
|
self._itemsSel.append(item)
|
|
|
|
for item in range(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 range(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 range(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
|
|
|
|
|
|
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
|
|
|
|
|
|
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 range(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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class UltimateListItemAttr(object):
|
|
"""
|
|
Represents the attributes (colour, font, ...) of a :class:`UltimateListCtrl`
|
|
:class:`UltimateListItem`.
|
|
"""
|
|
|
|
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
|
|
def SetTextColour(self, colText):
|
|
"""
|
|
Sets a new text colour.
|
|
|
|
:param `colText`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
self._colText = colText
|
|
|
|
|
|
def SetBackgroundColour(self, colBack):
|
|
"""
|
|
Sets a new background colour.
|
|
|
|
:param `colBack`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
self._colBack = colBack
|
|
|
|
|
|
def SetFont(self, font):
|
|
"""
|
|
Sets a new font for the item.
|
|
|
|
:param `font`: an instance of :class:`wx.Font`.
|
|
"""
|
|
|
|
self._font = font
|
|
|
|
|
|
def Enable(self, enable=True):
|
|
"""
|
|
Enables or disables the item.
|
|
|
|
:param `enable`: ``True`` to enable the item, ``False`` to disable it.
|
|
"""
|
|
|
|
self._enabled = enable
|
|
|
|
|
|
def SetFooterTextColour(self, colText):
|
|
"""
|
|
Sets a new footer item text colour.
|
|
|
|
:param `colText`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
self._footerColText = colText
|
|
|
|
|
|
def SetFooterBackgroundColour(self, colBack):
|
|
"""
|
|
Sets a new footer item background colour.
|
|
|
|
:param `colBack`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
self._footerColBack = colBack
|
|
|
|
|
|
def SetFooterFont(self, font):
|
|
"""
|
|
Sets a new font for the footer item.
|
|
|
|
:param `font`: an instance of :class:`wx.Font`.
|
|
"""
|
|
|
|
self._footerFont = font
|
|
|
|
|
|
# accessors
|
|
def HasTextColour(self):
|
|
""" Returns ``True`` if the currently set text colour is valid. """
|
|
|
|
return self._colText.IsOk()
|
|
|
|
|
|
def HasBackgroundColour(self):
|
|
""" Returns ``True`` if the currently set background colour is valid. """
|
|
|
|
return self._colBack.IsOk()
|
|
|
|
|
|
def HasFont(self):
|
|
""" Returns ``True`` if the currently set font is valid. """
|
|
|
|
return self._font.IsOk()
|
|
|
|
|
|
def HasFooterTextColour(self):
|
|
"""
|
|
Returns ``True`` if the currently set text colour for the footer item
|
|
is valid.
|
|
"""
|
|
|
|
return self._footerColText.IsOk()
|
|
|
|
|
|
def HasFooterBackgroundColour(self):
|
|
"""
|
|
Returns ``True`` if the currently set background colour for the footer item
|
|
is valid.
|
|
"""
|
|
|
|
return self._footerColBack.IsOk()
|
|
|
|
|
|
def HasFooterFont(self):
|
|
"""
|
|
Returns ``True`` if the currently set font for the footer item
|
|
is valid.
|
|
"""
|
|
|
|
return self._footerFont.IsOk()
|
|
|
|
|
|
# getters
|
|
def GetTextColour(self):
|
|
""" Returns the currently set text colour. """
|
|
|
|
return self._colText
|
|
|
|
|
|
def GetBackgroundColour(self):
|
|
""" Returns the currently set background colour. """
|
|
|
|
return self._colBack
|
|
|
|
|
|
def GetFont(self):
|
|
""" Returns the currently set item font. """
|
|
|
|
return self._font
|
|
|
|
|
|
def GetFooterTextColour(self):
|
|
""" Returns the currently set text colour for a footer item. """
|
|
|
|
return self._footerColText
|
|
|
|
|
|
def GetFooterBackgroundColour(self):
|
|
""" Returns the currently set background colour for a footer item. """
|
|
|
|
return self._footerColBack
|
|
|
|
|
|
def GetFooterFont(self):
|
|
""" Returns the currently set font for a footer item. """
|
|
|
|
return self._footerFont
|
|
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class UltimateListItem(wx.Object):
|
|
""" This class stores information about a :class:`UltimateListCtrl` item or column. """
|
|
|
|
def __init__(self, item=None):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `item`: if not ``None``, another instance of :class:`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
|
|
def Clear(self):
|
|
""" Resets the item state to the default. """
|
|
|
|
self.Init()
|
|
self._text = ""
|
|
self.ClearAttributes()
|
|
|
|
|
|
def ClearAttributes(self):
|
|
""" Deletes the item attributes if they have been stored. """
|
|
|
|
if self._attr:
|
|
del self._attr
|
|
self._attr = None
|
|
|
|
# setters
|
|
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 :meth:`~UltimateListItem.GetState` is valid
|
|
``ULC_MASK_TEXT`` 0x2 :meth:`~UltimateListItem.GetText` is valid
|
|
``ULC_MASK_IMAGE`` 0x4 :meth:`~UltimateListItem.GetImage` is valid
|
|
``ULC_MASK_DATA`` 0x8 :meth:`~UltimateListItem.GetData` is valid
|
|
``ULC_MASK_WIDTH`` 0x20 :meth:`~UltimateListItem.GetWidth` is valid
|
|
``ULC_MASK_FORMAT`` 0x40 :meth:`~UltimateListItem.GetFormat` is valid
|
|
``ULC_MASK_FONTCOLOUR`` 0x80 :meth:`~UltimateListItem.GetTextColour` is valid
|
|
``ULC_MASK_FONT`` 0x100 :meth:`~UltimateListItem.GetFont` is valid
|
|
``ULC_MASK_BACKCOLOUR`` 0x200 :meth:`~UltimateListItem.GetBackgroundColour` is valid
|
|
``ULC_MASK_KIND`` 0x400 :meth:`~UltimateListItem.GetKind` is valid
|
|
``ULC_MASK_ENABLE`` 0x800 :meth:`~UltimateListItem.IsEnabled` is valid
|
|
``ULC_MASK_CHECK`` 0x1000 :meth:`~UltimateListItem.IsChecked` is valid
|
|
``ULC_MASK_HYPERTEXT`` 0x2000 :meth:`~UltimateListItem.IsHyperText` is valid
|
|
``ULC_MASK_WINDOW`` 0x4000 :meth:`~UltimateListItem.GetWindow` is valid
|
|
``ULC_MASK_PYDATA`` 0x8000 :meth:`~UltimateListItem.GetPyData` is valid
|
|
``ULC_MASK_SHOWN`` 0x10000 :meth:`~UltimateListItem.IsShown` is valid
|
|
``ULC_MASK_RENDERER`` 0x20000 :meth:`~UltimateListItem.GetCustomRenderer` is valid
|
|
``ULC_MASK_OVERFLOW`` 0x40000 :meth:`~UltimateListItem.GetOverFlow` is valid
|
|
``ULC_MASK_FOOTER_TEXT`` 0x80000 :meth:`~UltimateListItem.GetFooterText` is valid
|
|
``ULC_MASK_FOOTER_IMAGE`` 0x100000 :meth:`~UltimateListItem.GetFooterImage` is valid
|
|
``ULC_MASK_FOOTER_FORMAT`` 0x200000 :meth:`~UltimateListItem.GetFooterFormat` is valid
|
|
``ULC_MASK_FOOTER_FONT`` 0x400000 :meth:`~UltimateListItem.GetFooterFont` is valid
|
|
``ULC_MASK_FOOTER_CHECK`` 0x800000 :meth:`~UltimateListItem.IsFooterChecked` is valid
|
|
``ULC_MASK_FOOTER_KIND`` 0x1000000 :meth:`~UltimateListItem.GetFooterKind` is valid
|
|
============================ ========= ==============================
|
|
|
|
"""
|
|
|
|
self._mask = mask
|
|
|
|
|
|
def SetId(self, id):
|
|
"""
|
|
Sets the zero-based item position.
|
|
|
|
:param `id`: the zero-based item position.
|
|
"""
|
|
|
|
self._itemId = id
|
|
|
|
|
|
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
|
|
|
|
|
|
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: :meth:`~UltimateListItem.SetStateMask`
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_STATE
|
|
self._state = state
|
|
self._stateMask |= state
|
|
|
|
|
|
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: :meth:`~UltimateListItem.SetState` for a list of valid state bits.
|
|
"""
|
|
|
|
self._stateMask = stateMask
|
|
|
|
|
|
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
|
|
|
|
|
|
def SetToolTip(self, text):
|
|
"""
|
|
Sets the tooltip text for the item.
|
|
|
|
:param `text`: the tooltip text for the item.
|
|
"""
|
|
self._mask |= ULC_MASK_TOOLTIP
|
|
self._tooltip = text
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def SetTextColour(self, colText):
|
|
"""
|
|
Sets the text colour for the item.
|
|
|
|
:param `colText`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
self.Attributes().SetTextColour(colText)
|
|
|
|
|
|
def SetBackgroundColour(self, colBack):
|
|
"""
|
|
Sets the background colour for the item.
|
|
|
|
:param `colBack`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
self.Attributes().SetBackgroundColour(colBack)
|
|
|
|
|
|
def SetFont(self, font):
|
|
"""
|
|
Sets the font for the item.
|
|
|
|
:param `font`: a valid :class:`wx.Font` object.
|
|
"""
|
|
|
|
self.Attributes().SetFont(font)
|
|
|
|
|
|
def SetFooterTextColour(self, colText):
|
|
"""
|
|
Sets the text colour for the footer item.
|
|
|
|
:param `colText`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
self.Attributes().SetFooterTextColour(colText)
|
|
|
|
|
|
def SetFooterBackgroundColour(self, colBack):
|
|
"""
|
|
Sets the background colour for the footer item.
|
|
|
|
:param `colBack`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
self.Attributes().SetFooterBackgroundColour(colBack)
|
|
|
|
|
|
def SetFooterFont(self, font):
|
|
"""
|
|
Sets the font for the footer item.
|
|
|
|
:param `font`: a valid :class:`wx.Font` object.
|
|
"""
|
|
|
|
self.Attributes().SetFooterFont(font)
|
|
|
|
|
|
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
|
|
def GetMask(self):
|
|
"""
|
|
Returns a bit mask indicating which fields of the structure are valid.
|
|
|
|
:see: :meth:`~UltimateListItem.SetMask` for a list of valid bit masks.
|
|
"""
|
|
|
|
return self._mask
|
|
|
|
|
|
def GetId(self):
|
|
""" Returns the zero-based item position. """
|
|
|
|
return self._itemId
|
|
|
|
|
|
def GetColumn(self):
|
|
"""
|
|
Returns the zero-based column.
|
|
|
|
:note: This method is meaningful only in report mode.
|
|
"""
|
|
|
|
return self._col
|
|
|
|
|
|
def GetFormat(self):
|
|
""" Returns the header item format. """
|
|
|
|
return self._format
|
|
|
|
|
|
def GetState(self):
|
|
"""
|
|
Returns a bit field representing the state of the item.
|
|
|
|
:see: :meth:`~UltimateListItem.SetState` for a list of valid item states.
|
|
"""
|
|
|
|
return self._state & self._stateMask
|
|
|
|
|
|
def GetText(self):
|
|
""" Returns the label/header text. """
|
|
|
|
return self._text
|
|
|
|
|
|
def GetToolTip(self):
|
|
""" Returns the label/header tooltip. """
|
|
|
|
return self._tooltip
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetWidth(self):
|
|
"""
|
|
Returns the column width.
|
|
|
|
:note: This method is meaningful only for column headers in report mode.
|
|
"""
|
|
|
|
return self._width
|
|
|
|
|
|
def GetAlign(self):
|
|
"""
|
|
Returns the alignment for the item.
|
|
|
|
:see: :meth:`~UltimateListItem.SetAlign` for a list of valid alignment bits.
|
|
"""
|
|
|
|
return self._format
|
|
|
|
|
|
def GetAttributes(self):
|
|
""" Returns the associated :class:`UltimateListItemAttr` attributes. """
|
|
|
|
return self._attr
|
|
|
|
|
|
def HasAttributes(self):
|
|
""" Returns ``True`` if the item has attributes associated with it. """
|
|
|
|
return self._attr is not None
|
|
|
|
|
|
def GetTextColour(self):
|
|
""" Returns the text colour. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetTextColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def GetBackgroundColour(self):
|
|
""" Returns the background colour. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetBackgroundColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def GetFont(self):
|
|
""" Returns the item font. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetFont()] or [wx.NullFont])[0]
|
|
|
|
|
|
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
|
|
def Attributes(self):
|
|
"""
|
|
Returns the associated attributes if they exist, or create a new :class:`UltimateListItemAttr`
|
|
structure and associate it with this item.
|
|
"""
|
|
|
|
if not self._attr:
|
|
self._attr = UltimateListItemAttr()
|
|
|
|
return self._attr
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetKind(self):
|
|
"""
|
|
Returns the item kind.
|
|
|
|
:see: :meth:`~UltimateListItem.SetKind` for a valid list of item's kind.
|
|
"""
|
|
|
|
return self._kind
|
|
|
|
|
|
def IsChecked(self):
|
|
""" Returns whether the item is checked or not. """
|
|
|
|
return self._checked
|
|
|
|
|
|
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
|
|
|
|
|
|
def IsShown(self):
|
|
""" Returns ``True`` if the item is shown, or ``False`` if it is hidden. """
|
|
|
|
return self._isColumnShown
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetVisited(self):
|
|
""" Returns whether an hypertext item was visited or not. """
|
|
|
|
return self._visited
|
|
|
|
|
|
def IsHyperText(self):
|
|
""" Returns whether the item is hypetext or not. """
|
|
|
|
return self._hypertext
|
|
|
|
|
|
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()
|
|
|
|
|
|
def GetWindow(self):
|
|
""" Returns the window associated to the item. """
|
|
|
|
return self._wnd
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetWindowSize(self):
|
|
""" Returns the associated window size. """
|
|
|
|
return self._windowsize
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetCustomRenderer(self):
|
|
""" Returns the custom renderer associated with this item (if any). """
|
|
|
|
return self._customRenderer
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def Init(self):
|
|
""" Initializes an empty :class:`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.BLACK
|
|
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
|
|
|
|
|
|
def SetFooterKind(self, kind):
|
|
"""
|
|
Sets the footer item kind.
|
|
|
|
:see: :meth:`~UltimateListItem.SetKind` for a list of valid items kind.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_KIND
|
|
self._footerKind = kind
|
|
|
|
|
|
def GetFooterKind(self):
|
|
"""
|
|
Returns the footer item kind.
|
|
|
|
:see: :meth:`~UltimateListItem.SetKind` for a list of valid items kind.
|
|
"""
|
|
|
|
return self._footerKind
|
|
|
|
|
|
def IsFooterChecked(self):
|
|
""" Returns whether the footer item is checked or not. """
|
|
|
|
return self._footerChecked
|
|
|
|
|
|
def CheckFooter(self, checked=True):
|
|
"""
|
|
Checks/unchecks a footer item.
|
|
|
|
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
|
|
|
|
:note: This method is meaningful only for check and radio footer items.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_CHECK
|
|
self._footerChecked = checked
|
|
|
|
|
|
def GetFooterFormat(self):
|
|
""" Returns the footer item format. """
|
|
|
|
return self._footerFormat
|
|
|
|
|
|
def SetFooterFormat(self, format):
|
|
"""
|
|
Sets the footer item format.
|
|
|
|
:param `format`: the footer item format.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_FORMAT
|
|
self._footerFormat = format
|
|
|
|
|
|
def GetFooterText(self):
|
|
""" Returns the footer text. """
|
|
|
|
return self._footerText
|
|
|
|
|
|
def SetFooterText(self, text):
|
|
"""
|
|
Sets the text label for the footer item.
|
|
|
|
:param `text`: the text label for the footer item.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_TEXT
|
|
self._footerText = text
|
|
|
|
|
|
def GetFooterImage(self):
|
|
"""
|
|
Returns the zero-based index of the image associated with the footer item into
|
|
the image list.
|
|
"""
|
|
|
|
return self._footerImage
|
|
|
|
|
|
def SetFooterImage(self, image):
|
|
"""
|
|
Sets the zero-based index of the image associated with the footer item into the
|
|
image list.
|
|
|
|
:param `image`: the zero-based index of the image associated with the footer item
|
|
into the image list.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_IMAGE
|
|
self._footerImage = to_list(image)
|
|
|
|
|
|
def GetFooterTextColour(self):
|
|
""" Returns the footer item text colour. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetFooterTextColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def GetFooterBackgroundColour(self):
|
|
""" Returns the footer item background colour. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetFooterBackgroundColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def GetFooterFont(self):
|
|
""" Returns the footer item font. """
|
|
|
|
return (self.HasAttributes() and [self._attr.GetFooterFont()] or [wx.NullFont])[0]
|
|
|
|
|
|
def SetFooterAlign(self, align):
|
|
"""
|
|
Sets the alignment for the footer item.
|
|
|
|
:see: :meth:`~UltimateListItem.SetAlign` for a list of valid alignment flags.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_FOOTER_FORMAT
|
|
self._footerFormat = align
|
|
|
|
|
|
def GetFooterAlign(self):
|
|
"""
|
|
Returns the alignment for the footer item.
|
|
|
|
:see: :meth:`~UltimateListItem.SetAlign` for a list of valid alignment flags.
|
|
"""
|
|
|
|
return self._footerFormat
|
|
|
|
|
|
def OnSetFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SET_FOCUS`` event for the window associated to an item.
|
|
|
|
:param `event`: a :class:`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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class CommandListEvent(wx.PyCommandEvent):
|
|
"""
|
|
A list event holds information about events associated with :class:`UltimateListCtrl`
|
|
objects.
|
|
"""
|
|
|
|
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
|
|
:class:`PyCommandEvent`;
|
|
:param `winid`: the event identifier.
|
|
"""
|
|
|
|
if isinstance(commandTypeOrEvent, int):
|
|
|
|
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
|
|
|
|
|
|
def GetKeyCode(self):
|
|
""" Returns the key code if the event is a keypress event. """
|
|
|
|
return self.m_code
|
|
|
|
|
|
def GetIndex(self):
|
|
""" Returns the item index. """
|
|
|
|
return self.m_itemIndex
|
|
|
|
Index = property(GetIndex, doc="See `GetIndex`")
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetPoint(self):
|
|
""" Returns the position of the mouse pointer if the event is a drag event. """
|
|
|
|
return self.m_pointDrag
|
|
|
|
|
|
def GetLabel(self):
|
|
""" Returns the (new) item label for ``EVT_LIST_END_LABEL_EDIT`` event. """
|
|
|
|
return self.m_item._text
|
|
|
|
|
|
def GetText(self):
|
|
""" Returns the item text. """
|
|
|
|
return self.m_item._text
|
|
|
|
|
|
def GetImage(self):
|
|
""" Returns the item image. """
|
|
|
|
return self.m_item._image
|
|
|
|
|
|
def GetData(self):
|
|
""" Returns the item data. """
|
|
|
|
return self.m_item._data
|
|
|
|
|
|
def GetMask(self):
|
|
""" Returns the item mask. """
|
|
|
|
return self.m_item._mask
|
|
|
|
|
|
def GetItem(self):
|
|
""" Returns the item itself. """
|
|
|
|
return self.m_item
|
|
|
|
|
|
# for wxEVT_COMMAND_LIST_CACHE_HINT only
|
|
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
|
|
|
|
|
|
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)
|
|
def IsEditCancelled(self):
|
|
"""
|
|
Returns ``True`` if it the label editing has been cancelled by the user
|
|
(:meth:`~CommandListEvent.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
|
|
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
class UltimateListEvent(CommandListEvent):
|
|
"""
|
|
A list event holds information about events associated with :class:`UltimateListCtrl`
|
|
objects.
|
|
"""
|
|
|
|
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
|
|
:class:`PyCommandEvent`;
|
|
:param `winid`: the event identifier.
|
|
"""
|
|
|
|
CommandListEvent.__init__(self, commandTypeOrEvent, winid)
|
|
|
|
if isinstance(commandTypeOrEvent, int):
|
|
self.notify = wx.NotifyEvent(commandTypeOrEvent, winid)
|
|
else:
|
|
self.notify = wx.NotifyEvent(commandTypeOrEvent.GetEventType(), commandTypeOrEvent.GetId())
|
|
|
|
|
|
def GetNotifyEvent(self):
|
|
""" Returns the actual :class:`NotifyEvent`. """
|
|
|
|
return self.notify
|
|
|
|
|
|
def IsAllowed(self):
|
|
"""
|
|
Returns ``True`` if the change is allowed (:meth:`~UltimateListEvent.Veto` hasn't been called) or
|
|
``False`` otherwise (if it was).
|
|
"""
|
|
|
|
return self.notify.IsAllowed()
|
|
|
|
|
|
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()
|
|
|
|
|
|
def Allow(self):
|
|
"""
|
|
This is the opposite of :meth:`~UltimateListEvent.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)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class ColWidthInfo(object):
|
|
""" A simple class which holds information about :class:`UltimateListCtrl` columns. """
|
|
|
|
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)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListItemData(object):
|
|
"""
|
|
A simple class which holds information about :class:`UltimateListItem` visual
|
|
attributes (client rectangles, positions, etc...).
|
|
"""
|
|
|
|
def __init__(self, owner):
|
|
"""
|
|
Default class constructor
|
|
|
|
:param `owner`: an instance of :class:`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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
|
|
|
|
def HasText(self):
|
|
""" Returns ``True`` if the item text is not the empty string. """
|
|
|
|
return self._text != ""
|
|
|
|
|
|
def GetText(self):
|
|
""" Returns the item text. """
|
|
|
|
return self._text
|
|
|
|
|
|
def GetToolTip(self):
|
|
""" Returns the item tooltip. """
|
|
|
|
return self._tooltip
|
|
|
|
|
|
def GetBackgroundColour(self):
|
|
""" Returns the currently set background colour. """
|
|
|
|
return self._backColour
|
|
|
|
|
|
def GetColour(self):
|
|
""" Returns the currently set text colour. """
|
|
|
|
return self._colour
|
|
|
|
|
|
def GetFont(self):
|
|
""" Returns the currently set font. """
|
|
|
|
return (self._hasFont and [self._font] or [wx.NullFont])[0]
|
|
|
|
|
|
def SetText(self, text):
|
|
"""
|
|
Sets the text label for the item.
|
|
|
|
:param `text`: the text label for the item.
|
|
"""
|
|
|
|
self._text = text
|
|
|
|
|
|
def SetToolTip(self, tooltip):
|
|
"""
|
|
Sets the tooltip for the item
|
|
|
|
:param `tooltip`: the tooltip text
|
|
"""
|
|
|
|
self._tooltip = tooltip
|
|
|
|
|
|
def SetColour(self, colour):
|
|
"""
|
|
Sets the text colour for the item.
|
|
|
|
:param `colour`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
if colour == wx.NullColour or colour is None:
|
|
if self._hasColour:
|
|
self._hasColour = False
|
|
del self._colour
|
|
return
|
|
|
|
self._hasColour = True
|
|
self._colour = colour
|
|
|
|
|
|
def SetFont(self, font):
|
|
"""
|
|
Sets the text font for the item.
|
|
|
|
:param `font`: an instance of :class:`wx.Font`.
|
|
"""
|
|
|
|
if font == wx.NullFont:
|
|
self._hasFont = False
|
|
del self._font
|
|
return
|
|
|
|
self._hasFont = True
|
|
self._font = font
|
|
|
|
|
|
def SetBackgroundColour(self, colour):
|
|
"""
|
|
Sets the background colour for the item.
|
|
|
|
:param `colour`: an instance of :class:`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
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def HasImage(self):
|
|
""" Returns ``True`` if the item has at least one image associated with it. """
|
|
|
|
return len(self._image) > 0
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetKind(self):
|
|
"""
|
|
Returns the item kind.
|
|
|
|
:see: :meth:`~UltimateListItemData.SetKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
return self._kind
|
|
|
|
|
|
def IsChecked(self):
|
|
""" Returns whether the item is checked or not. """
|
|
|
|
return self._checked
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetVisited(self):
|
|
"""Returns whether an hypertext item was visited or not."""
|
|
|
|
return self._visited
|
|
|
|
|
|
def IsHyperText(self):
|
|
"""Returns whether the item is hypetext or not."""
|
|
|
|
return self._hypertext
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetWindow(self):
|
|
""" Returns the window associated to the item. """
|
|
|
|
return self._wnd
|
|
|
|
|
|
def DeleteWindow(self):
|
|
""" Deletes the window associated to the item (if any). """
|
|
|
|
if self._wnd:
|
|
self._wnd.Destroy()
|
|
self._wnd = None
|
|
|
|
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetWindowSize(self):
|
|
""" Returns the associated window size. """
|
|
|
|
return self._windowsize
|
|
|
|
|
|
def SetAttr(self, attr):
|
|
"""
|
|
Sets the item attributes.
|
|
|
|
:param `attr`: an instance of :class:`UltimateListItemAttr`.
|
|
"""
|
|
|
|
self._attr = attr
|
|
|
|
|
|
def GetAttr(self):
|
|
""" Returns the item attributes. """
|
|
|
|
return self._attr
|
|
|
|
|
|
def HasColour(self):
|
|
""" Returns ``True`` if the currently set text colour is valid. """
|
|
|
|
return self._hasColour
|
|
|
|
|
|
def HasFont(self):
|
|
""" Returns ``True`` if the currently set font is valid. """
|
|
|
|
return self._hasFont
|
|
|
|
|
|
def HasBackgroundColour(self):
|
|
""" Returns ``True`` if the currently set background colour is valid. """
|
|
|
|
return self._hasBackColour
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetCustomRenderer(self):
|
|
""" Returns the custom renderer associated with this item (if any). """
|
|
|
|
return self._customRenderer
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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.BLACK
|
|
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
|
|
|
|
|
|
def SetItem(self, info):
|
|
"""
|
|
Sets information about the item.
|
|
|
|
:param `info`: an instance of :class:`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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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))
|
|
|
|
|
|
def GetX(self):
|
|
""" Returns the item `x` position. """
|
|
|
|
return self._rect.x
|
|
|
|
|
|
def GetY(self):
|
|
""" Returns the item `y` position. """
|
|
|
|
return self._rect.y
|
|
|
|
|
|
def GetWidth(self):
|
|
""" Returns the item width, in pixels. """
|
|
|
|
return self._rect.width
|
|
|
|
|
|
def GetHeight(self):
|
|
""" Returns the item height, in pixels. """
|
|
|
|
return self._rect.height
|
|
|
|
|
|
def GetItem(self, info):
|
|
"""
|
|
Returns information about the item.
|
|
|
|
:param `info`: an instance of :class:`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
|
|
|
|
|
|
def IsEnabled(self):
|
|
""" Returns ``True`` if the item is enabled, ``False`` if it is disabled. """
|
|
|
|
return self._enabled
|
|
|
|
|
|
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)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListHeaderData(object):
|
|
"""
|
|
A simple class which holds information about :class:`UltimateListItem` visual
|
|
attributes for the header/footer items (client rectangles, positions, etc...).
|
|
"""
|
|
|
|
def __init__(self, item=None):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `item`: another instance of :class:`UltimateListHeaderData`.
|
|
"""
|
|
|
|
self.Init()
|
|
|
|
if item:
|
|
self.SetItem(item)
|
|
|
|
|
|
def HasText(self):
|
|
""" Returns ``True`` if the currently set text colour is valid. """
|
|
|
|
return self._text != ""
|
|
|
|
|
|
def GetText(self):
|
|
""" Returns the header/footer item text. """
|
|
|
|
return self._text
|
|
|
|
|
|
def GetToolTip(self):
|
|
""" Returns the header/footer item tooltip. """
|
|
|
|
return self._tooltip
|
|
|
|
|
|
def SetText(self, text):
|
|
"""
|
|
Sets the header/footer item text.
|
|
|
|
:param `text`: the new header/footer text.
|
|
"""
|
|
|
|
self._text = text
|
|
|
|
|
|
def SetToolTip(self, tip):
|
|
"""
|
|
Sets the header/footer item tooltip.
|
|
|
|
:param `tip`: the new header/footer tooltip.
|
|
"""
|
|
|
|
self._tip = tip
|
|
|
|
|
|
def GetFont(self):
|
|
""" Returns the header/footer item font. """
|
|
|
|
return self._font
|
|
|
|
|
|
def Init(self):
|
|
""" Initializes the header/footer item. """
|
|
|
|
self._mask = 0
|
|
self._image = []
|
|
self._format = 0
|
|
self._width = 0
|
|
self._xpos = 0
|
|
self._ypos = 0
|
|
self._height = 0
|
|
self._text = ""
|
|
self._tooltip = ""
|
|
self._kind = 0
|
|
self._checked = False
|
|
self._font = wx.NullFont
|
|
self._state = 0
|
|
self._isColumnShown = True
|
|
self._customRenderer = None
|
|
|
|
self._footerImage = []
|
|
self._footerFormat = 0
|
|
self._footerText = ""
|
|
self._footerKind = 0
|
|
self._footerChecked = False
|
|
self._footerFont = wx.NullFont
|
|
|
|
|
|
def SetItem(self, item):
|
|
"""
|
|
Sets information about the header/footer item.
|
|
|
|
:param `info`: an instance of :class:`UltimateListHeaderData`.
|
|
"""
|
|
|
|
self._mask = item._mask
|
|
|
|
if self._mask & ULC_MASK_TEXT:
|
|
self._text = item._text
|
|
|
|
if self._mask & ULC_MASK_TOOLTIP:
|
|
self._tooltip = item._tooltip
|
|
|
|
if self._mask & ULC_MASK_FOOTER_TEXT:
|
|
self._footerText = item._footerText
|
|
|
|
if self._mask & ULC_MASK_IMAGE:
|
|
self._image = item._image[:]
|
|
|
|
if self._mask & ULC_MASK_FOOTER_IMAGE:
|
|
self._footerImage = item._footerImage[:]
|
|
|
|
if self._mask & ULC_MASK_FORMAT:
|
|
self._format = item._format
|
|
|
|
if self._mask & ULC_MASK_FOOTER_FORMAT:
|
|
self._footerFormat = item._footerFormat
|
|
|
|
if self._mask & ULC_MASK_WIDTH:
|
|
self.SetWidth(item._width)
|
|
|
|
if self._mask & ULC_MASK_FONT:
|
|
self._font = item._font
|
|
|
|
if self._mask & ULC_MASK_FOOTER_FONT:
|
|
self._footerFont = item._footerFont
|
|
|
|
if self._mask & ULC_MASK_FOOTER_KIND:
|
|
self._footerKind = item._footerKind
|
|
self._footerChecked = item._footerChecked
|
|
|
|
if self._mask & ULC_MASK_KIND:
|
|
self._kind = item._kind
|
|
self._checked = item._checked
|
|
|
|
if self._mask & ULC_MASK_CHECK:
|
|
self._kind = item._kind
|
|
self._checked = item._checked
|
|
|
|
if self._mask & ULC_MASK_FOOTER_CHECK:
|
|
self._footerKind = item._footerKind
|
|
self._footerChecked = item._footerChecked
|
|
|
|
if self._mask & ULC_MASK_STATE:
|
|
self.SetState(item._state)
|
|
|
|
if self._mask & ULC_MASK_SHOWN:
|
|
self._isColumnShown = item._isColumnShown
|
|
|
|
if self._mask & ULC_MASK_RENDERER:
|
|
self._customRenderer = item._customRenderer
|
|
|
|
|
|
def SetState(self, flag):
|
|
"""
|
|
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
|
|
============================ ========= ==============================
|
|
|
|
"""
|
|
|
|
self._state = flag
|
|
|
|
|
|
def SetPosition(self, x, y):
|
|
"""
|
|
Sets the header/footer item position.
|
|
|
|
:param `x`: the item `x` position;
|
|
:param `y`: the item `y` position.
|
|
"""
|
|
|
|
self._xpos = x
|
|
self._ypos = y
|
|
|
|
|
|
def SetHeight(self, h):
|
|
"""
|
|
Sets the header/footer item height, in pixels.
|
|
|
|
:param `h`: an integer value representing the header/footer height.
|
|
"""
|
|
|
|
self._height = h
|
|
|
|
|
|
def SetWidth(self, w):
|
|
"""
|
|
Sets the header/footer item width, in pixels.
|
|
|
|
:param `w`: an integer value representing the header/footer width.
|
|
"""
|
|
|
|
self._width = w
|
|
|
|
if self._width < 0:
|
|
self._width = WIDTH_COL_DEFAULT
|
|
elif self._width < WIDTH_COL_MIN:
|
|
self._width = WIDTH_COL_MIN
|
|
|
|
|
|
def SetFormat(self, format):
|
|
"""
|
|
Sets the header item format.
|
|
|
|
:param `format`: the header item format.
|
|
"""
|
|
|
|
self._format = format
|
|
|
|
|
|
def SetFooterFormat(self, format):
|
|
"""
|
|
Sets the footer item format.
|
|
|
|
:param `format`: the footer item format.
|
|
"""
|
|
|
|
self._footerFormat = format
|
|
|
|
|
|
def HasImage(self):
|
|
"""
|
|
Returns ``True`` if the header item has at least one image associated
|
|
with it.
|
|
"""
|
|
|
|
return len(self._image) > 0
|
|
|
|
|
|
def HasFooterImage(self):
|
|
"""
|
|
Returns ``True`` if the footer item has at least one image associated
|
|
with it.
|
|
"""
|
|
|
|
return len(self._footerImage) > 0
|
|
|
|
|
|
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 ((x >= self._xpos) and (x <= self._xpos+self._width) and (y >= self._ypos) and (y <= self._ypos+self._height))
|
|
|
|
|
|
def GetItem(self, item):
|
|
"""
|
|
Returns information about the item.
|
|
|
|
:param `item`: an instance of :class:`UltimateListHeaderData`.
|
|
"""
|
|
|
|
item._mask = self._mask
|
|
item._text = self._text
|
|
item._tooltip = self._tooltip
|
|
item._image = self._image[:]
|
|
item._format = self._format
|
|
item._width = self._width
|
|
if self._font:
|
|
item._font = self._font
|
|
item.Attributes().SetFont(self._font)
|
|
|
|
item._kind = self._kind
|
|
item._checked = self._checked
|
|
item._state = self._state
|
|
item._isColumnShown = self._isColumnShown
|
|
|
|
item._footerImage = self._footerImage
|
|
item._footerFormat = self._footerFormat
|
|
item._footerText = self._footerText
|
|
item._footerKind = self._footerKind
|
|
item._footerChecked = self._footerChecked
|
|
item._footerFont = self._footerFont
|
|
item._customRenderer = self._customRenderer
|
|
|
|
return item
|
|
|
|
|
|
def GetState(self):
|
|
"""
|
|
Returns a bit field representing the state of the item.
|
|
|
|
:see: :meth:`~UltimateListHeaderData.SetState` for a list of valid item states.
|
|
"""
|
|
|
|
return self._state
|
|
|
|
|
|
def GetImage(self):
|
|
"""
|
|
Returns a Python list with the zero-based indexes of the images associated
|
|
with the header item into the image list.
|
|
"""
|
|
|
|
return self._image
|
|
|
|
|
|
def GetFooterImage(self):
|
|
"""
|
|
Returns a Python list with the zero-based indexes of the images associated
|
|
with the footer item into the image list.
|
|
"""
|
|
|
|
return self._footerImage
|
|
|
|
|
|
def GetWidth(self):
|
|
""" Returns the header/footer item width, in pixels. """
|
|
|
|
return self._width
|
|
|
|
|
|
def GetFormat(self):
|
|
""" Returns the header item format. """
|
|
|
|
return self._format
|
|
|
|
|
|
def GetFooterFormat(self):
|
|
""" Returns the footer item format. """
|
|
|
|
return self._footerFormat
|
|
|
|
|
|
def SetFont(self, font):
|
|
"""
|
|
Sets a new font for the header item.
|
|
|
|
:param `font`: an instance of :class:`wx.Font`.
|
|
"""
|
|
|
|
self._font = font
|
|
|
|
|
|
def SetFooterFont(self, font):
|
|
"""
|
|
Sets a new font for the footer item.
|
|
|
|
:param `font`: an instance of :class:`wx.Font`.
|
|
"""
|
|
|
|
self._footerFont = font
|
|
|
|
|
|
def SetKind(self, kind):
|
|
"""
|
|
Sets the header 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
|
|
|
|
|
|
def SetFooterKind(self, kind):
|
|
"""
|
|
Sets the footer item kind.
|
|
|
|
:param `kind`: the footer item kind.
|
|
|
|
:see: :meth:`~UltimateListHeaderData.SetKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
self._footerKind = kind
|
|
|
|
|
|
def GetKind(self):
|
|
"""
|
|
Returns the header item kind.
|
|
|
|
:see: :meth:`~UltimateListHeaderData.SetKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
return self._kind
|
|
|
|
|
|
def GetFooterKind(self):
|
|
"""
|
|
Returns the footer item kind.
|
|
|
|
:see: :meth:`~UltimateListHeaderData.SetKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
return self._footerKind
|
|
|
|
|
|
def IsChecked(self):
|
|
""" Returns whether the header item is checked or not. """
|
|
|
|
return self._checked
|
|
|
|
|
|
def Check(self, checked=True):
|
|
"""
|
|
Checks/unchecks a header item.
|
|
|
|
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
|
|
|
|
:note: This method is meaningful only for check and radio header items.
|
|
"""
|
|
|
|
self._checked = checked
|
|
|
|
|
|
def IsFooterChecked(self):
|
|
""" Returns whether the footer item is checked or not. """
|
|
|
|
return self._footerChecked
|
|
|
|
|
|
def CheckFooter(self, check=True):
|
|
"""
|
|
Checks/unchecks a footer item.
|
|
|
|
:param `checked`: ``True`` to check an item, ``False`` to uncheck it.
|
|
|
|
:note: This method is meaningful only for check and radio footer items.
|
|
"""
|
|
|
|
self._footerChecked = check
|
|
|
|
|
|
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 `DrawHeaderButton`
|
|
and `GetForegroundColor`.
|
|
"""
|
|
|
|
self._mask |= ULC_MASK_RENDERER
|
|
self._customRenderer = renderer
|
|
|
|
|
|
def GetCustomRenderer(self):
|
|
""" Returns the custom renderer associated with this item (if any). """
|
|
|
|
return self._customRenderer
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# GeometryInfo (internal)
|
|
# this is not used in report view
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class GeometryInfo(object):
|
|
"""
|
|
A simple class which holds items geometries for :class:`UltimateListCtrl` not in
|
|
report mode.
|
|
"""
|
|
|
|
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
|
|
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)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListLineData(object):
|
|
""" A simple class which holds line geometries for :class:`UltimateListCtrl`. """
|
|
|
|
def __init__(self, owner):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `owner`: an instance of :class:`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)
|
|
|
|
|
|
def SetReportView(self, inReportView):
|
|
"""
|
|
Sets whether :class:`UltimateListLineData` is in report view or not.
|
|
|
|
:param `inReportView`: ``True`` to set :class:`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()
|
|
|
|
|
|
def GetHeight(self):
|
|
""" Returns the line height, in pixels. """
|
|
|
|
return self._height
|
|
|
|
|
|
def SetHeight(self, height):
|
|
"""
|
|
Sets the line height.
|
|
|
|
:param `height`: the new line height.
|
|
"""
|
|
|
|
self._height = height
|
|
|
|
|
|
def GetWidth(self):
|
|
""" Returns the line width. """
|
|
|
|
return self._width
|
|
|
|
|
|
def SetWidth(self, width):
|
|
"""
|
|
Sets the line width.
|
|
|
|
:param `width`: the new line width.
|
|
"""
|
|
|
|
self._width = width
|
|
|
|
|
|
def GetX(self):
|
|
""" Returns the line `x` position. """
|
|
|
|
return self._x
|
|
|
|
|
|
def SetX(self, x):
|
|
"""
|
|
Sets the line `x` position.
|
|
|
|
:param `x`: the new line `x` position.
|
|
"""
|
|
|
|
self._x = x
|
|
|
|
|
|
def GetY(self):
|
|
""" Returns the line `y` position. """
|
|
|
|
return self._y
|
|
|
|
|
|
def SetY(self, y):
|
|
"""
|
|
Sets the line `y` position.
|
|
|
|
:param `y`: the new line `y` position.
|
|
"""
|
|
|
|
self._y = y
|
|
|
|
|
|
def ResetDimensions(self):
|
|
""" Resets the line dimensions (client rectangle). """
|
|
|
|
self._height = self._width = self._x = self._y = -1
|
|
|
|
|
|
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) != []
|
|
|
|
|
|
def HasText(self):
|
|
"""
|
|
Returns ``True`` if the text of first item in the line is not the empty
|
|
string.
|
|
"""
|
|
|
|
return self.GetText(0) != ""
|
|
|
|
|
|
def IsHighlighted(self):
|
|
""" Returns ``True`` if the line is highlighted. """
|
|
|
|
if self.IsVirtual():
|
|
raise Exception("unexpected call to IsHighlighted")
|
|
|
|
return self._highlighted
|
|
|
|
|
|
def GetMode(self):
|
|
""" Returns the current highlighting mode. """
|
|
|
|
return self._owner.GetListCtrl().GetAGWWindowStyleFlag() & ULC_MASK_TYPE
|
|
|
|
|
|
def HasMode(self, mode):
|
|
"""
|
|
Returns ``True`` if the parent :class:`UltimateListCtrl` has the window
|
|
style specified by `mode`.
|
|
|
|
:param `mode`: the window style to check.
|
|
"""
|
|
|
|
return self._owner.GetListCtrl().HasAGWFlag(mode)
|
|
|
|
|
|
def InReportView(self):
|
|
""" Returns ``True`` if the parent :class:`UltimateListCtrl` is in report view. """
|
|
|
|
return self._owner.HasAGWFlag(ULC_REPORT)
|
|
|
|
|
|
def IsVirtual(self):
|
|
""" Returns ``True`` if the parent :class:`UltimateListCtrl` has the ``ULC_VIRTUAL`` style set. """
|
|
|
|
return self._owner.IsVirtual()
|
|
|
|
|
|
def CalculateSize(self, dc, spacing):
|
|
"""
|
|
Calculates the line size and item positions.
|
|
|
|
:param `dc`: an instance of :class:`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")
|
|
|
|
|
|
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")
|
|
|
|
|
|
def InitItems(self, num):
|
|
"""
|
|
Initializes the list of items.
|
|
|
|
:param `num`: the initial number of items to store.
|
|
"""
|
|
|
|
for i in range(num):
|
|
self._items.append(UltimateListItemData(self._owner))
|
|
|
|
|
|
def SetItem(self, index, info):
|
|
"""
|
|
Sets information about the item.
|
|
|
|
:param `index`: the index of the item;
|
|
:param `info`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
item.SetItem(info)
|
|
|
|
|
|
def GetItem(self, index, info):
|
|
"""
|
|
Returns information about the item.
|
|
|
|
:param `index`: the index of the item;
|
|
:param `info`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
return item.GetItem(info)
|
|
|
|
|
|
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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetToolTip(self, index):
|
|
"""
|
|
Returns the item tooltip at the position `index`.
|
|
|
|
:param `index`: the index of the item.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
return item.GetToolTip()
|
|
|
|
|
|
def SetToolTip(self, index, s):
|
|
"""
|
|
Sets the item tooltip at the position `index`.
|
|
|
|
:param `index`: the index of the item;
|
|
:param `s`: the new item tooltip.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
item.SetToolTip(s)
|
|
|
|
|
|
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)
|
|
|
|
|
|
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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetKind(self, index=0):
|
|
"""
|
|
Returns the item kind.
|
|
|
|
:param `index`: the index of the item.
|
|
|
|
:see: :meth:`~UltimateListLineData.SetKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
return item.GetKind()
|
|
|
|
|
|
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()
|
|
|
|
|
|
def SetColour(self, index, c):
|
|
"""
|
|
Sets the text colour for the item.
|
|
|
|
:param `index`: the index of the item;
|
|
:param `c`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
item = self._items[index]
|
|
item.SetColour(c)
|
|
|
|
|
|
def GetAttr(self):
|
|
"""
|
|
Returns an instance of :class:`UltimateListItemAttr` associated with the first item
|
|
in the line.
|
|
"""
|
|
|
|
item = self._items[0]
|
|
return item.GetAttr()
|
|
|
|
|
|
def SetAttr(self, attr):
|
|
"""
|
|
Sets an instance of :class:`UltimateListItemAttr` to the first item in the line.
|
|
|
|
:param `attr`: an instance of :class:`UltimateListItemAttr`.
|
|
"""
|
|
|
|
item = self._items[0]
|
|
item.SetAttr(attr)
|
|
|
|
|
|
def SetAttributes(self, dc, attr, highlighted):
|
|
"""
|
|
Sets various attributes to the input device context.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `attr`: an instance of :class:`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
|
|
# arithmetic 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.BRUSHSTYLE_SOLID))
|
|
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def Draw(self, line, dc):
|
|
"""
|
|
Draws the line on the specified device context.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`;
|
|
:param `dc`: an instance of :class:`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.DrawRectangle(self._gi._rectHighlight)
|
|
|
|
else:
|
|
|
|
if borderOnly:
|
|
dc.SetBrush(wx.WHITE_BRUSH)
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.DrawRectangle(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.SetClippingRegion(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.Colour("orange")))
|
|
dc.DrawRoundedRectangle(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.DrawRoundedRectangle(r, 4)
|
|
|
|
|
|
def HideItemWindow(self, item):
|
|
"""
|
|
If the input item has a window associated with it, hide it.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
wnd = item.GetWindow()
|
|
if wnd and wnd.IsShown():
|
|
wnd.Hide()
|
|
|
|
|
|
def DrawInReportMode(self, dc, line, rect, rectHL, highlighted, current, enabled, oldPN, oldBR):
|
|
"""
|
|
Draws the line on the specified device context when the parent :class:`UltimateListCtrl`
|
|
is in report mode.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `line`: an instance of :class:`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 :class:`wx.Pen`, to save and restore at the end of
|
|
the drawing;
|
|
:param `oldBR`: an instance of :class:`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.DrawRectangle(paintRect)
|
|
|
|
else:
|
|
|
|
if borderOnly:
|
|
|
|
dc.SetBrush(wx.WHITE_BRUSH)
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.DrawRectangle(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.FONTWEIGHT_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.SetClippingRegion(itemRect)
|
|
|
|
if item.HasBackgroundColour():
|
|
dc.SetBrush(wx.Brush(item.GetBackgroundColour()))
|
|
dc.SetPen(wx.Pen(item.GetBackgroundColour()))
|
|
dc.DrawRectangle(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())
|
|
|
|
font = True
|
|
coloured = True
|
|
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.Colour("orange")))
|
|
dc.DrawRoundedRectangle(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.DrawRoundedRectangle(rect, 3)
|
|
dc.SetPen(oldPN)
|
|
|
|
|
|
def DrawTextFormatted(self, dc, text, row, col, itemRect, overflow):
|
|
"""
|
|
Draws the item text, correctly formatted.
|
|
|
|
:param `dc`: an instance of :class:`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.GetFullMultiLineTextExtent(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)
|
|
|
|
|
|
def DrawVerticalGradient(self, dc, rect, hasfocus):
|
|
"""
|
|
Gradient fill from colour 1 to colour 2 from top to bottom.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `rect`: the rectangle to be filled with the gradient shading;
|
|
:param `hasfocus`: ``True`` if the main :class:`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 range(rect.y, rect.y + rect.height):
|
|
currCol = (r1 + rf, g1 + gf, b1 + bf)
|
|
dc.SetBrush(wx.Brush(currCol, wx.BRUSHSTYLE_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.DrawRectangle(rect)
|
|
dc.SetBrush(oldbrush)
|
|
|
|
|
|
def DrawHorizontalGradient(self, dc, rect, hasfocus):
|
|
"""
|
|
Gradient fill from colour 1 to colour 2 from left to right.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `rect`: the rectangle to be filled with the gradient shading;
|
|
:param `hasfocus`: ``True`` if the main :class:`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 range(rect.x, rect.x + rect.width):
|
|
currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf))
|
|
dc.SetBrush(wx.Brush(currCol, wx.BRUSHSTYLE_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.DrawRectangle(rect)
|
|
dc.SetBrush(oldbrush)
|
|
|
|
|
|
def DrawVistaRectangle(self, dc, rect, hasfocus):
|
|
"""
|
|
Draws the selected item(s) with the Windows Vista style.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `rect`: the rectangle to be filled with the gradient shading;
|
|
:param `hasfocus`: ``True`` if the main :class:`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 range(filRect.y, filRect.y + filRect.height):
|
|
currCol = (r1 + rf, g1 + gf, b1 + bf)
|
|
dc.SetBrush(wx.Brush(currCol, wx.BRUSHSTYLE_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.DrawRoundedRectangle(bdrRect, 3)
|
|
bdrRect.Deflate(1, 1)
|
|
dc.SetPen(wx.Pen(inner))
|
|
dc.DrawRoundedRectangle(bdrRect, 2)
|
|
|
|
dc.SetPen(oldpen)
|
|
dc.SetBrush(oldbrush)
|
|
|
|
|
|
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
|
|
|
|
|
|
def ReverseHighlight(self):
|
|
"""
|
|
Reverses the line highlighting, switching it off if it was on and vice-versa.
|
|
"""
|
|
|
|
self.Highlight(not self.IsHighlighted())
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# UltimateListHeaderWindow (internal)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListHeaderWindow(wx.Control):
|
|
"""
|
|
This class holds the header window for :class:`UltimateListCtrl`.
|
|
"""
|
|
|
|
def __init__(self, win, id, owner, pos=wx.DefaultPosition,
|
|
size=wx.DefaultSize, style=0, validator=wx.DefaultValidator,
|
|
name="UltimateListCtrlcolumntitles", isFooter=False):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: parent window. Must not be ``None``;
|
|
:param `id`: window identifier. A value of -1 indicates a default value;
|
|
:param `owner`: an instance of :class:`UltimateListCtrl`;
|
|
: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 window style;
|
|
:param `validator`: the window validator;
|
|
:param `name`: the window name;
|
|
:param `isFooter`: ``True`` if the :class:`UltimateListHeaderWindow` is in a footer
|
|
position, ``False`` otherwise.
|
|
"""
|
|
|
|
wx.Control.__init__(self, win, id, pos, size, style|wx.NO_BORDER, validator, name)
|
|
|
|
self._isFooter = isFooter
|
|
self._owner = owner
|
|
self._currentCursor = wx.NullCursor
|
|
self._resizeCursor = wx.Cursor(wx.CURSOR_SIZEWE)
|
|
self._isDragging = False
|
|
self._headerHeight = None
|
|
self._footerHeight = None
|
|
|
|
# Custom renderer for every column
|
|
self._headerCustomRenderer = None
|
|
|
|
# column being resized or -1
|
|
self._column = -1
|
|
|
|
# divider line position in logical (unscrolled) coords
|
|
self._currentX = 0
|
|
|
|
# minimal position beyond which the divider line can't be dragged in
|
|
# logical coords
|
|
self._minX = 0
|
|
|
|
# needs refresh
|
|
self._dirty = False
|
|
self._hasFont = False
|
|
self._sendSetColumnWidth = False
|
|
self._colToSend = -1
|
|
self._widthToSend = 0
|
|
self._leftDown = False
|
|
self._enter = False
|
|
self._currentColumn = -1
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
|
|
self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
|
|
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
|
|
self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow)
|
|
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
|
|
|
|
if _USE_VISATTR:
|
|
|
|
attr = wx.Panel.GetClassDefaultAttributes()
|
|
self.SetOwnForegroundColour(attr.colFg)
|
|
self.SetOwnBackgroundColour(attr.colBg)
|
|
if not self._hasFont:
|
|
self.SetOwnFont(attr.font)
|
|
|
|
else:
|
|
|
|
self.SetOwnForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
|
self.SetOwnBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
|
|
|
if not self._hasFont:
|
|
self.SetOwnFont(wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT))
|
|
|
|
|
|
def SetCustomRenderer(self, renderer=None):
|
|
"""
|
|
Associate a custom renderer with the header - all columns will use it
|
|
|
|
:param `renderer`: a class able to correctly render header buttons
|
|
|
|
:note: the renderer class **must** implement the methods `DrawHeaderButton`
|
|
and `GetForegroundColor`.
|
|
"""
|
|
|
|
if not self._owner.HasAGWFlag(ULC_REPORT):
|
|
raise Exception("Custom renderers can be used on with style = ULC_REPORT")
|
|
|
|
self._headerCustomRenderer = renderer
|
|
|
|
|
|
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()`.
|
|
"""
|
|
|
|
if not self._isFooter:
|
|
if self._headerHeight is not None:
|
|
self.GetParent()._headerHeight = self._headerHeight
|
|
return wx.Size(200, self._headerHeight)
|
|
else:
|
|
if self._footerHeight is not None:
|
|
self.GetParent()._footerHeight = self._footerHeight
|
|
return wx.Size(200, self._footerHeight)
|
|
|
|
w, h, d, dummy = self.GetFullTextExtent("Hg")
|
|
maxH = self.GetTextHeight()
|
|
nativeH = wx.RendererNative.Get().GetHeaderButtonHeight(self.GetParent())
|
|
|
|
if not self._isFooter:
|
|
maxH = max(max(h, maxH), nativeH)
|
|
maxH += d
|
|
self.GetParent()._headerHeight = maxH
|
|
else:
|
|
maxH = max(h, nativeH)
|
|
maxH += d
|
|
self.GetParent()._footerHeight = maxH
|
|
|
|
return wx.Size(200, maxH)
|
|
|
|
|
|
def GetWindowHeight(self):
|
|
""" Returns the :class:`UltimateListHeaderWindow` height, in pixels. """
|
|
|
|
return self.DoGetBestSize()
|
|
|
|
|
|
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 column < 0 or column >= self._owner.GetColumnCount():
|
|
raise Exception("Invalid column")
|
|
|
|
return self._owner.IsColumnShown(column)
|
|
|
|
|
|
# shift the DC origin to match the position of the main window horz
|
|
# scrollbar: this allows us to always use logical coords
|
|
def AdjustDC(self, dc):
|
|
"""
|
|
Shifts the :class:`wx.DC` origin to match the position of the main window horizontal
|
|
scrollbar: this allows us to always use logical coordinates.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`.
|
|
"""
|
|
|
|
xpix, dummy = self._owner.GetScrollPixelsPerUnit()
|
|
x, dummy = self._owner.GetViewStart()
|
|
|
|
# account for the horz scrollbar offset
|
|
dc.SetDeviceOrigin(-x*xpix, 0)
|
|
|
|
|
|
def GetTextHeight(self):
|
|
""" Returns the column text height, in pixels. """
|
|
|
|
maxH = 0
|
|
numColumns = self._owner.GetColumnCount()
|
|
dc = wx.ClientDC(self)
|
|
for i in range(numColumns):
|
|
|
|
if not self.IsColumnShown(i):
|
|
continue
|
|
|
|
item = self._owner.GetColumn(i)
|
|
if item.GetFont().IsOk():
|
|
dc.SetFont(item.GetFont())
|
|
else:
|
|
dc.SetFont(self.GetFont())
|
|
|
|
wLabel, hLabel, dummy = dc.GetFullMultiLineTextExtent(item.GetText())
|
|
maxH = max(maxH, hLabel)
|
|
|
|
return maxH
|
|
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for :class:`UltimateListHeaderWindow`.
|
|
|
|
:param `event`: a :class:`PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.BufferedPaintDC(self)
|
|
# width and height of the entire header window
|
|
w, h = self.GetClientSize()
|
|
w, dummy = self._owner.CalcUnscrolledPosition(w, 0)
|
|
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)))
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.DrawRectangle(0, -1, w, h+2)
|
|
|
|
self.AdjustDC(dc)
|
|
|
|
dc.SetBackgroundMode(wx.TRANSPARENT)
|
|
dc.SetTextForeground(self.GetForegroundColour())
|
|
|
|
x = HEADER_OFFSET_X
|
|
|
|
numColumns = self._owner.GetColumnCount()
|
|
item = UltimateListItem()
|
|
renderer = wx.RendererNative.Get()
|
|
enabled = self.GetParent().IsEnabled()
|
|
virtual = self._owner.IsVirtual()
|
|
isFooter = self._isFooter
|
|
|
|
for i in range(numColumns):
|
|
|
|
# Reset anything in the dc that a custom renderer might have changed
|
|
dc.SetTextForeground(self.GetForegroundColour())
|
|
|
|
if x >= w:
|
|
break
|
|
|
|
if not self.IsColumnShown(i):
|
|
continue # do next column if not shown
|
|
|
|
item = self._owner.GetColumn(i)
|
|
wCol = item._width
|
|
|
|
cw = wCol
|
|
ch = h
|
|
|
|
flags = 0
|
|
if not enabled:
|
|
flags |= wx.CONTROL_DISABLED
|
|
|
|
# NB: The code below is not really Mac-specific, but since we are close
|
|
# to 2.8 release and I don't have time to test on other platforms, I
|
|
# defined this only for wxMac. If this behavior is desired on
|
|
# other platforms, please go ahead and revise or remove the #ifdef.
|
|
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
if not virtual and item._mask & ULC_MASK_STATE and item._state & ULC_STATE_SELECTED:
|
|
flags |= wx.CONTROL_SELECTED
|
|
|
|
if i == 0:
|
|
flags |= wx.CONTROL_SPECIAL # mark as first column
|
|
|
|
if i == self._currentColumn:
|
|
if self._leftDown:
|
|
flags |= wx.CONTROL_PRESSED
|
|
else:
|
|
if self._enter:
|
|
flags |= wx.CONTROL_CURRENT
|
|
|
|
# the width of the rect to draw: make it smaller to fit entirely
|
|
# inside the column rect
|
|
header_rect = wx.Rect(x-1, HEADER_OFFSET_Y-1, cw-1, ch)
|
|
|
|
if self._headerCustomRenderer is not None:
|
|
self._headerCustomRenderer.DrawHeaderButton(dc, header_rect, flags)
|
|
|
|
# The custom renderer will specify the color to draw the header text and buttons
|
|
dc.SetTextForeground(self._headerCustomRenderer.GetForegroundColour())
|
|
|
|
elif item._mask & ULC_MASK_RENDERER:
|
|
item.GetCustomRenderer().DrawHeaderButton(dc, header_rect, flags)
|
|
|
|
# The custom renderer will specify the color to draw the header text and buttons
|
|
dc.SetTextForeground(item.GetCustomRenderer().GetForegroundColour())
|
|
else:
|
|
renderer.DrawHeaderButton(self, dc, header_rect, flags)
|
|
|
|
|
|
# see if we have enough space for the column label
|
|
if isFooter:
|
|
if item.GetFooterFont().IsOk():
|
|
dc.SetFont(item.GetFooterFont())
|
|
else:
|
|
dc.SetFont(self.GetFont())
|
|
else:
|
|
if item.GetFont().IsOk():
|
|
dc.SetFont(item.GetFont())
|
|
else:
|
|
dc.SetFont(self.GetFont())
|
|
|
|
wcheck = hcheck = 0
|
|
kind = (isFooter and [item.GetFooterKind()] or [item.GetKind()])[0]
|
|
checked = (isFooter and [item.IsFooterChecked()] or [item.IsChecked()])[0]
|
|
|
|
if kind in [1, 2]:
|
|
# We got a checkbox-type item
|
|
ix, iy = self._owner.GetCheckboxImageSize()
|
|
# We draw it on the left, always
|
|
self._owner.DrawCheckbox(dc, x + HEADER_OFFSET_X, HEADER_OFFSET_Y + (h - 4 - iy)//2, kind, checked, enabled)
|
|
wcheck += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
|
|
cw -= ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
|
|
|
|
# for this we need the width of the text
|
|
text = (isFooter and [item.GetFooterText()] or [item.GetText()])[0]
|
|
wLabel, hLabel, dummy = dc.GetFullMultiLineTextExtent(text)
|
|
wLabel += 2*EXTRA_WIDTH
|
|
|
|
# and the width of the icon, if any
|
|
image = (isFooter and [item._footerImage] or [item._image])[0]
|
|
|
|
if image:
|
|
imageList = self._owner._small_image_list
|
|
if imageList:
|
|
for img in image:
|
|
if img >= 0:
|
|
ix, iy = imageList.GetSize(img)
|
|
wLabel += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
|
|
|
|
else:
|
|
|
|
imageList = None
|
|
|
|
# ignore alignment if there is not enough space anyhow
|
|
align = (isFooter and [item.GetFooterAlign()] or [item.GetAlign()])[0]
|
|
align = (wLabel < cw and [align] or [ULC_FORMAT_LEFT])[0]
|
|
|
|
if align == ULC_FORMAT_LEFT:
|
|
xAligned = x + wcheck
|
|
|
|
elif align == ULC_FORMAT_RIGHT:
|
|
xAligned = x + cw - wLabel - HEADER_OFFSET_X
|
|
|
|
elif align == ULC_FORMAT_CENTER:
|
|
xAligned = x + wcheck + (cw - wLabel)//2
|
|
|
|
# if we have an image, draw it on the right of the label
|
|
if imageList:
|
|
for indx, img in enumerate(image):
|
|
if img >= 0:
|
|
imageList.Draw(img, dc,
|
|
xAligned + wLabel - (ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE)*(indx+1),
|
|
HEADER_OFFSET_Y + (h - 4 - iy)//2,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT)
|
|
|
|
cw -= ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE
|
|
|
|
# draw the text clipping it so that it doesn't overwrite the column
|
|
# boundary
|
|
dc.SetClippingRegion(x, HEADER_OFFSET_Y, cw, h - 4)
|
|
self.DrawTextFormatted(dc, text, wx.Rect(xAligned+EXTRA_WIDTH, HEADER_OFFSET_Y, cw-EXTRA_WIDTH, h-4))
|
|
|
|
x += wCol
|
|
dc.DestroyClippingRegion()
|
|
|
|
# Fill in what's missing to the right of the columns, otherwise we will
|
|
# leave an unpainted area when columns are removed (and it looks better)
|
|
if x < w:
|
|
header_rect = wx.Rect(x, HEADER_OFFSET_Y, w - x, h)
|
|
if self._headerCustomRenderer is not None:
|
|
# Why does the custom renderer need this adjustment??
|
|
header_rect.x = header_rect.x - 1
|
|
header_rect.y = header_rect.y - 1
|
|
self._headerCustomRenderer.DrawHeaderButton(dc, header_rect, wx.CONTROL_SPECIAL)
|
|
else:
|
|
renderer.DrawHeaderButton(self, dc, header_rect, wx.CONTROL_SPECIAL) # mark as last column
|
|
|
|
|
|
def DrawTextFormatted(self, dc, text, rect):
|
|
"""
|
|
Draws the item text, correctly formatted.
|
|
|
|
:param `dc`: an instance of :class:`wx.DC`;
|
|
:param `text`: the item text;
|
|
:param `rect`: the item client rectangle.
|
|
"""
|
|
|
|
# determine if the string can fit inside the current width
|
|
w, h, dummy = dc.GetFullMultiLineTextExtent(text)
|
|
width = rect.width
|
|
|
|
if w <= width:
|
|
|
|
dc.DrawLabel(text, rect, wx.ALIGN_CENTER_VERTICAL)
|
|
|
|
else:
|
|
|
|
# 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()
|
|
dc.DrawLabel(theText, rect, wx.ALIGN_CENTER_VERTICAL)
|
|
|
|
|
|
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 :meth:`wx.Window.UpdateWindowUI` in order to send update events
|
|
to the window in idle time.
|
|
"""
|
|
|
|
wx.Control.OnInternalIdle(self)
|
|
|
|
if self._isFooter:
|
|
return
|
|
|
|
if self._sendSetColumnWidth:
|
|
self._owner.SetColumnWidth(self._colToSend, self._widthToSend)
|
|
self._sendSetColumnWidth = False
|
|
|
|
|
|
def DrawCurrent(self):
|
|
""" Force the redrawing of the column window. """
|
|
|
|
self._sendSetColumnWidth = True
|
|
self._colToSend = self._column
|
|
self._widthToSend = self._currentX - self._minX
|
|
|
|
|
|
def OnMouse(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MOUSE_EVENTS`` event for :class:`UltimateListHeaderWindow`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
# we want to work with logical coords
|
|
x, dummy = self._owner.CalcUnscrolledPosition(event.GetX(), 0)
|
|
y = event.GetY()
|
|
|
|
columnX, columnY = x, y
|
|
|
|
if self._isDragging:
|
|
|
|
self.SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition())
|
|
|
|
# we don't draw the line beyond our window, but we allow dragging it
|
|
# there
|
|
|
|
w, dummy = self.GetClientSize()
|
|
w, dummy = self._owner.CalcUnscrolledPosition(w, 0)
|
|
w -= 6
|
|
|
|
# erase the line if it was drawn
|
|
if self._currentX < w:
|
|
self.DrawCurrent()
|
|
|
|
if event.ButtonUp():
|
|
|
|
self.ReleaseMouse()
|
|
self._isDragging = False
|
|
self._dirty = True
|
|
self._owner.SetColumnWidth(self._column, self._currentX - self._minX)
|
|
self.SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition())
|
|
|
|
else:
|
|
|
|
if x > self._minX + 7:
|
|
self._currentX = x
|
|
else:
|
|
self._currentX = self._minX + 7
|
|
|
|
# draw in the new location
|
|
if self._currentX < w:
|
|
self.DrawCurrent()
|
|
|
|
else: # not dragging
|
|
|
|
self._minX = 0
|
|
hit_border = False
|
|
|
|
# end of the current column
|
|
xpos = 0
|
|
|
|
# find the column where this event occurred
|
|
countCol = self._owner.GetColumnCount()
|
|
broken = False
|
|
tipCol = -1
|
|
|
|
for col in range(countCol):
|
|
|
|
if not self.IsColumnShown(col):
|
|
continue
|
|
|
|
xpos += self._owner.GetColumnWidth(col)
|
|
self._column = col
|
|
|
|
if abs(x-xpos) < 3 and y < 22:
|
|
# near the column border
|
|
hit_border = True
|
|
broken = True
|
|
tipCol = col
|
|
break
|
|
|
|
if x < xpos:
|
|
# inside the column
|
|
broken = True
|
|
tipCol = col
|
|
break
|
|
|
|
self._minX = xpos
|
|
|
|
if not broken:
|
|
self._column = -1
|
|
|
|
if tipCol >= 0:
|
|
# First check to see if we have a tooltip to display
|
|
colItem = self._owner.GetColumn(col)
|
|
if colItem.GetToolTip() != "":
|
|
self.SetToolTip(colItem.GetToolTip())
|
|
else:
|
|
self.SetToolTip("")
|
|
|
|
if event.LeftUp():
|
|
self._leftDown = False
|
|
self.Refresh()
|
|
|
|
if event.LeftDown() or event.RightUp():
|
|
|
|
if hit_border and event.LeftDown():
|
|
|
|
if not self._isFooter:
|
|
|
|
if self.SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
|
|
event.GetPosition()):
|
|
|
|
self._isDragging = True
|
|
self._currentX = x
|
|
self.CaptureMouse()
|
|
self.DrawCurrent()
|
|
|
|
#else: column resizing was vetoed by the user code
|
|
|
|
else: # click on a column
|
|
|
|
# record the selected state of the columns
|
|
if event.LeftDown():
|
|
|
|
for i in range(self._owner.GetColumnCount()):
|
|
|
|
if not self.IsColumnShown(col):
|
|
continue
|
|
|
|
colItem = self._owner.GetColumn(i)
|
|
state = colItem.GetState()
|
|
|
|
if i == self._column:
|
|
colItem.SetState(state | ULC_STATE_SELECTED)
|
|
theX = x
|
|
else:
|
|
colItem.SetState(state & ~ULC_STATE_SELECTED)
|
|
|
|
self._leftDown = True
|
|
self._owner.SetColumn(i, colItem)
|
|
x += self._owner.GetColumnWidth(i)
|
|
|
|
if self.HandleColumnCheck(self._column, event.GetPosition()):
|
|
return
|
|
|
|
if not self._isFooter:
|
|
self.SendListEvent((event.LeftDown() and [wxEVT_COMMAND_LIST_COL_CLICK] or \
|
|
[wxEVT_COMMAND_LIST_COL_RIGHT_CLICK])[0], event.GetPosition())
|
|
else:
|
|
self.SendListEvent((event.LeftDown() and [wxEVT_COMMAND_LIST_FOOTER_CLICK] or \
|
|
[wxEVT_COMMAND_LIST_FOOTER_RIGHT_CLICK])[0], event.GetPosition())
|
|
|
|
self._leftDown = True
|
|
self._currentColumn = self._column
|
|
|
|
elif event.Moving():
|
|
|
|
setCursor = False
|
|
|
|
if not self._isFooter:
|
|
if hit_border:
|
|
|
|
setCursor = self._currentCursor == wx.STANDARD_CURSOR
|
|
self._currentCursor = self._resizeCursor
|
|
|
|
else:
|
|
|
|
setCursor = self._currentCursor != wx.STANDARD_CURSOR
|
|
self._currentCursor = wx.STANDARD_CURSOR
|
|
|
|
if setCursor:
|
|
self.SetCursor(self._currentCursor)
|
|
else:
|
|
column = self.HitTestColumn(columnX, columnY)
|
|
self._enter = True
|
|
self._currentColumn = column
|
|
|
|
if _VERSION_STRING < "2.9":
|
|
leftDown = wx.GetMouseState().LeftDown()
|
|
else:
|
|
leftDown = wx.GetMouseState().LeftIsDown()
|
|
|
|
self._leftDown = leftDown
|
|
|
|
self.Refresh()
|
|
|
|
elif event.ButtonDClick():
|
|
|
|
self.HandleColumnCheck(self._column, event.GetPosition())
|
|
|
|
|
|
def HandleColumnCheck(self, column, pos):
|
|
"""
|
|
Handles the case in which a column contains a checkbox-like item.
|
|
|
|
:param `column`: the column index;
|
|
:param `pos`: the mouse position.
|
|
"""
|
|
|
|
if column < 0 or column >= self._owner.GetColumnCount():
|
|
return False
|
|
|
|
colItem = self._owner.GetColumn(column)
|
|
# Let's see if it is a checkbox-type item
|
|
|
|
kind = (self._isFooter and [colItem.GetFooterKind()] or [colItem.GetKind()])[0]
|
|
if kind not in [1, 2]:
|
|
return False
|
|
|
|
x = HEADER_OFFSET_X
|
|
|
|
for i in range(self._owner.GetColumnCount()):
|
|
|
|
if not self.IsColumnShown(i):
|
|
continue
|
|
|
|
if i == self._column:
|
|
theX = x
|
|
break
|
|
x += self._owner.GetColumnWidth(i)
|
|
|
|
parent = self.GetParent()
|
|
|
|
w, h = self.GetClientSize()
|
|
ix, iy = self._owner.GetCheckboxImageSize()
|
|
rect = wx.Rect(theX + HEADER_OFFSET_X, HEADER_OFFSET_Y + (h - 4 - iy)//2, ix, iy)
|
|
|
|
if rect.Contains(pos):
|
|
# User clicked on the checkbox
|
|
evt = (self._isFooter and [wxEVT_COMMAND_LIST_FOOTER_CHECKING] or [wxEVT_COMMAND_LIST_COL_CHECKING])[0]
|
|
if self.SendListEvent(evt, pos):
|
|
# No veto for the item checking
|
|
if self._isFooter:
|
|
isChecked = colItem.IsFooterChecked()
|
|
colItem.CheckFooter(not isChecked)
|
|
else:
|
|
isChecked = colItem.IsChecked()
|
|
colItem.Check(not isChecked)
|
|
|
|
self._owner.SetColumn(column, colItem)
|
|
self.RefreshRect(rect)
|
|
|
|
if self._isFooter:
|
|
return True
|
|
|
|
if parent.HasAGWFlag(ULC_AUTO_CHECK_CHILD):
|
|
self._owner.AutoCheckChild(isChecked, self._column)
|
|
elif parent.HasAGWFlag(ULC_AUTO_TOGGLE_CHILD):
|
|
self._owner.AutoToggleChild(self._column)
|
|
|
|
evt = (self._isFooter and [wxEVT_COMMAND_LIST_FOOTER_CHECKED] or [wxEVT_COMMAND_LIST_COL_CHECKED])[0]
|
|
self.SendListEvent(evt, pos)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def OnEnterWindow(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ENTER_WINDOW`` event for :class:`UltimateListHeaderWindow`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
x, y = self._owner.CalcUnscrolledPosition(*self.ScreenToClient(wx.GetMousePosition()))
|
|
column = self.HitTestColumn(x, y)
|
|
|
|
if _VERSION_STRING < "2.9":
|
|
leftDown = wx.GetMouseState().LeftDown()
|
|
else:
|
|
leftDown = wx.GetMouseState().LeftIsDown()
|
|
|
|
self._leftDown = leftDown
|
|
self._enter = column >= 0 and column < self._owner.GetColumnCount()
|
|
self._currentColumn = column
|
|
self.Refresh()
|
|
|
|
|
|
def OnLeaveWindow(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`UltimateListHeaderWindow`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
self._enter = False
|
|
self._leftDown = False
|
|
|
|
self._currentColumn = -1
|
|
self.Refresh()
|
|
|
|
|
|
def HitTestColumn(self, x, y):
|
|
"""
|
|
HitTest method for column headers.
|
|
|
|
:param `x`: the mouse `x` position;
|
|
:param `y`: the mouse `y` position.
|
|
|
|
:return: The column index if any column client rectangle contains the mouse
|
|
position, ``wx.NOT_FOUND`` otherwise.
|
|
"""
|
|
|
|
xOld = 0
|
|
|
|
for i in range(self._owner.GetColumnCount()):
|
|
if not self.IsColumnShown(i):
|
|
continue
|
|
|
|
xOld += self._owner.GetColumnWidth(i)
|
|
if x <= xOld:
|
|
return i
|
|
|
|
return -1
|
|
|
|
|
|
def OnSetFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SET_FOCUS`` event for :class:`UltimateListHeaderWindow`.
|
|
|
|
:param `event`: a :class:`FocusEvent` event to be processed.
|
|
"""
|
|
|
|
self._owner.SetFocusIgnoringChildren()
|
|
self._owner.Update()
|
|
|
|
|
|
def SendListEvent(self, eventType, pos):
|
|
"""
|
|
Sends a :class:`UltimateListEvent` for the parent window.
|
|
|
|
:param `eventType`: the event type;
|
|
:param `pos`: an instance of :class:`wx.Point`.
|
|
"""
|
|
|
|
parent = self.GetParent()
|
|
le = UltimateListEvent(eventType, parent.GetId())
|
|
le.SetEventObject(parent)
|
|
le.m_pointDrag = pos
|
|
|
|
# the position should be relative to the parent window, not
|
|
# this one for compatibility with MSW and common sense: the
|
|
# user code doesn't know anything at all about this header
|
|
# window, so why should it get positions relative to it?
|
|
le.m_pointDrag.y -= self.GetSize().y
|
|
|
|
le.m_col = self._column
|
|
return (not parent.GetEventHandler().ProcessEvent(le) or le.IsAllowed())
|
|
|
|
|
|
def GetOwner(self):
|
|
""" Returns the header window owner, an instance of :class:`UltimateListCtrl`. """
|
|
|
|
return self._owner
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# UltimateListRenameTimer (internal)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListRenameTimer(wx.Timer):
|
|
""" Timer used for enabling in-place edit. """
|
|
|
|
def __init__(self, owner):
|
|
"""
|
|
Default class constructor.
|
|
For internal use: do not call it in your code!
|
|
|
|
:param `owner`: an instance of :class:`UltimateListCtrl`.
|
|
"""
|
|
|
|
wx.Timer.__init__(self)
|
|
self._owner = owner
|
|
|
|
|
|
def Notify(self):
|
|
""" The timer has expired. """
|
|
|
|
self._owner.OnRenameTimer()
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# UltimateListTextCtrl (internal)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListTextCtrl(ExpandoTextCtrl):
|
|
"""
|
|
Control used for in-place edit.
|
|
|
|
This is a subclass of :class:`~wx.lib.expando.ExpandoTextCtrl` as :class:`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 :class:`UltimateListCtrl` to finish
|
|
the editing and ``Ctrl`` + ``Enter`` is consumed by the platform for tab navigation.
|
|
"""
|
|
|
|
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 :class:`UltimateListCtrl` );
|
|
:param `itemEdit`: an instance of :class:`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)
|
|
|
|
|
|
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
|
|
|
|
|
|
def Finish(self):
|
|
""" Finish editing. """
|
|
|
|
try:
|
|
if not self._finished:
|
|
self._finished = True
|
|
self._owner.SetFocusIgnoringChildren()
|
|
self._owner.ResetTextControl()
|
|
except RuntimeError:
|
|
return
|
|
|
|
|
|
def OnChar(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_CHAR`` event for :class:`UltimateListTextCtrl`.
|
|
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
def OnKeyUp(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KEY_UP`` event for :class:`UltimateListTextCtrl`.
|
|
|
|
:param `event`: a :class:`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.GetFullMultiLineTextExtent(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()
|
|
|
|
|
|
def OnKillFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`UltimateListTextCtrl`.
|
|
|
|
:param `event`: a :class:`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)
|
|
|
|
|
|
def StopEditing(self):
|
|
""" Suddenly stops the editing. """
|
|
|
|
self._owner.OnRenameCancelled(self._itemEdited)
|
|
self.Finish()
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# UltimateListMainWindow (internal)
|
|
#-----------------------------------------------------------------------------
|
|
|
|
class UltimateListMainWindow(wx.ScrolledWindow):
|
|
"""
|
|
This is the main widget implementation of :class:`UltimateListCtrl`.
|
|
"""
|
|
|
|
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 :class:`ScrolledWindow` 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 :class:`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 :meth:`ListCtrl.SortItems`.)
|
|
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in :meth:`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 when 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).
|
|
``ULC_NO_ITEM_DRAG`` 0x200000000 Disable item dragging
|
|
=============================== =========== ====================================================================================================
|
|
|
|
:param `name`: the window name.
|
|
"""
|
|
|
|
wx.ScrolledWindow.__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.BRUSHSTYLE_SOLID)
|
|
|
|
btnshadow = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)
|
|
self._highlightUnfocusedBrush = wx.Brush(btnshadow, wx.BRUSHSTYLE_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)
|
|
|
|
|
|
def Init(self):
|
|
""" Initializes the :class:`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.FONTWEIGHT_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.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 1)
|
|
image.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 1)
|
|
|
|
# make the image into a cursor
|
|
self._dragCursor = wx.Cursor(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
|
|
|
|
|
|
def GetMainWindowOfCompositeControl(self):
|
|
""" Returns the :class:`UltimateListMainWindow` parent. """
|
|
|
|
return self.GetParent()
|
|
|
|
|
|
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)
|
|
|
|
|
|
def HasAGWFlag(self, flag):
|
|
"""
|
|
Returns ``True`` if the window has the given `flag` bit set.
|
|
|
|
:param `flag`: the bit to check.
|
|
|
|
:see: :meth:`UltimateListCtrl.SetSingleStyle() <UltimateListCtrl.SetSingleStyle>` for a list of valid flags.
|
|
"""
|
|
|
|
return self._parent.HasAGWFlag(flag)
|
|
|
|
|
|
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
|
|
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
|
|
def InReportView(self):
|
|
""" Returns ``True`` if the window is in report mode. """
|
|
|
|
return self.HasAGWFlag(ULC_REPORT)
|
|
|
|
|
|
def InTileView(self):
|
|
"""
|
|
Returns ``True`` if the window is in tile mode (partially implemented).
|
|
|
|
.. todo:: Fully implement tile view for :class:`UltimateListCtrl`.
|
|
"""
|
|
|
|
return self.HasAGWFlag(ULC_TILE)
|
|
|
|
# return True if we are in single selection mode, False if multi sel
|
|
def IsSingleSel(self):
|
|
""" Returns ``True`` if we are in single selection mode, ``False`` if multi selection. """
|
|
|
|
return self.HasAGWFlag(ULC_SINGLE_SEL)
|
|
|
|
|
|
def HasFocus(self):
|
|
""" Returns ``True`` if the window has focus. """
|
|
|
|
return self._hasFocus
|
|
|
|
|
|
# do we have a header window?
|
|
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?
|
|
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
|
|
def ReverseHighlight(self, line):
|
|
"""
|
|
Toggles the line state and refreshes it.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`.
|
|
"""
|
|
|
|
self.HighlightLine(line, not self.IsHighlighted(line))
|
|
self.RefreshLine(line)
|
|
|
|
|
|
def SetUserLineHeight(self, height):
|
|
"""
|
|
Sets a custom value for the :class:`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.")
|
|
|
|
|
|
def GetUserLineHeight(self):
|
|
"""
|
|
Returns the custom value for the :class:`UltimateListMainWindow` item height, if previously set with
|
|
:meth:`~UltimateListMainWindow.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
|
|
def GetLineSize(self, line):
|
|
"""
|
|
Returns the size of the total line client rectangle.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`.
|
|
"""
|
|
|
|
return self.GetLineRect(line).GetSize()
|
|
|
|
|
|
# bring the current item into view
|
|
def MoveToFocus(self):
|
|
""" Brings the current item into view. """
|
|
|
|
self.MoveToItem(self._current)
|
|
|
|
|
|
def GetColumnCount(self):
|
|
""" Returns the total number of columns in the :class:`UltimateListCtrl`. """
|
|
|
|
return len(self._columns)
|
|
|
|
|
|
def GetItemText(self, item):
|
|
"""
|
|
Returns the item text.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._mask = ULC_MASK_TEXT
|
|
info._itemId = item
|
|
info = self.GetItem(info)
|
|
|
|
return info._text
|
|
|
|
|
|
def SetItemText(self, item, value):
|
|
"""
|
|
Sets the item text.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`;
|
|
:param `value`: the new item text.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._mask = ULC_MASK_TEXT
|
|
info._itemId = item
|
|
info._text = value
|
|
|
|
self.SetItem(info)
|
|
|
|
|
|
def IsEmpty(self):
|
|
""" Returns ``True`` if the window has no items in it. """
|
|
|
|
return self.GetItemCount() == 0
|
|
|
|
|
|
def ResetCurrent(self):
|
|
""" Resets the current item to ``None``. """
|
|
|
|
self.ChangeCurrent(-1)
|
|
|
|
|
|
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
|
|
def SetFont(self, font):
|
|
"""
|
|
Overridden base class virtual to reset the line height when the font changes.
|
|
|
|
:param `font`: a valid :class:`wx.Font` object.
|
|
|
|
:note: Overridden from :class:`ScrolledWindow`.
|
|
"""
|
|
|
|
if not wx.ScrolledWindow.SetFont(self, font):
|
|
return False
|
|
|
|
self._lineHeight = 0
|
|
self.ResetLineDimensions()
|
|
|
|
return True
|
|
|
|
|
|
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 range(self.GetItemCount()):
|
|
line = self.GetLine(l)
|
|
line.ResetDimensions()
|
|
|
|
# these are for UltimateListLineData usage only
|
|
# get the backpointer to the list ctrl
|
|
def GetListCtrl(self):
|
|
""" Returns the parent widget, an instance of :class:`UltimateListCtrl`. """
|
|
|
|
return self.GetParent()
|
|
|
|
|
|
# get the brush to use for the item highlighting
|
|
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
|
|
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
|
|
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
|
|
def ResizeColumns(self):
|
|
"""
|
|
If ``ULC_AUTOSIZE_FILL`` was passed to :meth:`UltimateListCtrl.SetColumnWidth() <UltimateListCtrl.SetColumnWidth>` then
|
|
that column's width will be expanded to fill the window on a resize event.
|
|
|
|
Called by :meth:`UltimateListCtrl.OnSize() <UltimateListCtrl.OnSize>` when the window is resized.
|
|
"""
|
|
|
|
if not self: # Avoid RuntimeError on Mac
|
|
return
|
|
|
|
if self._resizeColumn == -1:
|
|
return
|
|
|
|
|
|
numCols = self.GetColumnCount()
|
|
if numCols == 0: return # Nothing to resize.
|
|
|
|
resizeCol = self._resizeColumn
|
|
|
|
if self._resizeColMinWidth is 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
|
|
def GetRuleColour(self):
|
|
""" Returns the colour to be used for drawing the horizontal and vertical rules. """
|
|
|
|
return wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT)
|
|
|
|
|
|
def SetReportView(self, inReportView):
|
|
"""
|
|
Sets whether :class:`UltimateListCtrl` is in report view or not.
|
|
|
|
:param `inReportView`: ``True`` to set :class:`UltimateListCtrl` in report view, ``False``
|
|
otherwise.
|
|
"""
|
|
|
|
for line in self._lines:
|
|
line.SetReportView(inReportView)
|
|
|
|
|
|
def CacheLineData(self, line):
|
|
"""
|
|
Saves the current line attributes.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`.
|
|
|
|
:note: This method is used only if the :class:`UltimateListCtrl` has the ``ULC_VIRTUAL``
|
|
style set.
|
|
"""
|
|
|
|
listctrl = self.GetListCtrl()
|
|
ld = self.GetDummyLine()
|
|
|
|
countCol = self.GetColumnCount()
|
|
for col in range(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))
|
|
|
|
|
|
def GetDummyLine(self):
|
|
"""
|
|
Returns a dummy line.
|
|
|
|
:note: This method is used only if the :class:`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)
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def GetLineHeight(self, item=None):
|
|
"""
|
|
Returns the line height for a specific item.
|
|
|
|
:param `item`: if not ``None``, an instance of :class:`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.GetFullMultiLineTextExtent(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
|
|
|
|
|
|
def GetLineY(self, line):
|
|
"""
|
|
Returns the line `y` position.
|
|
|
|
:param `line`: an instance of :class:`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 range(line):
|
|
lineY += self.GetLineHeight(l)
|
|
|
|
lineItem.SetY(LINE_SPACING + lineY)
|
|
return LINE_SPACING + lineY
|
|
|
|
|
|
def GetLineRect(self, line):
|
|
"""
|
|
Returns the line client rectangle.
|
|
|
|
:param `line`: an instance of :class:`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
|
|
|
|
|
|
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 :class:`UltimateListLineData`.
|
|
"""
|
|
|
|
if not self.InReportView():
|
|
return self.GetLine(line)._gi._rectLabel
|
|
|
|
image_x = 0
|
|
image_width = 0
|
|
|
|
for c in range(col):
|
|
if self.IsColumnShown(c):
|
|
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
|
|
|
|
|
|
def GetLineIconRect(self, line):
|
|
"""
|
|
Returns the line client rectangle for the item image only.
|
|
|
|
:param `line`: an instance of :class:`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
|
|
|
|
|
|
def GetLineCheckboxRect(self, line):
|
|
"""
|
|
Returns the line client rectangle for the item checkbox image only.
|
|
|
|
:param `line`: an instance of :class:`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
|
|
|
|
|
|
def GetLineHighlightRect(self, line):
|
|
"""
|
|
Returns the line client rectangle when the line is highlighted.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`.
|
|
"""
|
|
|
|
return (self.InReportView() and [self.GetLineRect(line)] or [self.GetLine(line)._gi._rectHighlight])[0]
|
|
|
|
|
|
def HitTestLine(self, line, x, y):
|
|
"""
|
|
HitTest method for a :class:`UltimateListCtrl` line.
|
|
|
|
:param `line`: an instance of :class:`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_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)
|
|
label_rect = self.GetLineLabelRect(line, col)
|
|
rect = wx.Rect(xOld+start, lineY, min(end, label_rect.width), 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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def IsHighlighted(self, line):
|
|
"""
|
|
Returns ``True`` if the input line is highlighted.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`.
|
|
"""
|
|
|
|
if self.IsVirtual():
|
|
|
|
return self._selStore.IsSelected(line)
|
|
|
|
else: # !virtual
|
|
|
|
ld = self.GetLine(line)
|
|
return ld.IsHighlighted()
|
|
|
|
|
|
def HighlightLines(self, lineFrom, lineTo, highlight=True):
|
|
"""
|
|
Highlights a range of lines in :class:`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 range(len(linesChanged)):
|
|
self.RefreshLine(linesChanged[n])
|
|
|
|
else: # iterate over all items in non report view
|
|
|
|
for line in range(lineFrom, lineTo+1):
|
|
if self.HighlightLine(line, highlight):
|
|
self.RefreshLine(line)
|
|
|
|
|
|
def HighlightLine(self, line, highlight=True):
|
|
"""
|
|
Highlights a line in :class:`UltimateListCtrl`.
|
|
|
|
:param `line`: an instance of :class:`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)
|
|
command = (highlight and [wxEVT_COMMAND_LIST_ITEM_SELECTED] or [wxEVT_COMMAND_LIST_ITEM_DESELECTED])[0]
|
|
|
|
if changed and not dontNotify:
|
|
col = -1
|
|
if command==wxEVT_COMMAND_LIST_ITEM_SELECTED and wx.GetTopLevelParent(self).IsShown():
|
|
x, y = self.ScreenToClient(wx.GetMousePosition())
|
|
newItem, hitResult = self.HitTestLine(line, x, y)
|
|
col=newItem._col if newItem else -1
|
|
|
|
self.SendNotify(line, command, col=col)
|
|
|
|
return changed
|
|
|
|
|
|
def RefreshLine(self, line):
|
|
"""
|
|
Redraws the input line.
|
|
|
|
:param `line`: an instance of :class:`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)
|
|
|
|
|
|
def RefreshLines(self, lineFrom, lineTo):
|
|
"""
|
|
Redraws a range of lines in :class:`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 range(lineFrom, lineTo+1):
|
|
self.RefreshLine(line)
|
|
|
|
|
|
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
|
|
|
|
|
|
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 range(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)
|
|
|
|
|
|
def HideWindows(self):
|
|
""" Hides the windows associated to the items. Used internally. """
|
|
|
|
for child in self._itemWithWindow:
|
|
wnd = child.GetWindow()
|
|
if wnd:
|
|
wnd.Hide()
|
|
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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 range(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.PENSTYLE_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 range(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.PENSTYLE_SOLID)
|
|
|
|
firstItemRect = self.GetItemRect(visibleFrom)
|
|
lastItemRect = self.GetItemRect(visibleTo)
|
|
x = firstItemRect.GetX()
|
|
dc.SetPen(pen)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
for col in range(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 range(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.DrawRectangle(self.GetLineHighlightRect(self._current))
|
|
|
|
|
|
def OnEraseBackground(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def TileBackground(self, dc):
|
|
"""
|
|
Tiles the background image to fill all the available area.
|
|
|
|
:param `dc`: an instance of :class:`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
|
|
|
|
|
|
def PaintWaterMark(self, dc):
|
|
"""
|
|
Draws a watermark at the bottom right of :class:`UltimateListCtrl`.
|
|
|
|
:param `dc`: an instance of :class:`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)
|
|
|
|
|
|
def HighlightAll(self, on=True):
|
|
"""
|
|
Highlights/unhighlights all the lines in :class:`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)
|
|
|
|
|
|
def OnChildFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_CHILD_FOCUS`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`ChildFocusEvent` event to be processed.
|
|
|
|
.. note::
|
|
|
|
This method is intentionally empty to prevent the default handler in
|
|
:class:`ScrolledWindow` from needlessly scrolling the window when the edit
|
|
control is dismissed.
|
|
|
|
"""
|
|
|
|
# Do nothing here. This prevents the default handler in wx.ScrolledWindow
|
|
# from needlessly scrolling the window when the edit control is
|
|
# dismissed. See ticket #9563.
|
|
|
|
pass
|
|
|
|
|
|
def SendNotify(self, line, command, point=wx.DefaultPosition, col=0):
|
|
"""
|
|
Actually sends a :class:`UltimateListEvent`.
|
|
|
|
:param `line`: an instance of :class:`UltimateListLineData`;
|
|
:param `command`: the event type to send;
|
|
:param `point`: an instance of :class:`wx.Point`.
|
|
:param `col`: an integer specifying the column index.
|
|
"""
|
|
|
|
bRet = True
|
|
le = UltimateListEvent(command, self.GetParent().GetId())
|
|
le.SetEventObject(self.GetParent())
|
|
le.m_itemIndex = line
|
|
le.m_col = col
|
|
|
|
# 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(col, le.m_item)
|
|
le.m_item.SetId(line)
|
|
le.m_item.SetColumn(col)
|
|
|
|
#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
|
|
|
|
|
|
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)
|
|
|
|
|
|
def EditLabel(self, item):
|
|
"""
|
|
Starts editing an item label.
|
|
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
def OnRenameTimer(self):
|
|
""" The timer for renaming has expired. Start editing. """
|
|
|
|
if not self.HasCurrent():
|
|
raise Exception("unexpected rename timer")
|
|
|
|
self.EditLabel(self._current)
|
|
|
|
|
|
def OnRenameAccept(self, itemEdit, value):
|
|
"""
|
|
Called by :class:`UltimateListTextCtrl`, to accept the changes and to send the
|
|
``EVT_LIST_END_LABEL_EDIT`` event.
|
|
|
|
:param `itemEdit`: an instance of :class:`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()
|
|
|
|
|
|
def OnRenameCancelled(self, itemEdit):
|
|
"""
|
|
Called by :class:`UltimateListTextCtrl`, to cancel the changes and to send the
|
|
``EVT_LIST_END_LABEL_EDIT`` event.
|
|
|
|
:param `item`: an instance of :class:`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)
|
|
|
|
|
|
def OnMouse(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MOUSE_EVENTS`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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(), -1)
|
|
|
|
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 range(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 range(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.Cursor(wx.CURSOR_HAND))
|
|
self._isonhyperlink = True
|
|
else:
|
|
if self._isonhyperlink:
|
|
self.SetCursor(wx.Cursor(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.SetToolTip(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.SetToolTip(text)
|
|
else:
|
|
self.SetToolTip("")
|
|
else:
|
|
self.SetToolTip("")
|
|
|
|
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 self.HasAGWFlag(ULC_NO_ITEM_DRAG):
|
|
return
|
|
|
|
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.Cursor(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(), -1)
|
|
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, col=newItem._col)
|
|
|
|
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(), newItem._col):
|
|
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, col=newItem._col)
|
|
|
|
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 range(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(), newItem._col)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def DragFinish(self, event):
|
|
"""
|
|
A drag and drop operation has just finished.
|
|
|
|
:param `event`: a :class:`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
|
|
|
|
|
|
def HandleHyperLink(self, item):
|
|
"""
|
|
Handles the hyperlink items, sending the ``EVT_LIST_ITEM_HYPERLINK`` event.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
if self.IsItemHyperText(item):
|
|
self.SendNotify(item._itemId, wxEVT_COMMAND_LIST_ITEM_HYPERLINK)
|
|
|
|
|
|
def OnHoverTimer(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_TIMER`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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)
|
|
|
|
|
|
def MoveToItem(self, item):
|
|
"""
|
|
Scrolls the input item into view.
|
|
|
|
:param `item`: an instance of :class:`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(int(sx), int(sy))
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# keyboard handling
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def GetNextActiveItem(self, item, down=True):
|
|
"""
|
|
Returns the next active item. Used Internally at present.
|
|
|
|
:param `item`: an instance of :class:`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]
|
|
|
|
|
|
def OnArrowChar(self, newCurrent, event):
|
|
"""
|
|
Handles the keyboard arrows key events.
|
|
|
|
:param `newCurrent`: an integer specifying the new current item;
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
def OnKeyDown(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KEY_DOWN`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`KeyEvent` event to be processed.
|
|
"""
|
|
|
|
parent = self.GetParent()
|
|
|
|
# we propagate the key event upwards
|
|
ke = event.Clone()
|
|
|
|
ke.SetEventObject(parent)
|
|
if parent.GetEventHandler().ProcessEvent(ke):
|
|
event.Skip()
|
|
return
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnKeyUp(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KEY_UP`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`KeyEvent` event to be processed.
|
|
"""
|
|
|
|
parent = self.GetParent()
|
|
|
|
# we propagate the key event upwards
|
|
ke = event.Clone()
|
|
|
|
ke.SetEventObject(parent)
|
|
if parent.GetEventHandler().ProcessEvent(ke):
|
|
return
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnChar(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_CHAR`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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 = event.Clone()
|
|
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_PAGEUP:
|
|
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_PAGEDOWN:
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def OnSetFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SET_FOCUS`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
def OnKillFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
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 :class:`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)
|
|
|
|
|
|
def DrawCheckbox(self, dc, x, y, kind, checked, enabled):
|
|
"""
|
|
Draws the item checkbox/radiobutton image.
|
|
|
|
:param `dc`: an instance of :class:`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)
|
|
|
|
|
|
def GetCheckboxImageSize(self):
|
|
""" Returns the checkbox/radiobutton image size. """
|
|
|
|
bmp = self._image_list_check.GetBitmap(0)
|
|
return bmp.GetWidth(), bmp.GetHeight()
|
|
|
|
|
|
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
|
|
|
|
|
|
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.GetFullMultiLineTextExtent(s)
|
|
|
|
return lw + AUTOSIZE_COL_MARGIN
|
|
|
|
|
|
def SetImageList(self, imageList, which):
|
|
"""
|
|
Sets the image list associated with the control.
|
|
|
|
:param `imageList`: an instance of :class:`wx.ImageList` or an instance of :class:`PyImageList`;
|
|
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
|
|
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
|
|
|
|
:note: Using :class:`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 range(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 range(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()
|
|
|
|
|
|
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 :class:`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 range(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()
|
|
|
|
|
|
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.Bitmap(x, y)
|
|
mdc = wx.MemoryDC(bmp)
|
|
mdc.SetBrush(wx.BLACK_BRUSH)
|
|
mdc.Clear()
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def SetColumn(self, col, item):
|
|
"""
|
|
Sets information about this column.
|
|
|
|
:param `col`: an integer specifying the column index;
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
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 range(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()
|
|
|
|
|
|
def GetHeaderWidth(self):
|
|
""" Returns the header window width, in pixels. """
|
|
|
|
if not self._headerWidth:
|
|
|
|
count = self.GetColumnCount()
|
|
for col in range(count):
|
|
|
|
if not self.IsColumnShown(col):
|
|
continue
|
|
|
|
self._headerWidth += self.GetColumnWidth(col)
|
|
|
|
if self.HasAGWFlag(ULC_FOOTER):
|
|
self._footerWidth = self._headerWidth
|
|
|
|
return self._headerWidth
|
|
|
|
|
|
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
|
|
|
|
|
|
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()
|
|
|
|
|
|
def GetTotalWidth(self):
|
|
""" Returns the total width of the columns in :class:`UltimateListCtrl`. """
|
|
|
|
width = 0
|
|
for column in self._columns:
|
|
width += column.GetWidth()
|
|
|
|
return width
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# item state
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def SetItem(self, item):
|
|
"""
|
|
Sets information about the item.
|
|
|
|
:param `item`: an instance of :class:`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)
|
|
|
|
|
|
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 range(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 focused, so clearing focus for
|
|
# all items is simply clearing focus of the focused item.
|
|
self.SetItemState(self._current, state, stateMask)
|
|
|
|
#(setting focus to all items makes no sense, so it is not handled here.)
|
|
|
|
|
|
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: :meth:`~UltimateListMainWindow.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)
|
|
|
|
|
|
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: :meth:`~UltimateListMainWindow.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
|
|
|
|
|
|
def GetItem(self, item, col=0):
|
|
"""
|
|
Returns the information about the input item.
|
|
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
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 :class:`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 self.HasAGWFlag(ULC_AUTO_CHECK_PARENT) and\
|
|
item.GetKind() == 1: # check box like item
|
|
col = item.GetColumn()
|
|
info = self.GetColumn(col)
|
|
if self.GetCheckedItemCount(col) == self.GetItemCount():
|
|
info.Check(True)
|
|
else:
|
|
info.Check(False)
|
|
self.SetColumn(col, info)
|
|
|
|
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)
|
|
|
|
|
|
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 range(self.GetItemCount()):
|
|
item = CreateListItem(indx, column)
|
|
newItem = self.GetItem(item, column)
|
|
self.CheckItem(newItem, not isChecked, False)
|
|
|
|
|
|
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 range(self.GetItemCount()):
|
|
item = CreateListItem(indx, column)
|
|
newItem = self.GetItem(item, column)
|
|
|
|
if newItem.GetKind() != 1:
|
|
continue
|
|
|
|
self.CheckItem(newItem, not item.IsChecked(), False)
|
|
|
|
|
|
def IsItemChecked(self, item):
|
|
"""
|
|
Returns whether an item is checked or not.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.IsChecked()
|
|
|
|
|
|
def IsItemEnabled(self, item):
|
|
"""
|
|
Returns whether an item is enabled or not.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.IsEnabled()
|
|
|
|
|
|
def EnableItem(self, item, enable=True):
|
|
"""
|
|
Enables/disables an item.
|
|
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
def GetItemKind(self, item):
|
|
"""
|
|
Returns the item kind.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
|
|
:see: :meth:`~UltimateListMainWindow.SetItemKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetKind()
|
|
|
|
|
|
def SetItemKind(self, item, kind):
|
|
"""
|
|
Sets the item kind.
|
|
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
def IsItemHyperText(self, item):
|
|
"""
|
|
Returns whether an item is hypertext or not.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.IsHyperText()
|
|
|
|
|
|
def SetItemHyperText(self, item, hyper=True):
|
|
"""
|
|
Sets whether the item is hypertext or not.
|
|
|
|
:param `item`: an instance of :class:`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
|
|
|
|
|
|
def GetHyperTextFont(self):
|
|
"""Returns the font used to render an hypertext item."""
|
|
|
|
return self._hypertextfont
|
|
|
|
|
|
def SetHyperTextFont(self, font):
|
|
"""
|
|
Sets the font used to render hypertext items.
|
|
|
|
:param `font`: a valid :class:`wx.Font` instance.
|
|
"""
|
|
|
|
self._hypertextfont = font
|
|
self._dirty = True
|
|
|
|
|
|
def SetHyperTextNewColour(self, colour):
|
|
"""
|
|
Sets the colour used to render a non-visited hypertext item.
|
|
|
|
:param `colour`: a valid :class:`wx.Colour` instance.
|
|
"""
|
|
|
|
self._hypertextnewcolour = colour
|
|
self._dirty = True
|
|
|
|
|
|
def GetHyperTextNewColour(self):
|
|
""" Returns the colour used to render a non-visited hypertext item. """
|
|
|
|
return self._hypertextnewcolour
|
|
|
|
|
|
def SetHyperTextVisitedColour(self, colour):
|
|
"""
|
|
Sets the colour used to render a visited hypertext item.
|
|
|
|
:param `colour`: a valid :class:`wx.Colour` instance.
|
|
"""
|
|
|
|
self._hypertextvisitedcolour = colour
|
|
self._dirty = True
|
|
|
|
|
|
def GetHyperTextVisitedColour(self):
|
|
""" Returns the colour used to render a visited hypertext item. """
|
|
|
|
return self._hypertextvisitedcolour
|
|
|
|
|
|
def SetItemVisited(self, item, visited=True):
|
|
"""
|
|
Sets whether an hypertext item was visited.
|
|
|
|
:param `item`: an instance of :class:`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)
|
|
|
|
return True
|
|
|
|
|
|
def GetItemVisited(self, item):
|
|
"""
|
|
Returns whether an hypertext item was visited.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetVisited()
|
|
|
|
|
|
def GetItemWindow(self, item):
|
|
"""
|
|
Returns the window associated to the item (if any).
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetWindow()
|
|
|
|
|
|
def SetItemWindow(self, item, wnd, expand=False):
|
|
"""
|
|
Sets the window for the given item.
|
|
|
|
:param `item`: an instance of :class:`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()
|
|
|
|
|
|
def DeleteItemWindow(self, item):
|
|
"""
|
|
Deletes the window associated to an item (if any).
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
if item.GetWindow() is None:
|
|
return
|
|
|
|
item.DeleteWindow()
|
|
if item in self._itemWithWindow:
|
|
self._itemWithWindow.remove(item)
|
|
|
|
self.SetItem(item)
|
|
self.RecalculatePositions()
|
|
|
|
|
|
def GetItemWindowEnabled(self, item):
|
|
"""
|
|
Returns whether the window associated to the item is enabled.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetWindowEnabled()
|
|
|
|
|
|
def SetItemWindowEnabled(self, item, enable=True):
|
|
"""
|
|
Enables/disables the window associated to the item.
|
|
|
|
:param `item`: an instance of :class:`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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetColumnCustomRenderer(self, col):
|
|
"""
|
|
Returns the custom renderer used to draw the column header
|
|
|
|
:param `col`: the column index.
|
|
"""
|
|
|
|
return self._columns[col].GetCustomRenderer()
|
|
|
|
|
|
def GetItemCustomRenderer(self, item):
|
|
"""
|
|
Returns the custom renderer used to draw the input item (if any).
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetCustomRenderer()
|
|
|
|
|
|
def SetItemCustomRenderer(self, item, renderer=None):
|
|
"""
|
|
Associate a custom renderer to this item.
|
|
|
|
:param `item`: an instance of :class:`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()
|
|
|
|
|
|
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 :class:`UltimateListItem`.
|
|
"""
|
|
|
|
item = self.GetItem(item, item._col)
|
|
return item.GetOverFlow()
|
|
|
|
|
|
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 :class:`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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def GetCheckedItemCount(self, col=0):
|
|
"""
|
|
Returns the number of checked items in the given column.
|
|
|
|
:param col: an integer specifying the column index.
|
|
:returns: the number of checked items.
|
|
:rtype: int
|
|
"""
|
|
|
|
GetLine = self.GetLine # local optimization
|
|
return len([line for line in range(self.GetItemCount()) if GetLine(line).IsChecked(col)])
|
|
|
|
|
|
def GetItemCount(self):
|
|
""" Returns the number of items in the :class:`UltimateListCtrl`. """
|
|
|
|
return (self.IsVirtual() and [self._countVirt] or [len(self._lines)])[0]
|
|
|
|
|
|
def SetItemCount(self, count):
|
|
"""
|
|
This method can only be used with virtual :class:`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 :meth:`UltimateListCtrl.OnGetItemText() <UltimateListCtrl.OnGetItemText>`) for all items in the range from 0 to `count`.
|
|
|
|
:param `count`: the total number of items in :class:`UltimateListCtrl`.
|
|
"""
|
|
|
|
self._selStore.SetItemCount(count)
|
|
self._countVirt = count
|
|
|
|
self.ResetVisibleLinesRange()
|
|
|
|
# scrollbars must be reset
|
|
self._dirty = True
|
|
|
|
|
|
def GetSelectedItemCount(self):
|
|
""" Returns the number of selected items in :class:`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 range(count):
|
|
if self.GetLine(line).IsHighlighted():
|
|
countSel += 1
|
|
|
|
return countSel
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# item position/size
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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 range(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)
|
|
|
|
|
|
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
|
|
:meth:`~UltimateListMainWindow.GetItemRect`.
|
|
|
|
:note: This method is only meaningful when the :class:`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 range(subItem):
|
|
rect.x += self.GetColumnWidth(i)
|
|
|
|
rect.width = self.GetColumnWidth(subItem)
|
|
|
|
rect.x, rect.y = self.CalcScrolledPosition(rect.x, rect.y)
|
|
return rect
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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 range(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 range(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 range(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 range(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:
|
|
self.RefreshAll()
|
|
|
|
|
|
def RefreshAll(self):
|
|
""" Refreshes the entire :class:`UltimateListCtrl`. """
|
|
|
|
self._dirty = False
|
|
self.Refresh()
|
|
|
|
headerWin = self.GetListCtrl()._headerWin
|
|
if headerWin and headerWin._dirty:
|
|
headerWin._dirty = False
|
|
headerWin.Refresh()
|
|
|
|
|
|
def UpdateCurrent(self):
|
|
""" Updates the current line selection. """
|
|
|
|
if not self.HasCurrent() and not self.IsEmpty():
|
|
self.ChangeCurrent(0)
|
|
|
|
|
|
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 range(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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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 range(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)
|
|
|
|
|
|
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 range(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
|
|
|
|
|
|
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 range(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
|
|
|
|
|
|
def DeleteAllItems(self):
|
|
"""
|
|
Deletes all items in the :class:`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 :meth:`~UltimateListMainWindow.DeleteItem`).
|
|
"""
|
|
|
|
self.DoDeleteAllItems()
|
|
self.RecalculatePositions()
|
|
|
|
|
|
def DeleteEverything(self):
|
|
""" Deletes all items in the :class:`UltimateListCtrl`, resetting column widths to zero. """
|
|
|
|
self.DeleteAllItems()
|
|
|
|
count = len(self._columns)
|
|
for n in range(count):
|
|
self.DeleteColumn(0)
|
|
|
|
self.RecalculatePositions()
|
|
self.GetListCtrl().Refresh()
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# scanning for an item
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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)
|
|
|
|
|
|
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 range(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
|
|
|
|
|
|
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 range(start, count):
|
|
line = self.GetLine(i)
|
|
item = UltimateListItem()
|
|
item = line.GetItem(0, item)
|
|
|
|
if item._data == data:
|
|
return i
|
|
|
|
return wx.NOT_FOUND
|
|
|
|
|
|
def FindItemAtPos(self, pt):
|
|
"""
|
|
Find an item nearest this position.
|
|
|
|
:param `pt`: an instance of :class:`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
|
|
|
|
|
|
def HitTest(self, x, y):
|
|
"""
|
|
HitTest method for a :class:`UltimateListCtrl`.
|
|
|
|
:param `x`: the mouse `x` position;
|
|
:param `y`: the mouse `y` position.
|
|
|
|
:see: :meth:`~UltimateListMainWindow.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 range(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 range(count):
|
|
newItem, flags = self.HitTestLine(current, x, y)
|
|
if flags:
|
|
return current, flags
|
|
|
|
return wx.NOT_FOUND, None
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# adding stuff
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def InsertItem(self, item):
|
|
"""
|
|
Inserts an item into :class:`UltimateListCtrl`.
|
|
|
|
:param `item`: an instance of :class:`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)
|
|
|
|
|
|
def InsertColumn(self, col, item):
|
|
"""
|
|
Inserts a column into :class:`UltimateListCtrl`.
|
|
|
|
:param `col`: the column index at which we wish to insert a new column;
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
|
|
:return: the index at which the column has been inserted.
|
|
|
|
:note: This method is meaningful only if :class:`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)
|
|
idx = col
|
|
|
|
else:
|
|
|
|
self._columns.append(column)
|
|
self._aColWidths.append(colWidthInfo)
|
|
idx = len(self._columns)-1
|
|
|
|
if not self.IsVirtual():
|
|
|
|
# update all the items
|
|
for i in range(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
|
|
return idx
|
|
|
|
|
|
def GetItemWidthWithImage(self, item):
|
|
"""
|
|
Returns the item width, in pixels, considering the item text and its images.
|
|
|
|
:param `item`: an instance of :class:`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.GetFullMultiLineTextExtent(item.GetText())
|
|
width += w
|
|
|
|
if item.GetWindow():
|
|
width += item._windowsize.x + 5
|
|
|
|
return width
|
|
|
|
|
|
def GetItemTextSize(self, item):
|
|
"""
|
|
Returns the item width, in pixels, considering only the item text.
|
|
|
|
:param `item`: an instance of :class:`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.GetFullMultiLineTextExtent(item.GetText())
|
|
end = w
|
|
|
|
return start, end
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# sorting
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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 :class:`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 :class:`UltimateListItem`;
|
|
:param `line2`: another instance of :class:`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)
|
|
|
|
|
|
def SortItems(self, func):
|
|
"""
|
|
Call this function to sort the items in the :class:`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
|
|
:meth:`~UltimateListMainWindow.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(key=cmp_to_key(self.OnCompareItems))
|
|
|
|
if self.IsShownOnScreen():
|
|
self._dirty = True
|
|
self._lineHeight = 0
|
|
self.ResetLineDimensions(True)
|
|
|
|
self.RecalculatePositions(True)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# scrolling
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def OnScroll(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SCROLLWIN`` event for :class:`UltimateListMainWindow`.
|
|
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
def GetCountPerPage(self):
|
|
"""
|
|
Returns the number of items that can fit vertically in the visible area
|
|
of the :class:`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
|
|
|
|
|
|
def GetVisibleLinesRange(self):
|
|
"""
|
|
Returns the range of visible items on screen.
|
|
|
|
:note: This method can be used only if :class:`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 range(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 range(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
|
|
|
|
|
|
def ResetTextControl(self):
|
|
""" Called by :class:`UltimateListTextCtrl` when it marks itself for deletion."""
|
|
|
|
self._textctrl.Destroy()
|
|
self._textctrl = None
|
|
|
|
self.RecalculatePositions()
|
|
self.Refresh()
|
|
|
|
|
|
def SetFirstGradientColour(self, colour=None):
|
|
"""
|
|
Sets the first gradient colour for gradient-style selections.
|
|
|
|
:param `colour`: if not ``None``, a valid :class:`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()
|
|
|
|
|
|
def SetSecondGradientColour(self, colour=None):
|
|
"""
|
|
Sets the second gradient colour for gradient-style selections.
|
|
|
|
:param `colour`: if not ``None``, a valid :class:`wx.Colour` instance. Otherwise,
|
|
the colour generated is a slightly darker version of the :class:`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()
|
|
|
|
|
|
def GetFirstGradientColour(self):
|
|
""" Returns the first gradient colour for gradient-style selections. """
|
|
|
|
return self._firstcolour
|
|
|
|
|
|
def GetSecondGradientColour(self):
|
|
""" Returns the second gradient colour for gradient-style selections. """
|
|
|
|
return self._secondcolour
|
|
|
|
|
|
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()
|
|
|
|
|
|
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()
|
|
|
|
|
|
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
|
|
|
|
|
|
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()
|
|
|
|
|
|
def SetBackgroundImage(self, image):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` background image.
|
|
|
|
:param `image`: if not ``None``, an instance of :class:`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()
|
|
|
|
|
|
def GetBackgroundImage(self):
|
|
"""
|
|
Returns the :class:`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
|
|
|
|
|
|
def SetWaterMark(self, watermark):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` watermark image to be displayed in the bottom
|
|
right part of the window.
|
|
|
|
:param `watermark`: if not ``None``, an instance of :class:`wx.Bitmap`.
|
|
|
|
.. todo:: Better support for this is needed.
|
|
"""
|
|
|
|
self._waterMark = watermark
|
|
self.Refresh()
|
|
|
|
|
|
def GetWaterMark(self):
|
|
"""
|
|
Returns the :class:`UltimateListCtrl` watermark image (if any), displayed in the
|
|
bottom right part of the window.
|
|
|
|
.. todo:: Better support for this is needed.
|
|
"""
|
|
|
|
return self._waterMark
|
|
|
|
|
|
def SetDisabledTextColour(self, colour):
|
|
"""
|
|
Sets the items disabled colour.
|
|
|
|
:param `colour`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
# Disabled items colour
|
|
self._disabledColour = colour
|
|
self.Refresh()
|
|
|
|
|
|
def GetDisabledTextColour(self):
|
|
""" Returns the items disabled colour. """
|
|
|
|
return self._disabledColour
|
|
|
|
|
|
def ScrollList(self, dx, dy):
|
|
"""
|
|
Scrolls the :class:`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
|
|
# -------------------------------------------------------------------------------------
|
|
|
|
class UltimateListCtrl(wx.Control):
|
|
"""
|
|
UltimateListCtrl is a class that mimics the behaviour of :class:`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.
|
|
"""
|
|
|
|
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 :class:`wx.Control` 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 :class:`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 :meth:`ListCtrl.SortItems`.)
|
|
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in :meth:`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 when 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.Control.__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)
|
|
|
|
|
|
def CreateOrDestroyHeaderWindowAsNeeded(self):
|
|
""" Creates or destroys the header window depending on the window style flags. """
|
|
|
|
needs_header = self.HasHeader()
|
|
has_header = self._headerWin is not None
|
|
|
|
if needs_header == has_header:
|
|
return
|
|
|
|
if needs_header:
|
|
|
|
self._headerWin = UltimateListHeaderWindow(self, wx.ID_ANY, self._mainWin,
|
|
wx.Point(0, 0),
|
|
wx.DefaultSize,
|
|
wx.TAB_TRAVERSAL, isFooter=False)
|
|
|
|
# ----------------------------------------------------
|
|
# How do you translate all this blah-blah to wxPython?
|
|
# ----------------------------------------------------
|
|
#if defined( __WXMAC__ ) && wxOSX_USE_COCOA_OR_CARBON
|
|
# wxFont font
|
|
#if wxOSX_USE_ATSU_TEXT
|
|
# font.MacCreateFromThemeFont( kThemeSmallSystemFont )
|
|
#else
|
|
# font.MacCreateFromUIFont( kCTFontSystemFontType )
|
|
#endif
|
|
# m_headerWin->SetFont( font )
|
|
#endif
|
|
|
|
self.GetSizer().Prepend(self._headerWin, 0, wx.GROW)
|
|
|
|
else:
|
|
|
|
self.GetSizer().Detach(self._headerWin)
|
|
self._headerWin.Destroy()
|
|
self._headerWin = None
|
|
|
|
|
|
def CreateOrDestroyFooterWindowAsNeeded(self):
|
|
""" Creates or destroys the footer window depending on the window style flags. """
|
|
|
|
needs_footer = self.HasFooter()
|
|
has_footer = self._footerWin is not None
|
|
|
|
if needs_footer == has_footer:
|
|
return
|
|
|
|
if needs_footer:
|
|
|
|
self._footerWin = UltimateListHeaderWindow(self, wx.ID_ANY, self._mainWin,
|
|
wx.Point(0, 0),
|
|
wx.DefaultSize,
|
|
wx.TAB_TRAVERSAL, isFooter=True)
|
|
|
|
# ----------------------------------------------------
|
|
# How do you translate all this blah-blah to wxPython?
|
|
# ----------------------------------------------------
|
|
#if defined( __WXMAC__ ) && wxOSX_USE_COCOA_OR_CARBON
|
|
# wxFont font
|
|
#if wxOSX_USE_ATSU_TEXT
|
|
# font.MacCreateFromThemeFont( kThemeSmallSystemFont )
|
|
#else
|
|
# font.MacCreateFromUIFont( kCTFontSystemFontType )
|
|
#endif
|
|
# m_headerWin->SetFont( font )
|
|
#endif
|
|
|
|
self.GetSizer().Add(self._footerWin, 0, wx.GROW)
|
|
|
|
else:
|
|
|
|
self.GetSizer().Detach(self._footerWin)
|
|
self._footerWin.Destroy()
|
|
self._footerWin = None
|
|
|
|
|
|
def HasHeader(self):
|
|
""" Returns ``True`` if :class:`UltimateListCtrl` has a header window. """
|
|
|
|
return self._mainWin.HasHeader()
|
|
|
|
|
|
def HasFooter(self):
|
|
""" Returns ``True`` if :class:`UltimateListCtrl` has a footer window. """
|
|
|
|
return self._mainWin.HasFooter()
|
|
|
|
|
|
def GetDefaultBorder(self):
|
|
""" Returns the default window border. """
|
|
|
|
return wx.BORDER_THEME
|
|
|
|
|
|
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 :class:`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 :meth:`ListCtrl.SortItems`.)
|
|
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in :meth:`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 when 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)
|
|
|
|
|
|
def GetAGWWindowStyleFlag(self):
|
|
"""
|
|
Returns the :class:`UltimateListCtrl` AGW-specific style flag.
|
|
|
|
:see: :meth:`~UltimateListCtrl.SetAGWWindowStyleFlag` for a list of possible style flags.
|
|
"""
|
|
|
|
return self._agwStyle
|
|
|
|
|
|
def SetAGWWindowStyleFlag(self, style):
|
|
"""
|
|
Sets the :class:`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 :class:`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 :meth:`ListCtrl.SortItems`.)
|
|
``ULC_SORT_DESCENDING`` 0x8000 Sort in descending order. (You must still supply a comparison callback in :meth:`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 when 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()
|
|
|
|
|
|
def HasAGWFlag(self, flag):
|
|
"""
|
|
Returns ``True`` if the window has the given flag bit set.
|
|
|
|
:param `flag`: the window style to check.
|
|
|
|
:see: :meth:`~UltimateListCtrl.SetAGWWindowStyleFlag` for a list of valid window styles.
|
|
"""
|
|
|
|
return self._agwStyle & flag
|
|
|
|
|
|
def SetUserLineHeight(self, height):
|
|
"""
|
|
Sets a custom value for the :class:`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)
|
|
|
|
|
|
def GetUserLineHeight(self):
|
|
"""
|
|
Returns the custom value for the :class:`UltimateListCtrl` item height, if previously set with
|
|
:meth:`~UltimateListCtrl.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()
|
|
|
|
|
|
def GetCheckedItemCount(self, col=0):
|
|
"""
|
|
Returns the number of checked items in the given column.
|
|
|
|
:param 'col': an integer specifying the column index.
|
|
:returns: the number of checked items.
|
|
:rtype: int
|
|
"""
|
|
|
|
return self._mainWin.GetCheckedItemCount(col)
|
|
|
|
|
|
def GetColumn(self, col):
|
|
"""
|
|
Returns information about this column.
|
|
|
|
:param `col`: an integer specifying the column index.
|
|
"""
|
|
|
|
return self._mainWin.GetColumn(col)
|
|
|
|
|
|
def SetColumn(self, col, item):
|
|
"""
|
|
Sets information about this column.
|
|
|
|
:param `col`: an integer specifying the column index;
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
self._mainWin.SetColumn(col, item)
|
|
return True
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetCountPerPage(self):
|
|
"""
|
|
Returns the number of items that can fit vertically in the visible area
|
|
of the :class:`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 ?
|
|
|
|
|
|
def GetItem(self, itemOrId, col=0):
|
|
"""
|
|
Returns the information about the input item.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetItem(self, info):
|
|
"""
|
|
Sets the information about the input item.
|
|
|
|
:param `info`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
self._mainWin.SetItem(info)
|
|
return True
|
|
|
|
|
|
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
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.SetItemState` for a list of valid state flags.
|
|
"""
|
|
|
|
return self._mainWin.GetItemState(item, stateMask)
|
|
|
|
|
|
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
|
|
|
|
|
|
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 :class:`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)
|
|
|
|
|
|
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 :class:`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
|
|
|
|
|
|
def GetItemText(self, item):
|
|
"""
|
|
Returns the item text.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem` or an integer specifying
|
|
the item index.
|
|
"""
|
|
|
|
return self._mainWin.GetItemText(item)
|
|
|
|
|
|
def SetItemText(self, item, text):
|
|
"""
|
|
Sets the item text.
|
|
|
|
:param `item`: an instance of :class:`UltimateListItem` or an integer specifying
|
|
the item index;
|
|
:param `text`: the new item text.
|
|
"""
|
|
|
|
self._mainWin.SetItemText(item, text)
|
|
|
|
|
|
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
|
|
|
|
|
|
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 :meth:`~UltimateListCtrl.SetItemPyData` instead.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._mask = ULC_MASK_DATA
|
|
info._itemId = item
|
|
info._data = data
|
|
self._mainWin.SetItem(info)
|
|
return True
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
:meth:`~UltimateListCtrl.GetItemRect`;
|
|
:param `code`: one of ``ULC_RECT_BOUNDS``, ``ULC_RECT_ICON``, ``ULC_RECT_LABEL``.
|
|
|
|
:note: This method is only meaningful when the :class:`UltimateListCtrl` is in the
|
|
report mode.
|
|
"""
|
|
|
|
rect = self._mainWin.GetSubItemRect(item, subItem)
|
|
if self._mainWin.HasHeader():
|
|
rect.y += self._headerHeight + 1
|
|
|
|
return rect
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
|
|
|
|
def GetItemCount(self):
|
|
""" Returns the number of items in the :class:`UltimateListCtrl`. """
|
|
|
|
return self._mainWin.GetItemCount()
|
|
|
|
|
|
def GetColumnCount(self):
|
|
""" Returns the total number of columns in the :class:`UltimateListCtrl`. """
|
|
|
|
return self._mainWin.GetColumnCount()
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def SetItemTextColour(self, item, col):
|
|
"""
|
|
Sets the item text colour.
|
|
|
|
:param `item`: the index of the item;
|
|
:param `col`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._itemId = item
|
|
info = self._mainWin.GetItem(info)
|
|
info.SetTextColour(col)
|
|
self._mainWin.SetItem(info)
|
|
|
|
|
|
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()
|
|
|
|
|
|
def SetItemBackgroundColour(self, item, col):
|
|
"""
|
|
Sets the item background colour.
|
|
|
|
:param `item`: the index of the item;
|
|
:param `col`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._itemId = item
|
|
info = self._mainWin.GetItem(info)
|
|
info.SetBackgroundColour(col)
|
|
self._mainWin.SetItem(info)
|
|
|
|
|
|
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()
|
|
|
|
|
|
def SetItemFont(self, item, f):
|
|
"""
|
|
Sets the item font.
|
|
|
|
:param `item`: the index of the item;
|
|
:param `f`: a valid :class:`wx.Font` object.
|
|
"""
|
|
|
|
info = UltimateListItem()
|
|
info._itemId = item
|
|
info = self._mainWin.GetItem(info)
|
|
info.SetFont(f)
|
|
info.SetBackgroundColour(self.GetItemBackgroundColour(item))
|
|
self._mainWin.SetItem(info)
|
|
|
|
|
|
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()
|
|
|
|
|
|
def GetSelectedItemCount(self):
|
|
""" Returns the number of selected items in :class:`UltimateListCtrl`. """
|
|
|
|
return self._mainWin.GetSelectedItemCount()
|
|
|
|
|
|
def GetTextColour(self):
|
|
""" Returns the :class:`UltimateListCtrl` foreground colour. """
|
|
|
|
return self.GetForegroundColour()
|
|
|
|
|
|
def SetTextColour(self, col):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` foreground colour.
|
|
|
|
:param `col`: a valid :class:`wx.Colour` object.
|
|
"""
|
|
|
|
self.SetForegroundColour(col)
|
|
|
|
|
|
def GetTopItem(self):
|
|
""" Gets the index of the topmost visible item when in list or report view. """
|
|
|
|
top, dummy = self._mainWin.GetVisibleLinesRange()
|
|
return top
|
|
|
|
|
|
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)
|
|
|
|
|
|
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 :class:`UltimateListCtrl` allows you to use a standard :class:`wx.ImageList` or
|
|
:class:`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
|
|
|
|
|
|
def SetImageList(self, imageList, which):
|
|
"""
|
|
Sets the image list associated with the control.
|
|
|
|
:param `imageList`: an instance of :class:`wx.ImageList` or an instance of :class:`PyImageList`;
|
|
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
|
|
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
|
|
|
|
:note: Using :class:`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)
|
|
|
|
|
|
def AssignImageList(self, imageList, which):
|
|
"""
|
|
Assigns the image list associated with the control.
|
|
|
|
:param `imageList`: an instance of :class:`wx.ImageList` or an instance of :class:`PyImageList`;
|
|
:param `which`: one of ``wx.IMAGE_LIST_NORMAL``, ``wx.IMAGE_LIST_SMALL``,
|
|
``wx.IMAGE_LIST_STATE`` (the last is unimplemented).
|
|
|
|
:note: Using :class:`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)
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
def DeleteAllItems(self):
|
|
"""
|
|
Deletes all items in the :class:`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 :meth:`~UltimateListCtrl.DeleteItem`).
|
|
"""
|
|
|
|
self._mainWin.DeleteAllItems()
|
|
return True
|
|
|
|
|
|
def DeleteAllColumns(self):
|
|
""" Deletes all the column in :class:`UltimateListCtrl`. """
|
|
|
|
count = len(self._mainWin._columns)
|
|
for n in range(count):
|
|
self.DeleteColumn(0)
|
|
|
|
return True
|
|
|
|
|
|
def ClearAll(self):
|
|
""" Deletes everything in :class:`UltimateListCtrl`. """
|
|
|
|
self._mainWin.DeleteEverything()
|
|
|
|
|
|
def DeleteColumn(self, col):
|
|
"""
|
|
Deletes the specified column.
|
|
|
|
:param `col`: the index of the column to delete.
|
|
"""
|
|
|
|
self._mainWin.DeleteColumn(col)
|
|
return True
|
|
|
|
|
|
def EditLabel(self, item):
|
|
"""
|
|
Starts editing an item label.
|
|
|
|
:param `item`: the index of the item to edit.
|
|
"""
|
|
|
|
self._mainWin.EditLabel(item)
|
|
|
|
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def FindItemAtPos(self, start, pt):
|
|
"""
|
|
Find an item nearest this position.
|
|
|
|
:param `pt`: an instance of :class:`wx.Point`.
|
|
"""
|
|
|
|
return self._mainWin.FindItemAtPos(pt)
|
|
|
|
|
|
def HitTest(self, pointOrTuple):
|
|
"""
|
|
HitTest method for a :class:`UltimateListCtrl`.
|
|
|
|
:param `pointOrTuple`: an instance of :class:`wx.Point` or a tuple representing
|
|
the mouse `x`, `y` position.
|
|
|
|
:see: :meth:`UltimateListMainWindow.HitTestLine() <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)
|
|
|
|
|
|
def InsertItem(self, info):
|
|
"""
|
|
Inserts an item into :class:`UltimateListCtrl`.
|
|
|
|
:param `info`: an instance of :class:`UltimateListItem`.
|
|
"""
|
|
|
|
self._mainWin.InsertItem(info)
|
|
return info._itemId
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.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)
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.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)
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.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)
|
|
|
|
|
|
def InsertColumnInfo(self, col, item):
|
|
"""
|
|
Inserts a column into :class:`UltimateListCtrl`.
|
|
|
|
:param `col`: the column index at which we wish to insert a column;
|
|
:param `item`: an instance of :class:`UltimateListItem`.
|
|
|
|
:return: the index at which the column has been inserted.
|
|
"""
|
|
|
|
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")
|
|
|
|
idx = self._mainWin.InsertColumn(col, item)
|
|
if self._headerWin:
|
|
self._headerWin.Refresh()
|
|
|
|
return idx
|
|
|
|
|
|
def InsertColumn(self, col, heading, format=ULC_FORMAT_LEFT, width=-1):
|
|
"""
|
|
Inserts a column into :class:`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.
|
|
|
|
:return: the index at which the column has been inserted.
|
|
"""
|
|
|
|
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)
|
|
|
|
|
|
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")
|
|
|
|
|
|
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()
|
|
|
|
|
|
def ScrollList(self, dx, dy):
|
|
"""
|
|
Scrolls the :class:`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.
|
|
"""
|
|
|
|
return self._mainWin.ScrollList(dx, dy)
|
|
|
|
|
|
# Sort items.
|
|
# The return value is a negative number if the first item should precede the second
|
|
# item, a positive number of the second item should precede the first,
|
|
# or zero if the two items are equivalent.
|
|
|
|
def SortItems(self, func=None):
|
|
"""
|
|
Call this function to sort the items in the :class:`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
|
|
:meth:`UltimateListMainWindow.OnCompareItems() <UltimateListMainWindow.OnCompareItems>` method.
|
|
"""
|
|
|
|
self._mainWin.SortItems(func)
|
|
wx.CallAfter(self.Refresh)
|
|
|
|
return True
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# event handlers
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def OnSize(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SIZE`` event for :class:`UltimateListCtrl`.
|
|
|
|
:param `event`: a :class:`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()
|
|
|
|
|
|
def OnSetFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SET_FOCUS`` event for :class:`UltimateListCtrl`.
|
|
|
|
:param `event`: a :class:`FocusEvent` event to be processed.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
self._mainWin.SetFocusIgnoringChildren()
|
|
self._mainWin.Update()
|
|
event.Skip()
|
|
|
|
|
|
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 :meth:`wx.Window.UpdateWindowUI` in order to send update events
|
|
to the window in idle time.
|
|
"""
|
|
|
|
wx.Control.OnInternalIdle(self)
|
|
|
|
# do it only if needed
|
|
if self._mainWin and self._mainWin._dirty:
|
|
self._mainWin._shortItems = []
|
|
self._mainWin.RecalculatePositions()
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# font/colours
|
|
# ----------------------------------------------------------------------------
|
|
|
|
def SetBackgroundColour(self, colour):
|
|
"""
|
|
Changes the background colour of :class:`UltimateListCtrl`.
|
|
|
|
:param `colour`: the colour to be used as the background colour, pass
|
|
:class:`NullColour` to reset to the default colour.
|
|
|
|
:note: The background colour is usually painted by the default :class:`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 :meth:`wx.Window.ClearBackground` or :meth:`wx.Window.Refresh` after
|
|
calling this function.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
self._mainWin.SetBackgroundColour(colour)
|
|
self._mainWin._dirty = True
|
|
|
|
return True
|
|
|
|
|
|
def SetForegroundColour(self, colour):
|
|
"""
|
|
Changes the foreground colour of :class:`UltimateListCtrl`.
|
|
|
|
:param `colour`: the colour to be used as the foreground colour, pass
|
|
:class:`NullColour` to reset to the default colour.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
if not wx.Control.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
|
|
|
|
|
|
def SetFont(self, font):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` font.
|
|
|
|
:param `font`: a valid :class:`wx.Font` instance.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
if not wx.Control.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
|
|
|
|
|
|
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, :meth:`Button.GetClassDefaultAttributes` () will typically return the
|
|
values appropriate for a button which will be normally different from those
|
|
returned by, say, :meth:`ListCtrl.GetClassDefaultAttributes` ().
|
|
|
|
:note: The :class:`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 :class:`wx.Control`.
|
|
"""
|
|
|
|
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
|
|
|
|
|
|
def GetScrolledWin(self):
|
|
""" Returns the header window owner. """
|
|
|
|
return self._headerWin.GetOwner()
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# methods forwarded to self._mainWin
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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 :class:`DropTarget`.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
self._mainWin.SetDropTarget(dropTarget)
|
|
|
|
|
|
def GetDropTarget(self):
|
|
"""
|
|
Returns the associated drop target, which may be ``None``.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.GetDropTarget()
|
|
|
|
|
|
def SetCursor(self, cursor):
|
|
"""
|
|
Sets the window's cursor.
|
|
|
|
:param `cursor`: specifies the cursor that the window should normally display.
|
|
The `cursor` may be :class:`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 :class:`wx.Control`.
|
|
"""
|
|
|
|
return (self._mainWin and [self._mainWin.SetCursor(cursor)] or [False])[0]
|
|
|
|
|
|
def GetBackgroundColour(self):
|
|
"""
|
|
Returns the background colour of the window.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return (self._mainWin and [self._mainWin.GetBackgroundColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def GetForegroundColour(self):
|
|
"""
|
|
Returns the foreground colour of the window.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return (self._mainWin and [self._mainWin.GetForegroundColour()] or [wx.NullColour])[0]
|
|
|
|
|
|
def PopupMenu(self, menu, pos=wx.DefaultPosition):
|
|
"""
|
|
Pops up the given `menu` at the specified coordinates, relative to this window,
|
|
and returns control when the user has dismissed the menu. If a menu item is
|
|
selected, the corresponding menu event is generated and will be processed as
|
|
usual. If the coordinates are not specified, the current mouse cursor position
|
|
is used.
|
|
|
|
:param `menu`: an instance of :class:`wx.Menu` to pop up;
|
|
:param `pos`: the position where the menu will appear.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.PopupMenu(menu, pos)
|
|
|
|
|
|
def ClientToScreen(self, pointOrTuple):
|
|
"""
|
|
Converts to screen coordinates from coordinates relative to this window.
|
|
|
|
:param `pointOrTuple`: an instance of :class:`wx.Point` or a tuple representing the
|
|
`x`, `y` coordinates for this point.
|
|
|
|
:return: the coordinates relative to the screen.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.ClientToScreen(*pointOrTuple)
|
|
|
|
|
|
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 :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.ClientToScreen(x, y)
|
|
|
|
|
|
def ScreenToClient(self, pointOrTuple):
|
|
"""
|
|
Converts from screen to client window coordinates.
|
|
|
|
:param `pointOrTuple`: an instance of :class:`wx.Point` or a tuple representing the
|
|
`x`, `y` coordinates for this point.
|
|
|
|
:return: the coordinates relative to this window.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.ScreenToClient(*pointOrTuple)
|
|
|
|
|
|
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 :class:`wx.Control`.
|
|
"""
|
|
|
|
return self._mainWin.ScreenToClient(x, y)
|
|
|
|
|
|
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()
|
|
|
|
|
|
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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
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")
|
|
|
|
|
|
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 :class:`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")
|
|
|
|
|
|
def OnGetItemToolTip(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 tooltip 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.OnGetItemToolTip not supposed to be called")
|
|
|
|
|
|
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, :meth:`~UltimateListCtrl.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 []
|
|
|
|
|
|
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 []
|
|
|
|
|
|
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:
|
|
|
|
:class:`UltimateListCtrl` will not delete the pointer or keep a reference of it.
|
|
You can return the same :class:`UltimateListItemAttr` pointer for every
|
|
:meth:`~UltimateListCtrl.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
|
|
|
|
|
|
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 []
|
|
|
|
|
|
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 []
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.SetItemKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
return 0
|
|
|
|
|
|
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: :meth:`~UltimateListCtrl.SetItemKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
if column == 0:
|
|
return self.OnGetItemKind(item)
|
|
|
|
return 0
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
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 :meth:`~UltimateListCtrl.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.
|
|
#
|
|
|
|
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 :meth:`~UltimateListCtrl.Update` instead.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
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)
|
|
|
|
|
|
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 :meth:`~UltimateListCtrl.Refresh` first if you want to immediately redraw the window unconditionally.
|
|
|
|
:note: Overridden from :class:`wx.Control`.
|
|
"""
|
|
|
|
self._mainWin.ResetVisibleLinesRange(True)
|
|
wx.Control.Update(self)
|
|
|
|
|
|
def GetEditControl(self):
|
|
"""
|
|
Returns a pointer to the edit :class:`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
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def GetFocusedItem(self):
|
|
""" Returns the currently focused item or -1 if none is focused. """
|
|
|
|
return self.GetNextItem(-1, ULC_NEXT_ALL, ULC_STATE_FOCUSED)
|
|
|
|
|
|
def GetFirstSelected(self):
|
|
""" Return first selected item, or -1 when none is selected. """
|
|
|
|
return self.GetNextSelected(-1)
|
|
|
|
|
|
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)
|
|
|
|
|
|
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
|
|
|
|
|
|
def IsItemChecked(self, itemOrId, col=0):
|
|
"""
|
|
Returns whether an item is checked or not.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def IsItemEnabled(self, itemOrId, col=0):
|
|
"""
|
|
Returns whether an item is enabled or not.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def GetItemKind(self, itemOrId, col=0):
|
|
"""
|
|
Returns the item kind.
|
|
|
|
:param `itemOrId`: an instance of :class:`UltimateListItem` or the item index;
|
|
:param `col`: the column index to which the input item belongs to.
|
|
|
|
:see: :meth:`~UltimateListCtrl.SetItemKind` for a list of valid item kinds.
|
|
"""
|
|
|
|
item = CreateListItem(itemOrId, col)
|
|
return self._mainWin.GetItemKind(item)
|
|
|
|
|
|
def SetItemKind(self, itemOrId, col=0, kind=0):
|
|
"""
|
|
Sets the item kind.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def EnableItem(self, itemOrId, col=0, enable=True):
|
|
"""
|
|
Enables/disables an item.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def IsItemHyperText(self, itemOrId, col=0):
|
|
"""
|
|
Returns whether an item is hypertext or not.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetItemHyperText(self, itemOrId, col=0, hyper=True):
|
|
"""
|
|
Sets whether the item is hypertext or not.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetColumnToolTip(self, col, tip):
|
|
"""
|
|
Sets the tooltip for the column header
|
|
|
|
:param `col`: the column index;
|
|
:param `tip`: the tooltip text
|
|
"""
|
|
|
|
item = self.GetColumn(col)
|
|
item.SetToolTip(tip)
|
|
self.SetColumn(col, item)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def ClearColumnImage(self, col):
|
|
"""
|
|
Clears all the images in the specified column.
|
|
|
|
:param `col`: the column index;
|
|
"""
|
|
|
|
self.SetColumnImage(col, -1)
|
|
|
|
|
|
def Append(self, entry):
|
|
"""
|
|
Append an item to the :class:`UltimateListCtrl`.
|
|
|
|
:param `entry`: should be a sequence with an item for each column.
|
|
"""
|
|
|
|
if entry:
|
|
pos = self.GetItemCount()
|
|
self.InsertStringItem(pos, str(entry[0]))
|
|
for i in range(1, len(entry)):
|
|
self.SetStringItem(pos, i, str(entry[i]))
|
|
|
|
return pos
|
|
|
|
|
|
def SetFirstGradientColour(self, colour=None):
|
|
"""
|
|
Sets the first gradient colour for gradient-style selections.
|
|
|
|
:param `colour`: if not ``None``, a valid :class:`wx.Colour` instance. Otherwise,
|
|
the colour is taken from the system value ``wx.SYS_COLOUR_HIGHLIGHT``.
|
|
"""
|
|
|
|
self._mainWin.SetFirstGradientColour(colour)
|
|
|
|
|
|
def SetSecondGradientColour(self, colour=None):
|
|
"""
|
|
Sets the second gradient colour for gradient-style selections.
|
|
|
|
:param `colour`: if not ``None``, a valid :class:`wx.Colour` instance. Otherwise,
|
|
the colour generated is a slightly darker version of the :class:`UltimateListCtrl`
|
|
background colour.
|
|
"""
|
|
|
|
self._mainWin.SetSecondGradientColour(colour)
|
|
|
|
|
|
def GetFirstGradientColour(self):
|
|
""" Returns the first gradient colour for gradient-style selections. """
|
|
|
|
return self._mainWin.GetFirstGradientColour()
|
|
|
|
|
|
def GetSecondGradientColour(self):
|
|
""" Returns the second gradient colour for gradient-style selections. """
|
|
|
|
return self._mainWin.GetSecondGradientColour()
|
|
|
|
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
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()
|
|
|
|
|
|
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)
|
|
|
|
|
|
def SetBackgroundImage(self, image=None):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` background image.
|
|
|
|
:param `image`: if not ``None``, an instance of :class:`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)
|
|
|
|
|
|
def GetBackgroundImage(self):
|
|
"""
|
|
Returns the :class:`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()
|
|
|
|
|
|
def SetWaterMark(self, watermark=None):
|
|
"""
|
|
Sets the :class:`UltimateListCtrl` watermark image to be displayed in the bottom
|
|
right part of the window.
|
|
|
|
:param `watermark`: if not ``None``, an instance of :class:`wx.Bitmap`.
|
|
|
|
.. todo:: Better support for this is needed.
|
|
"""
|
|
|
|
self._mainWin.SetWaterMark(watermark)
|
|
|
|
|
|
def GetWaterMark(self):
|
|
"""
|
|
Returns the :class:`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()
|
|
|
|
|
|
def SetDisabledTextColour(self, colour):
|
|
"""
|
|
Sets the items disabled colour.
|
|
|
|
:param `colour`: an instance of :class:`wx.Colour`.
|
|
"""
|
|
|
|
self._mainWin.SetDisabledTextColour(colour)
|
|
|
|
|
|
def GetDisabledTextColour(self):
|
|
""" Returns the items disabled colour. """
|
|
|
|
return self._mainWin.GetDisabledTextColour()
|
|
|
|
|
|
def GetHyperTextFont(self):
|
|
""" Returns the font used to render an hypertext item. """
|
|
|
|
return self._mainWin.GetHyperTextFont()
|
|
|
|
|
|
def SetHyperTextFont(self, font):
|
|
"""
|
|
Sets the font used to render hypertext items.
|
|
|
|
:param `font`: a valid :class:`wx.Font` instance.
|
|
"""
|
|
|
|
self._mainWin.SetHyperTextFont(font)
|
|
|
|
|
|
def SetHyperTextNewColour(self, colour):
|
|
"""
|
|
Sets the colour used to render a non-visited hypertext item.
|
|
|
|
:param `colour`: a valid :class:`wx.Colour` instance.
|
|
"""
|
|
|
|
self._mainWin.SetHyperTextNewColour(colour)
|
|
|
|
|
|
def GetHyperTextNewColour(self):
|
|
""" Returns the colour used to render a non-visited hypertext item. """
|
|
|
|
return self._mainWin.GetHyperTextNewColour()
|
|
|
|
|
|
def SetHyperTextVisitedColour(self, colour):
|
|
"""
|
|
Sets the colour used to render a visited hypertext item.
|
|
|
|
:param `colour`: a valid :class:`wx.Colour` instance.
|
|
"""
|
|
|
|
self._mainWin.SetHyperTextVisitedColour(colour)
|
|
|
|
|
|
def GetHyperTextVisitedColour(self):
|
|
""" Returns the colour used to render a visited hypertext item. """
|
|
|
|
return self._mainWin.GetHyperTextVisitedColour()
|
|
|
|
|
|
def SetItemVisited(self, itemOrId, col=0, visited=True):
|
|
"""
|
|
Sets whether an hypertext item was visited or not.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def GetItemVisited(self, itemOrId, col=0):
|
|
"""
|
|
Returns whether an hypertext item was visited.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def GetItemWindow(self, itemOrId, col=0):
|
|
"""
|
|
Returns the window associated to the item (if any).
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetItemWindow(self, itemOrId, col=0, wnd=None, expand=False):
|
|
"""
|
|
Sets the window for the given item.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def DeleteItemWindow(self, itemOrId, col=0):
|
|
"""
|
|
Deletes the window associated to an item (if any).
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def GetItemWindowEnabled(self, itemOrId, col=0):
|
|
"""
|
|
Returns whether the window associated to the item is enabled.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetItemWindowEnabled(self, itemOrId, col=0, enable=True):
|
|
"""
|
|
Enables/disables the window associated to the item.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def GetItemCustomRenderer(self, itemOrId, col=0):
|
|
"""
|
|
Returns the custom renderer used to draw the input item (if any).
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
def SetHeaderCustomRenderer(self, renderer=None):
|
|
"""
|
|
Associate a custom renderer with the header - all columns will use it.
|
|
|
|
:param `renderer`: a class able to correctly render header buttons
|
|
|
|
: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")
|
|
|
|
self._headerWin.SetCustomRenderer(renderer)
|
|
|
|
|
|
def SetFooterCustomRenderer(self, renderer=None):
|
|
"""
|
|
Associate a custom renderer with the footer - all columns will use it.
|
|
|
|
:param `renderer`: a class able to correctly render header buttons
|
|
|
|
:note: the renderer class **must** implement the methods `DrawHeaderButton`
|
|
and `GetForegroundColor`.
|
|
"""
|
|
|
|
if not self.HasAGWFlag(ULC_REPORT) or not self.HasAGWFlag(ULC_FOOTER):
|
|
raise Exception("Custom renderers can only be used on with style = ULC_REPORT | ULC_FOOTER")
|
|
|
|
self._footerWin.SetCustomRenderer(renderer)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def SetItemCustomRenderer(self, itemOrId, col=0, renderer=None):
|
|
"""
|
|
Associate a custom renderer to this item.
|
|
|
|
:param `itemOrId`: an instance of :class:`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)
|
|
|
|
|
|
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 :class:`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)
|
|
|
|
|
|
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 :class:`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)
|
|
|
|
|
|
def IsVirtual(self):
|
|
""" Returns ``True`` if the :class:`UltimateListCtrl` has the ``ULC_VIRTUAL`` style set. """
|
|
|
|
return self._mainWin.IsVirtual()
|
|
|
|
|
|
def GetScrollPos(self, orientation):
|
|
"""
|
|
Returns the scrollbar position.
|
|
|
|
:note: This method is forwarded to :class:`UltimateListMainWindow`.
|
|
|
|
:param `orientation`: May be wx.HORIZONTAL or wx.VERTICAL.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
return self._mainWin.GetScrollPos(orientation)
|
|
|
|
return 0
|
|
|
|
|
|
def SetScrollPos(self, orientation, pos, refresh=True):
|
|
"""
|
|
Sets the scrollbar position.
|
|
|
|
:param `orientation`: determines the scrollbar whose position is to be set.
|
|
May be ``wx.HORIZONTAL`` or ``wx.VERTICAL``;
|
|
:param `pos`: the scrollbar position in scroll units;
|
|
:param `refresh`: ``True`` to redraw the scrollbar, ``False`` otherwise.
|
|
|
|
:note: This method is forwarded to :class:`UltimateListMainWindow`.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
self._mainWin.SetScrollPos(orientation, pos, refresh)
|
|
|
|
|
|
def GetScrollThumb(self):
|
|
"""
|
|
Returns the scrollbar size in pixels.
|
|
|
|
:note: This method is forwarded to :class:`UltimateListMainWindow`.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
return self._mainWin.GetScrollThumb()
|
|
|
|
return 0
|
|
|
|
|
|
def GetScrollRange(self):
|
|
"""
|
|
Returns the scrollbar range in pixels.
|
|
|
|
:note: This method is forwarded to :class:`UltimateListMainWindow`.
|
|
"""
|
|
|
|
if self._mainWin:
|
|
return self._mainWin.GetScrollRange()
|
|
|
|
return 0
|
|
|
|
|
|
def SetHeaderHeight(self, height):
|
|
"""
|
|
Sets the :class:`UltimateListHeaderWindow` height, in pixels. This overrides the default
|
|
header window size derived from :class:`RendererNative`. If `height` is ``None``, the
|
|
default behaviour is restored.
|
|
|
|
:param `height`: the header window height, in pixels (if it is ``None``, the default
|
|
height obtained using :class:`RendererNative` is used).
|
|
"""
|
|
|
|
if not self._headerWin:
|
|
return
|
|
|
|
if height is not None and height < 1:
|
|
raise Exception("Invalid height passed to SetHeaderHeight: %s"%repr(height))
|
|
|
|
self._headerWin._headerHeight = height
|
|
self._headerWin.InvalidateBestSize()
|
|
self.DoLayout()
|
|
|
|
|
|
def GetHeaderHeight(self):
|
|
""" Returns the :class:`UltimateListHeaderWindow` height, in pixels. """
|
|
|
|
if not self._headerWin:
|
|
return -1
|
|
|
|
return self._headerWin.GetWindowHeight()
|
|
|
|
|
|
def SetFooterHeight(self, height):
|
|
"""
|
|
Sets the :class:`UltimateListHeaderWindow` height, in pixels. This overrides the default
|
|
footer window size derived from :class:`RendererNative`. If `height` is ``None``, the
|
|
default behaviour is restored.
|
|
|
|
:param `height`: the footer window height, in pixels (if it is ``None``, the default
|
|
height obtained using :class:`RendererNative` is used).
|
|
"""
|
|
|
|
if not self._footerWin:
|
|
return
|
|
|
|
if height is not None and height < 1:
|
|
raise Exception("Invalid height passed to SetFooterHeight: %s"%repr(height))
|
|
|
|
self._footerWin._footerHeight = height
|
|
self.DoLayout()
|
|
|
|
|
|
def GetFooterHeight(self):
|
|
""" Returns the :class:`UltimateListHeaderWindow` height, in pixels. """
|
|
|
|
if not self._footerWin:
|
|
return -1
|
|
|
|
return self._headerWin.GetWindowHeight()
|
|
|
|
|
|
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()
|