X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ec873c943d71f0d5f13e3398557071448cda6c23..a4027e74873007e3430af3bd77019bcab76f6c04:/wxPython/wx/lib/customtreectrl.py diff --git a/wxPython/wx/lib/customtreectrl.py b/wxPython/wx/lib/customtreectrl.py deleted file mode 100644 index a33b8574ce..0000000000 --- a/wxPython/wx/lib/customtreectrl.py +++ /dev/null @@ -1,5764 +0,0 @@ -# --------------------------------------------------------------------------------- # -# CUSTOMTREECTRL wxPython IMPLEMENTATION -# Inspired By And Heavily Based On wxGenericTreeCtrl. -# -# Andrea Gavana, @ 17 May 2006 -# Latest Revision: 16 Apr 2007, 11.00 CET -# -# -# TODO List -# -# Almost All The Features Of wx.TreeCtrl Are Available, And There Is Practically -# No Limit In What Could Be Added To This Class. The First Things That Comes -# To My Mind Are: -# -# 1. Implement The Style TR_EXTENDED (I Have Never Used It, But It May Be Useful). -# -# 2. Add Support For 3-State CheckBoxes (Is That Really Useful?). -# -# 3. Try To Implement A More Flicker-Free Background Image In Cases Like -# Centered Or Stretched Image (Now CustomTreeCtrl Supports Only Tiled -# Background Images). -# -# 4. Try To Mimic Windows wx.TreeCtrl Expanding/Collapsing behaviour: CustomTreeCtrl -# Suddenly Expands/Collapses The Nodes On Mouse Click While The Native Control -# Has Some Kind Of "Smooth" Expanding/Collapsing, Like A Wave. I Don't Even -# Know Where To Start To Do That. -# -# 5. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite -# Fast, But We Should See On Slower Machines. -# -# -# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please -# Write To Me At: -# -# gavana@kpo.kz -# andrea.gavana@gmail.com -# -# Or, Obviously, To The wxPython Mailing List!!! -# -# -# End Of Comments -# --------------------------------------------------------------------------------- # - - -""" -Description -=========== - -CustomTreeCtrl is a class that mimics the behaviour of wx.TreeCtrl, 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 tree control. -Apart of the base functionalities of CustomTreeCtrl (described below), in addition -to the standard wx.TreeCtrl behaviour this class supports: - -* CheckBox-type items: checkboxes are easy to handle, just selected or unselected - state with no particular issues in handling the item's children; - -* RadioButton-type items: since I elected to put radiobuttons in CustomTreeCtrl, I - needed some way to handle them, that made sense. So, I used the following approach: - - All peer-nodes that are radiobuttons will be mutually exclusive. In other words, - only one of a set of radiobuttons that share a common parent can be checked at - once. If a radiobutton node becomes checked, then all of its peer radiobuttons - must be unchecked. - - If a radiobutton node becomes unchecked, then all of its child nodes will become - inactive. - -* Hyperlink-type items: they look like an hyperlink, with the proper mouse cursor on - hovering. - -* Multiline text items. - -* Enabling/disabling items (together with their plain or grayed out icons). - -* Whatever non-toplevel widget can be attached next to an item. - -* Default selection style, gradient (horizontal/vertical) selection style and Windows - Vista selection style. - -* Customized drag and drop images built on the fly. - -* Setting the CustomTreeCtrl item buttons to a personalized imagelist. - -* Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist. - -* Changing the style of the lines that connect the items (in terms of wx.Pen styles). - -* Using an image as a CustomTreeCtrl background (currently only in "tile" mode). - -And a lot more. Check the demo for an almost complete review of the functionalities. - - -Base Functionalities -==================== - -CustomTreeCtrl supports all the wx.TreeCtrl styles, except: - - TR_EXTENDED: supports for this style is on the todo list (Am I sure of this?). - -Plus it has 3 more styles to handle checkbox-type items: - - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children; - - TR_AUTO_CHECK_PARENT : automatically checks/unchecks the item parent; - - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children. - -All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl. - - -Events -====== - -All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with -a few exceptions: - - - EVT_TREE_GET_INFO (don't know what this means); - - EVT_TREE_SET_INFO (don't know what this means); - - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add); - - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below. - -Plus, CustomTreeCtrl supports the events related to the checkbutton-type items: - - - EVT_TREE_ITEM_CHECKING: an item is being checked; - - EVT_TREE_ITEM_CHECKED: an item has been checked. - -And to hyperlink-type items: - - - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent - after the EVT_TREE_SEL_CHANGED event). - - -Supported Platforms -=================== - -CustomTreeCtrl has been tested on the following platforms: - * Windows (Windows XP); - * GTK (Thanks to Michele Petrazzo); - * Mac OS (Thanks to John Jackson). - - -Latest Revision: Andrea Gavana @ 16 Apr 2007, 11.00 CET -Version 1.0 - -""" - - -import wx -import zlib -import cStringIO - -# ---------------------------------------------------------------------------- -# Constants -# ---------------------------------------------------------------------------- - -_NO_IMAGE = -1 -_PIXELS_PER_UNIT = 10 - -# Start editing the current item after half a second (if the mouse hasn't -# been clicked/moved) -_DELAY = 500 - -# ---------------------------------------------------------------------------- -# Constants -# ---------------------------------------------------------------------------- - -# Enum for different images associated with a treectrl item -TreeItemIcon_Normal = 0 # not selected, not expanded -TreeItemIcon_Selected = 1 # selected, not expanded -TreeItemIcon_Expanded = 2 # not selected, expanded -TreeItemIcon_SelectedExpanded = 3 # selected, expanded - -TreeItemIcon_Checked = 0 # check button, checked -TreeItemIcon_NotChecked = 1 # check button, not checked -TreeItemIcon_Flagged = 2 # radio button, selected -TreeItemIcon_NotFlagged = 3 # radio button, not selected - -# ---------------------------------------------------------------------------- -# CustomTreeCtrl flags -# ---------------------------------------------------------------------------- - -TR_NO_BUTTONS = wx.TR_NO_BUTTONS # for convenience -TR_HAS_BUTTONS = wx.TR_HAS_BUTTONS # draw collapsed/expanded btns -TR_NO_LINES = wx.TR_NO_LINES # don't draw lines at all -TR_LINES_AT_ROOT = wx.TR_LINES_AT_ROOT # connect top-level nodes -TR_TWIST_BUTTONS = wx.TR_TWIST_BUTTONS # still used by wxTreeListCtrl - -TR_SINGLE = wx.TR_SINGLE # for convenience -TR_MULTIPLE = wx.TR_MULTIPLE # can select multiple items -TR_EXTENDED = wx.TR_EXTENDED # TODO: allow extended selection -TR_HAS_VARIABLE_ROW_HEIGHT = wx.TR_HAS_VARIABLE_ROW_HEIGHT # what it says - -TR_EDIT_LABELS = wx.TR_EDIT_LABELS # can edit item labels -TR_ROW_LINES = wx.TR_ROW_LINES # put border around items -TR_HIDE_ROOT = wx.TR_HIDE_ROOT # don't display root node - -TR_FULL_ROW_HIGHLIGHT = wx.TR_FULL_ROW_HIGHLIGHT # highlight full horz space - -TR_AUTO_CHECK_CHILD = 0x04000 # only meaningful for checkboxes -TR_AUTO_TOGGLE_CHILD = 0x08000 # only meaningful for checkboxes -TR_AUTO_CHECK_PARENT = 0x10000 # only meaningful for checkboxes - -TR_DEFAULT_STYLE = wx.TR_DEFAULT_STYLE # default style for the tree control - -# Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine -# where exactly the specified point is situated: - -TREE_HITTEST_ABOVE = wx.TREE_HITTEST_ABOVE -TREE_HITTEST_BELOW = wx.TREE_HITTEST_BELOW -TREE_HITTEST_NOWHERE = wx.TREE_HITTEST_NOWHERE -# on the button associated with an item. -TREE_HITTEST_ONITEMBUTTON = wx.TREE_HITTEST_ONITEMBUTTON -# on the bitmap associated with an item. -TREE_HITTEST_ONITEMICON = wx.TREE_HITTEST_ONITEMICON -# on the indent associated with an item. -TREE_HITTEST_ONITEMINDENT = wx.TREE_HITTEST_ONITEMINDENT -# on the label (string) associated with an item. -TREE_HITTEST_ONITEMLABEL = wx.TREE_HITTEST_ONITEMLABEL -# on the right of the label associated with an item. -TREE_HITTEST_ONITEMRIGHT = wx.TREE_HITTEST_ONITEMRIGHT -# on the label (string) associated with an item. -TREE_HITTEST_ONITEMSTATEICON = wx.TREE_HITTEST_ONITEMSTATEICON -# on the left of the CustomTreeCtrl. -TREE_HITTEST_TOLEFT = wx.TREE_HITTEST_TOLEFT -# on the right of the CustomTreeCtrl. -TREE_HITTEST_TORIGHT = wx.TREE_HITTEST_TORIGHT -# on the upper part (first half) of the item. -TREE_HITTEST_ONITEMUPPERPART = wx.TREE_HITTEST_ONITEMUPPERPART -# on the lower part (second half) of the item. -TREE_HITTEST_ONITEMLOWERPART = wx.TREE_HITTEST_ONITEMLOWERPART -# on the check icon, if present -TREE_HITTEST_ONITEMCHECKICON = 0x4000 -# anywhere on the item -TREE_HITTEST_ONITEM = TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON - - -# 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) - -# Flags for wx.RendererNative -_CONTROL_EXPANDED = 8 -_CONTROL_CURRENT = 16 - -# Version Info -__version__ = "0.8" - - -# ---------------------------------------------------------------------------- -# CustomTreeCtrl events and binding for handling them -# ---------------------------------------------------------------------------- - -wxEVT_TREE_BEGIN_DRAG = wx.wxEVT_COMMAND_TREE_BEGIN_DRAG -wxEVT_TREE_BEGIN_RDRAG = wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG -wxEVT_TREE_BEGIN_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT -wxEVT_TREE_END_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT -wxEVT_TREE_DELETE_ITEM = wx.wxEVT_COMMAND_TREE_DELETE_ITEM -wxEVT_TREE_GET_INFO = wx.wxEVT_COMMAND_TREE_GET_INFO -wxEVT_TREE_SET_INFO = wx.wxEVT_COMMAND_TREE_SET_INFO -wxEVT_TREE_ITEM_EXPANDED = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDED -wxEVT_TREE_ITEM_EXPANDING = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING -wxEVT_TREE_ITEM_COLLAPSED = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED -wxEVT_TREE_ITEM_COLLAPSING = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSING -wxEVT_TREE_SEL_CHANGED = wx.wxEVT_COMMAND_TREE_SEL_CHANGED -wxEVT_TREE_SEL_CHANGING = wx.wxEVT_COMMAND_TREE_SEL_CHANGING -wxEVT_TREE_KEY_DOWN = wx.wxEVT_COMMAND_TREE_KEY_DOWN -wxEVT_TREE_ITEM_ACTIVATED = wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED -wxEVT_TREE_ITEM_RIGHT_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK -wxEVT_TREE_ITEM_MIDDLE_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK -wxEVT_TREE_END_DRAG = wx.wxEVT_COMMAND_TREE_END_DRAG -wxEVT_TREE_STATE_IMAGE_CLICK = wx.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK -wxEVT_TREE_ITEM_GETTOOLTIP = wx.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP -wxEVT_TREE_ITEM_MENU = wx.wxEVT_COMMAND_TREE_ITEM_MENU -wxEVT_TREE_ITEM_CHECKING = wx.NewEventType() -wxEVT_TREE_ITEM_CHECKED = wx.NewEventType() -wxEVT_TREE_ITEM_HYPERLINK = wx.NewEventType() - -EVT_TREE_BEGIN_DRAG = wx.EVT_TREE_BEGIN_DRAG -EVT_TREE_BEGIN_RDRAG = wx.EVT_TREE_BEGIN_RDRAG -EVT_TREE_BEGIN_LABEL_EDIT = wx.EVT_TREE_BEGIN_LABEL_EDIT -EVT_TREE_END_LABEL_EDIT = wx.EVT_TREE_END_LABEL_EDIT -EVT_TREE_DELETE_ITEM = wx.EVT_TREE_DELETE_ITEM -EVT_TREE_GET_INFO = wx.EVT_TREE_GET_INFO -EVT_TREE_SET_INFO = wx.EVT_TREE_SET_INFO -EVT_TREE_ITEM_EXPANDED = wx.EVT_TREE_ITEM_EXPANDED -EVT_TREE_ITEM_EXPANDING = wx.EVT_TREE_ITEM_EXPANDING -EVT_TREE_ITEM_COLLAPSED = wx.EVT_TREE_ITEM_COLLAPSED -EVT_TREE_ITEM_COLLAPSING = wx.EVT_TREE_ITEM_COLLAPSING -EVT_TREE_SEL_CHANGED = wx.EVT_TREE_SEL_CHANGED -EVT_TREE_SEL_CHANGING = wx.EVT_TREE_SEL_CHANGING -EVT_TREE_KEY_DOWN = wx.EVT_TREE_KEY_DOWN -EVT_TREE_ITEM_ACTIVATED = wx.EVT_TREE_ITEM_ACTIVATED -EVT_TREE_ITEM_RIGHT_CLICK = wx.EVT_TREE_ITEM_RIGHT_CLICK -EVT_TREE_ITEM_MIDDLE_CLICK = wx.EVT_TREE_ITEM_MIDDLE_CLICK -EVT_TREE_END_DRAG = wx.EVT_TREE_END_DRAG -EVT_TREE_STATE_IMAGE_CLICK = wx.EVT_TREE_STATE_IMAGE_CLICK -EVT_TREE_ITEM_GETTOOLTIP = wx.EVT_TREE_ITEM_GETTOOLTIP -EVT_TREE_ITEM_MENU = wx.EVT_TREE_ITEM_MENU -EVT_TREE_ITEM_CHECKING = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKING, 1) -EVT_TREE_ITEM_CHECKED = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKED, 1) -EVT_TREE_ITEM_HYPERLINK = wx.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK, 1) - - -def GetFlaggedData(): - return zlib.decompress( -'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\ -\x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\ -|\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\ -\x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\ -\xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\ -\xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\ -\xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\ -\xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\ -\r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\ -\xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\ -\xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\ -\xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\ -\x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\ -\xb3\xb7H,a\xe1\xa9)\xdf36\x192\ -\xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\ -\xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\ -nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\ -\xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\ -\xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' ) - -def GetFlaggedBitmap(): - return wx.BitmapFromImage(GetFlaggedImage()) - -def GetFlaggedImage(): - stream = cStringIO.StringIO(GetFlaggedData()) - return wx.ImageFromStream(stream) - -#---------------------------------------------------------------------- -def GetNotFlaggedData(): - return zlib.decompress( -'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\ -\x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\ -|\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\ -\x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\ -\x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\ -\x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\ -\x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\ -\xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\ -\x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\ -\xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\ -\x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\ -\xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\ -\x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\ -\x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\ -\xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\ -\xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\ -\xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' ) - -def GetNotFlaggedBitmap(): - return wx.BitmapFromImage(GetNotFlaggedImage()) - -def GetNotFlaggedImage(): - stream = cStringIO.StringIO(GetNotFlaggedData()) - return wx.ImageFromStream(stream) - -#---------------------------------------------------------------------- -def GetCheckedData(): - return zlib.decompress( -"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\ -\x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\ -\x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\ -\xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\ -\x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\ -\xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\ -\xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\ -\xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\ -\x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\ -\xeb\x9c\x12\x9a\x00\x92\xccS\x9f" ) - -def GetCheckedBitmap(): - return wx.BitmapFromImage(GetCheckedImage()) - -def GetCheckedImage(): - stream = cStringIO.StringIO(GetCheckedData()) - return wx.ImageFromStream(stream) - -#---------------------------------------------------------------------- -def GetNotCheckedData(): - return zlib.decompress( -"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\ -\x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\ -\xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\ -\xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\ -\x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\ -\xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\ -\xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\ -\x00\x87S=\xbe" ) - -def GetNotCheckedBitmap(): - return wx.BitmapFromImage(GetNotCheckedImage()) - -def GetNotCheckedImage(): - stream = cStringIO.StringIO(GetNotCheckedData()) - return wx.ImageFromStream(stream) - - -def GrayOut(anImage): - """ - Convert the given image (in place) to a grayed-out version, - appropriate for a 'disabled' appearance. - """ - - factor = 0.7 # 0 < f < 1. Higher Is Grayer - - if anImage.HasMask(): - maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue()) - else: - maskColor = None - - data = map(ord, list(anImage.GetData())) - - for i in range(0, len(data), 3): - - pixel = (data[i], data[i+1], data[i+2]) - pixel = MakeGray(pixel, factor, maskColor) - - for x in range(3): - data[i+x] = pixel[x] - - anImage.SetData(''.join(map(chr, data))) - - return anImage - - -def MakeGray((r,g,b), factor, maskColor): - """ - Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be - changed. - """ - - if (r,g,b) != maskColor: - return map(lambda x: int((230 - x) * factor) + x, (r,g,b)) - else: - return (r,g,b) - - -def DrawTreeItemButton(win, dc, rect, flags): - """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """ - - # white background - dc.SetPen(wx.GREY_PEN) - dc.SetBrush(wx.WHITE_BRUSH) - dc.DrawRectangleRect(rect) - - # black lines - xMiddle = rect.x + rect.width/2 - yMiddle = rect.y + rect.height/2 - - # half of the length of the horz lines in "-" and "+" - halfWidth = rect.width/2 - 2 - dc.SetPen(wx.BLACK_PEN) - dc.DrawLine(xMiddle - halfWidth, yMiddle, - xMiddle + halfWidth + 1, yMiddle) - - if not flags & _CONTROL_EXPANDED: - - # turn "-" into "+" - halfHeight = rect.height/2 - 2 - dc.DrawLine(xMiddle, yMiddle - halfHeight, - xMiddle, yMiddle + halfHeight + 1) - - -#--------------------------------------------------------------------------- -# DragImage Implementation -# This Class Handles The Creation Of A Custom Image In Case Of Item Drag -# And Drop. -#--------------------------------------------------------------------------- - -class DragImage(wx.DragImage): - """ - This class handles the creation of a custom image in case of item drag - and drop. - """ - - def __init__(self, treeCtrl, item): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - text = item.GetText() - font = item.Attr().GetFont() - colour = item.Attr().GetTextColour() - if not colour: - colour = wx.BLACK - if not font: - font = treeCtrl._normalFont - - backcolour = treeCtrl.GetBackgroundColour() - r, g, b = int(backcolour.Red()), int(backcolour.Green()), int(backcolour.Blue()) - backcolour = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) - backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2]) - self._backgroundColour = backcolour - - tempdc = wx.ClientDC(treeCtrl) - tempdc.SetFont(font) - width, height, dummy = tempdc.GetMultiLineTextExtent(text + "M") - - image = item.GetCurrentImage() - - image_w, image_h = 0, 0 - wcheck, hcheck = 0, 0 - itemcheck = None - itemimage = None - ximagepos = 0 - yimagepos = 0 - xcheckpos = 0 - ycheckpos = 0 - - if image != _NO_IMAGE: - if treeCtrl._imageListNormal: - image_w, image_h = treeCtrl._imageListNormal.GetSize(image) - image_w += 4 - itemimage = treeCtrl._imageListNormal.GetBitmap(image) - - checkimage = item.GetCurrentCheckedImage() - - if checkimage is not None: - if treeCtrl._imageListCheck: - wcheck, hcheck = treeCtrl._imageListCheck.GetSize(checkimage) - wcheck += 4 - itemcheck = treeCtrl._imageListCheck.GetBitmap(checkimage) - - total_h = max(hcheck, height) - total_h = max(image_h, total_h) - - if image_w: - ximagepos = wcheck - yimagepos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] - - if checkimage is not None: - xcheckpos = 2 - ycheckpos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] + 2 - - extraH = ((total_h > height) and [(total_h - height)/2] or [0])[0] - - xtextpos = wcheck + image_w - ytextpos = extraH - - total_h = max(image_h, hcheck) - total_h = max(total_h, height) - - if total_h < 30: - total_h += 2 # at least 2 pixels - else: - total_h += total_h/10 # otherwise 10% extra spacing - - total_w = image_w + wcheck + width - - self._total_w = total_w - self._total_h = total_h - self._itemimage = itemimage - self._itemcheck = itemcheck - self._text = text - self._colour = colour - self._font = font - self._xtextpos = xtextpos - self._ytextpos = ytextpos - self._ximagepos = ximagepos - self._yimagepos = yimagepos - self._xcheckpos = xcheckpos - self._ycheckpos = ycheckpos - self._textwidth = width - self._textheight = height - self._extraH = extraH - - self._bitmap = self.CreateBitmap() - - wx.DragImage.__init__(self, self._bitmap) - - - def CreateBitmap(self): - """Actually creates the dnd bitmap.""" - - memory = wx.MemoryDC() - - bitmap = wx.EmptyBitmap(self._total_w, self._total_h) - memory.SelectObject(bitmap) - - memory.SetTextBackground(self._backgroundColour) - memory.SetBackground(wx.Brush(self._backgroundColour)) - memory.SetFont(self._font) - memory.SetTextForeground(self._colour) - memory.Clear() - - if self._itemimage: - memory.DrawBitmap(self._itemimage, self._ximagepos, self._yimagepos, True) - - if self._itemcheck: - memory.DrawBitmap(self._itemcheck, self._xcheckpos, self._ycheckpos, True) - - textrect = wx.Rect(self._xtextpos, self._ytextpos+self._extraH, self._textwidth, self._textheight) - memory.DrawLabel(self._text, textrect) - - memory.SelectObject(wx.NullBitmap) - - return bitmap - - -# ---------------------------------------------------------------------------- -# TreeItemAttr: a structure containing the visual attributes of an item -# ---------------------------------------------------------------------------- - -class TreeItemAttr: - """Creates the item attributes (text colour, background colour and font).""" - - def __init__(self, colText=wx.NullColour, colBack=wx.NullColour, font=wx.NullFont): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - self._colText = colText - self._colBack = colBack - self._font = font - - # setters - def SetTextColour(self, colText): - """Sets the attribute text colour.""" - - self._colText = colText - - - def SetBackgroundColour(self, colBack): - """Sets the attribute background colour.""" - - self._colBack = colBack - - - def SetFont(self, font): - """Sets the attribute font.""" - - self._font = font - - - # accessors - def HasTextColour(self): - """Returns whether the attribute has text colour.""" - - return self._colText != wx.NullColour - - - def HasBackgroundColour(self): - """Returns whether the attribute has background colour.""" - - return self._colBack != wx.NullColour - - - def HasFont(self): - """Returns whether the attribute has font.""" - - return self._font != wx.NullFont - - - # getters - def GetTextColour(self): - """Returns the attribute text colour.""" - - return self._colText - - - def GetBackgroundColour(self): - """Returns the attribute background colour.""" - - return self._colBack - - - def GetFont(self): - """Returns the attribute font.""" - - return self._font - - -# ---------------------------------------------------------------------------- -# CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent -# -# NB: Note That Not All The Accessors Make Sense For All The Events, See The -# Event Description Below. -# ---------------------------------------------------------------------------- - -class CommandTreeEvent(wx.PyCommandEvent): - """ - CommandTreeEvent is a special subclassing of wx.PyCommandEvent. - NB: note that not all the accessors make sense for all the events, see the - event description for every method in this class. - """ - - def __init__(self, type, id, item=None, evtKey=None, point=None, - label=None, **kwargs): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - wx.PyCommandEvent.__init__(self, type, id, **kwargs) - self._item = item - self._evtKey = evtKey - self._pointDrag = point - self._label = label - - - def GetItem(self): - """ - Gets the item on which the operation was performed or the newly selected - item for EVT_TREE_SEL_CHANGED/ING events. - """ - - return self._item - - - def SetItem(self, item): - """ - Sets the item on which the operation was performed or the newly selected - item for EVT_TREE_SEL_CHANGED/ING events. - """ - - self._item = item - - - def GetOldItem(self): - """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item.""" - - return self._itemOld - - - def SetOldItem(self, item): - """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item.""" - - self._itemOld = item - - - def GetPoint(self): - """ - Returns the point where the mouse was when the drag operation started - (for EVT_TREE_BEGIN(R)DRAG events only) or the click position. - """ - - return self._pointDrag - - - def SetPoint(self, pt): - """ - Sets the point where the mouse was when the drag operation started - (for EVT_TREE_BEGIN(R)DRAG events only) or the click position. - """ - - self._pointDrag = pt - - - def GetKeyEvent(self): - """Keyboard data (for EVT_TREE_KEY_DOWN only).""" - - return self._evtKey - - - def GetKeyCode(self): - """Returns the integer key code (for EVT_TREE_KEY_DOWN only).""" - - return self._evtKey.GetKeyCode() - - - def SetKeyEvent(self, evt): - """Keyboard data (for EVT_TREE_KEY_DOWN only).""" - - self._evtKey = evt - - - def GetLabel(self): - """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" - - return self._label - - - def SetLabel(self, label): - """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" - - self._label = label - - - def IsEditCancelled(self): - """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" - - return self._editCancelled - - - def SetEditCanceled(self, editCancelled): - """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" - - self._editCancelled = editCancelled - - - def SetToolTip(self, toolTip): - """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events).""" - - self._label = toolTip - - - def GetToolTip(self): - """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events).""" - - return self._label - - -# ---------------------------------------------------------------------------- -# TreeEvent is a special class for all events associated with tree controls -# -# NB: note that not all accessors make sense for all events, see the event -# descriptions below -# ---------------------------------------------------------------------------- - -class TreeEvent(CommandTreeEvent): - - def __init__(self, type, id, item=None, evtKey=None, point=None, - label=None, **kwargs): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - CommandTreeEvent.__init__(self, type, id, item, evtKey, point, label, **kwargs) - self.notify = wx.NotifyEvent(type, id) - - - def GetNotifyEvent(self): - """Returns the actual wx.NotifyEvent.""" - - return self.notify - - - def IsAllowed(self): - """Returns whether the event is allowed or not.""" - - return self.notify.IsAllowed() - - - def Veto(self): - """Vetos the event.""" - - self.notify.Veto() - - - def Allow(self): - """The event is allowed.""" - - self.notify.Allow() - - -# ----------------------------------------------------------------------------- -# Auxiliary Classes: TreeRenameTimer -# ----------------------------------------------------------------------------- - -class TreeRenameTimer(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! - """ - - wx.Timer.__init__(self) - self._owner = owner - - - def Notify(self): - """The timer has expired.""" - - self._owner.OnRenameTimer() - - -# ----------------------------------------------------------------------------- -# Auxiliary Classes: TreeTextCtrl -# This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item -# ----------------------------------------------------------------------------- - -class TreeTextCtrl(wx.TextCtrl): - """Control used for in-place edit.""" - - def __init__(self, owner, item=None): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - self._owner = owner - self._itemEdited = item - self._startValue = item.GetText() - self._finished = False - self._aboutToFinish = False - - w = self._itemEdited.GetWidth() - h = self._itemEdited.GetHeight() - - wnd = self._itemEdited.GetWindow() - if wnd: - w = w - self._itemEdited.GetWindowSize()[0] - h = 0 - - x, y = self._owner.CalcScrolledPosition(item.GetX(), item.GetY()) - - image_h = 0 - image_w = 0 - - image = item.GetCurrentImage() - - if image != _NO_IMAGE: - - if self._owner._imageListNormal: - image_w, image_h = self._owner._imageListNormal.GetSize(image) - image_w += 4 - - else: - - raise Exception("\n ERROR: You Must Create An Image List To Use Images!") - - checkimage = item.GetCurrentCheckedImage() - - if checkimage is not None: - wcheck, hcheck = self._owner._imageListCheck.GetSize(checkimage) - wcheck += 4 - else: - wcheck = 0 - - if wnd: - h = max(hcheck, image_h) - dc = wx.ClientDC(self._owner) - h = max(h, dc.GetTextExtent("Aq")[1]) - h = h + 2 - - # FIXME: what are all these hardcoded 4, 8 and 11s really? - x += image_w + wcheck - w -= image_w + 4 + wcheck - - wx.TextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue, - wx.Point(x - 4, y), wx.Size(w + 15, h)) - if wx.Platform == "__WXMAC__": - self.SetFont(owner.GetFont()) - bs = self.GetBestSize() - self.SetSize((-1, bs.height)) - - 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) - - return True - - - def Finish(self): - """Finish editing.""" - - if not self._finished: - -## wxPendingDelete.Append(this) - self._finished = True - self._owner.SetFocusIgnoringChildren() - self._owner.ResetTextControl() - - - def OnChar(self, event): - """Handles the wx.EVT_CHAR event for TreeTextCtrl.""" - - keycode = event.GetKeyCode() - - if keycode == wx.WXK_RETURN: - self._aboutToFinish = True - # 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 TreeTextCtrl.""" - - if not self._finished: - - # auto-grow the textctrl: - parentSize = self._owner.GetSize() - myPos = self.GetPosition() - mySize = self.GetSize() - - sx, sy = self.GetTextExtent(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)) - - event.Skip() - - - def OnKillFocus(self, event): - """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl.""" - - # I commented out those lines, and everything seems to work fine. - # But why in the world are these lines of code here? Maybe GTK - # or MAC give troubles? - -## 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() - - - def StopEditing(self): - """Suddenly stops the editing.""" - - self._owner.OnRenameCancelled(self._itemEdited) - self.Finish() - - - def item(self): - """Returns the item currently edited.""" - - return self._itemEdited - - -# ----------------------------------------------------------------------------- -# Auxiliary Classes: TreeFindTimer -# Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A -# Sufficiently Long Time. -# ----------------------------------------------------------------------------- - -class TreeFindTimer(wx.Timer): - """ - Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed - for a sufficiently long time. - """ - - def __init__(self, owner): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - wx.Timer.__init__(self) - self._owner = owner - - - def Notify(self): - """The timer has expired.""" - - self._owner._findPrefix = "" - - -# ----------------------------------------------------------------------------- -# GenericTreeItem Implementation. -# This Class Holds All The Information And Methods For Every Single Item In -# CustomTreeCtrl. -# ----------------------------------------------------------------------------- - -class GenericTreeItem: - """ - This class holds all the information and methods for every single item in - CustomTreeCtrl. No wx based. - """ - - def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """ - Default class constructor. - For internal use: do not call it in your code! - """ - - # since there can be very many of these, we save size by chosing - # the smallest representation for the elements and by ordering - # the members to avoid padding. - self._text = text # label to be rendered for item - self._data = data # user-provided data - - self._children = [] # list of children - self._parent = parent # parent of this item - - self._attr = None # attributes??? - - # tree ctrl images for the normal, selected, expanded and - # expanded+selected states - self._images = [-1, -1, -1, -1] - self._images[TreeItemIcon_Normal] = image - self._images[TreeItemIcon_Selected] = selImage - self._images[TreeItemIcon_Expanded] = _NO_IMAGE - self._images[TreeItemIcon_SelectedExpanded] = _NO_IMAGE - - self._checkedimages = [None, None, None, None] - - self._x = 0 # (virtual) offset from top - self._y = 0 # (virtual) offset from left - self._width = 0 # width of this item - self._height = 0 # height of this item - - self._isCollapsed = True - self._hasHilight = False # same as focused - self._hasPlus = False # used for item which doesn't have - # children but has a [+] button - self._isBold = False # render the label in bold font - self._isItalic = False # render the label in italic font - self._ownsAttr = False # delete attribute when done - self._type = ct_type # item type: 0=normal, 1=check, 2=radio - self._checked = False # only meaningful for check and radio - self._enabled = True # flag to enable/disable an item - self._hypertext = False # indicates if the item is hypertext - self._visited = False # visited state for an hypertext item - - if self._type > 0: - # do not construct the array for normal items - self._checkedimages[TreeItemIcon_Checked] = 0 - self._checkedimages[TreeItemIcon_NotChecked] = 1 - self._checkedimages[TreeItemIcon_Flagged] = 2 - self._checkedimages[TreeItemIcon_NotFlagged] = 3 - - if parent: - if parent.GetType() == 2 and not parent.IsChecked(): - # if the node parent is a radio not enabled, we are disabled - self._enabled = False - - self._wnd = wnd # are we holding a window? - - if wnd: - self.SetWindow(wnd) - - - def IsOk(self): - """ - Returns whether the item is ok or not. Useless on Python, but added for - backward compatibility with the C++ implementation. - """ - - return True - - - def GetChildren(self): - """Returns the item's children.""" - - return self._children - - - def GetText(self): - """Returns the item text.""" - - return self._text - - - def GetImage(self, which=TreeItemIcon_Normal): - """Returns the item image for a particular state.""" - - return self._images[which] - - - def GetCheckedImage(self, which=TreeItemIcon_Checked): - """Returns the item check image. Meaningful only for radio & check items.""" - - return self._checkedimages[which] - - - def GetData(self): - """Returns the data associated to this item.""" - - return self._data - - - def SetImage(self, image, which): - """Sets the item image.""" - - self._images[which] = image - - - def SetData(self, data): - """Sets the data associated to this item.""" - - self._data = data - - - def SetHasPlus(self, has=True): - """Sets whether an item has the 'plus' button.""" - - self._hasPlus = has - - - def SetBold(self, bold): - """Sets the item font bold.""" - - self._isBold = bold - - - def SetItalic(self, italic): - """Sets the item font italic.""" - - self._isItalic = italic - - - def GetX(self): - """Returns the x position on an item in the ScrolledWindow.""" - - return self._x - - - def GetY(self): - """Returns the y position on an item in the ScrolledWindow.""" - - return self._y - - - def SetX(self, x): - """Sets the x position on an item in the ScrolledWindow.""" - - self._x = x - - - def SetY(self, y): - """Sets the y position on an item in the ScrolledWindow.""" - - self._y = y - - - def GetHeight(self): - """Returns the height of the item.""" - - return self._height - - - def GetWidth(self): - """Returns the width of the item.""" - - return self._width - - - def SetHeight(self, h): - """Sets the height of the item.""" - - self._height = h - - - def SetWidth(self, w): - """Sets the width of the item.""" - - self._width = w - - - def SetWindow(self, wnd): - """Sets the window associated to the item.""" - - 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 - # CustomTreeCtrl and the window associated to an item - # Do better strategies exist? - self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) - - self._height = size.GetHeight() + 2 - self._width = size.GetWidth() - self._windowsize = size - - # We don't show the window if the item is collapsed - if self._isCollapsed: - self._wnd.Show(False) - - # The window is enabled only if the item is enabled - self._wnd.Enable(self._enabled) - self._windowenabled = self._enabled - - - 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.""" - - 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 OnSetFocus(self, event): - """Handles the wx.EVT_SET_FOCUS event for the associated window.""" - - treectrl = self._wnd.GetParent() - select = treectrl.GetSelection() - - # 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 select != self: - treectrl._hasFocus = False - else: - treectrl._hasFocus = True - - event.Skip() - - - def GetType(self): - """ - Returns the item type. It should be one of: - 0: normal items - 1: checkbox item - 2: radiobutton item - """ - - return self._type - - - def SetHyperText(self, hyper=True): - """Sets whether the item is hypertext or not.""" - - self._hypertext = hyper - - - def SetVisited(self, visited=True): - """Sets whether an hypertext item was visited or not.""" - - 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 GetParent(self): - """Gets the item parent.""" - - return self._parent - - - def Insert(self, child, index): - """Inserts an item in the item children.""" - - self._children.insert(index, child) - - - def Expand(self): - """Expand the item.""" - - self._isCollapsed = False - - - def Collapse(self): - """Collapse the item.""" - - self._isCollapsed = True - - - def SetHilight(self, set=True): - """Sets the item focus/unfocus.""" - - self._hasHilight = set - - - def HasChildren(self): - """Returns whether the item has children or not.""" - - return len(self._children) > 0 - - - def IsSelected(self): - """Returns whether the item is selected or not.""" - - return self._hasHilight != 0 - - - def IsExpanded(self): - """Returns whether the item is expanded or not.""" - - return not self._isCollapsed - - - def IsChecked(self): - """Returns whether the item is checked or not.""" - - return self._checked - - - def Check(self, checked=True): - """Check an item. Meaningful only for check and radio items.""" - - self._checked = checked - - - def HasPlus(self): - """Returns whether the item has the plus button or not.""" - - return self._hasPlus or self.HasChildren() - - - def IsBold(self): - """Returns whether the item font is bold or not.""" - - return self._isBold != 0 - - - def IsItalic(self): - """Returns whether the item font is italic or not.""" - - return self._isItalic != 0 - - - def Enable(self, enable=True): - """Enables/disables the item.""" - - self._enabled = enable - - - def IsEnabled(self): - """Returns whether the item is enabled or not.""" - - return self._enabled - - - def GetAttributes(self): - """Returns the item attributes (font, colours).""" - - return self._attr - - - def Attr(self): - """Creates a new attribute (font, colours).""" - - if not self._attr: - - self._attr = TreeItemAttr() - self._ownsAttr = True - - return self._attr - - - def SetAttributes(self, attr): - """Sets the item attributes (font, colours).""" - - if self._ownsAttr: - del self._attr - - self._attr = attr - self._ownsAttr = False - - - def AssignAttributes(self, attr): - """Assigns the item attributes (font, colours).""" - - self.SetAttributes(attr) - self._ownsAttr = True - - - def DeleteChildren(self, tree): - """Deletes the item children.""" - - for child in self._children: - if tree: - tree.SendDeleteEvent(child) - - child.DeleteChildren(tree) - - if child == tree._select_me: - tree._select_me = None - - # We have to destroy the associated window - wnd = child.GetWindow() - if wnd: - wnd.Destroy() - child._wnd = None - - if child in tree._itemWithWindow: - tree._itemWithWindow.remove(child) - - del child - - self._children = [] - - - def SetText(self, text): - """Sets the item text.""" - - self._text = text - - - def GetChildrenCount(self, recursively=True): - """Gets the number of children.""" - - count = len(self._children) - - if not recursively: - return count - - total = count - - for n in xrange(count): - total += self._children[n].GetChildrenCount() - - return total - - - def GetSize(self, x, y, theButton): - """Returns the item size.""" - - bottomY = self._y + theButton.GetLineHeight(self) - - if y < bottomY: - y = bottomY - - width = self._x + self._width - - if x < width: - x = width - - if self.IsExpanded(): - for child in self._children: - x, y = child.GetSize(x, y, theButton) - - return x, y - - - def HitTest(self, point, theCtrl, flags=0, level=0): - """ - HitTest method for an item. Called from the main window HitTest. - see the CustomTreeCtrl HitTest method for the flags explanation. - """ - - # for a hidden root node, don't evaluate it, but do evaluate children - if not (level == 0 and theCtrl.HasFlag(TR_HIDE_ROOT)): - - # evaluate the item - h = theCtrl.GetLineHeight(self) - - if point.y > self._y and point.y < self._y + h: - - y_mid = self._y + h/2 - - if point.y < y_mid: - flags |= TREE_HITTEST_ONITEMUPPERPART - else: - flags |= TREE_HITTEST_ONITEMLOWERPART - - xCross = self._x - theCtrl.GetSpacing() - - if wx.Platform == "__WXMAC__": - # according to the drawing code the triangels are drawn - # at -4 , -4 from the position up to +10/+10 max - if point.x > xCross-4 and point.x < xCross+10 and point.y > y_mid-4 and \ - point.y < y_mid+10 and self.HasPlus() and theCtrl.HasButtons(): - - flags |= TREE_HITTEST_ONITEMBUTTON - return self, flags - else: - # 5 is the size of the plus sign - if point.x > xCross-6 and point.x < xCross+6 and point.y > y_mid-6 and \ - point.y < y_mid+6 and self.HasPlus() and theCtrl.HasButtons(): - - flags |= TREE_HITTEST_ONITEMBUTTON - return self, flags - - if point.x >= self._x and point.x <= self._x + self._width: - - image_w = -1 - wcheck = 0 - - # assuming every image (normal and selected) has the same size! - if self.GetImage() != _NO_IMAGE and theCtrl._imageListNormal: - image_w, image_h = theCtrl._imageListNormal.GetSize(self.GetImage()) - - if self.GetCheckedImage() is not None: - wcheck, hcheck = theCtrl._imageListCheck.GetSize(self.GetCheckedImage()) - - if wcheck and point.x <= self._x + wcheck + 1: - flags |= TREE_HITTEST_ONITEMCHECKICON - return self, flags - - if image_w != -1 and point.x <= self._x + wcheck + image_w + 1: - flags |= TREE_HITTEST_ONITEMICON - else: - flags |= TREE_HITTEST_ONITEMLABEL - - return self, flags - - if point.x < self._x: - flags |= TREE_HITTEST_ONITEMINDENT - if point.x > self._x + self._width: - flags |= TREE_HITTEST_ONITEMRIGHT - - return self, flags - - # if children are expanded, fall through to evaluate them - if self._isCollapsed: - return None, 0 - - # evaluate children - for child in self._children: - res, flags = child.HitTest(point, theCtrl, flags, level + 1) - if res != None: - return res, flags - - return None, 0 - - - def GetCurrentImage(self): - """Returns the current item image.""" - - image = _NO_IMAGE - - if self.IsExpanded(): - - if self.IsSelected(): - - image = self.GetImage(TreeItemIcon_SelectedExpanded) - - if image == _NO_IMAGE: - - # we usually fall back to the normal item, but try just the - # expanded one (and not selected) first in this case - image = self.GetImage(TreeItemIcon_Expanded) - - else: # not expanded - - if self.IsSelected(): - image = self.GetImage(TreeItemIcon_Selected) - - # maybe it doesn't have the specific image we want, - # try the default one instead - if image == _NO_IMAGE: - image = self.GetImage() - - return image - - - def GetCurrentCheckedImage(self): - """Returns the current item check image.""" - - if self._type == 0: - return None - - if self.IsChecked(): - if self._type == 1: # Checkbox - return self._checkedimages[TreeItemIcon_Checked] - else: # Radiobutton - return self._checkedimages[TreeItemIcon_Flagged] - else: - if self._type == 1: # Checkbox - return self._checkedimages[TreeItemIcon_NotChecked] - else: # Radiobutton - return self._checkedimages[TreeItemIcon_NotFlagged] - - -def EventFlagsToSelType(style, shiftDown=False, ctrlDown=False): - """ - Translate the key or mouse event flag to the type of selection we - are dealing with. - """ - - is_multiple = (style & TR_MULTIPLE) != 0 - extended_select = shiftDown and is_multiple - unselect_others = not (extended_select or (ctrlDown and is_multiple)) - - return is_multiple, extended_select, unselect_others - - -# ----------------------------------------------------------------------------- -# CustomTreeCtrl Main Implementation. -# This Is The Main Class. -# ----------------------------------------------------------------------------- - -class CustomTreeCtrl(wx.PyScrolledWindow): - - def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, - style=TR_DEFAULT_STYLE, ctstyle=0, validator=wx.DefaultValidator, - name="CustomTreeCtrl"): - """ - Default class constructor. - - parent: parent window. Must not be none. - - id: window identifier. A value of -1 indicates a default value. - - pos: window position. - - size: window size. If the default size (-1, -1) is specified then the window is sized appropriately. - - style: the underlying wx.ScrolledWindow style + CustomTreeCtrl window style. This can be one of: - - TR_NO_BUTTONS - TR_HAS_BUTTONS # draw collapsed/expanded btns - TR_NO_LINES # don't draw lines at all - TR_LINES_AT_ROOT # connect top-level nodes - TR_TWIST_BUTTONS # draw mac-like twist buttons - TR_SINGLE # single selection mode - TR_MULTIPLE # can select multiple items - TR_EXTENDED # todo: allow extended selection - TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height - TR_EDIT_LABELS # can edit item labels - TR_ROW_LINES # put border around items - TR_HIDE_ROOT # don't display root node - TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space - TR_AUTO_CHECK_CHILD # only meaningful for checkboxes - TR_AUTO_CHECK_PARENT # only meaningful for checkboxes - TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes - - ctstyle: kept for backward compatibility. - - validator: window validator. - - name: window name. - """ - - style = style | ctstyle - - self._current = self._key_current = self._anchor = self._select_me = None - self._hasFocus = False - self._dirty = False - - # Default line height: it will soon be changed - self._lineHeight = 10 - # Item indent wrt parent - self._indent = 15 - # item horizontal spacing between the start and the text - self._spacing = 18 - - # Brushes for focused/unfocused items (also gradient type) - self._hilightBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) - btnshadow = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW) - self._hilightUnfocusedBrush = wx.Brush(btnshadow) - 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._hilightUnfocusedBrush2 = wx.Brush(backcolour) - - # image list for icons - self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = None - self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = False - - # Drag and drop initial settings - self._dragCount = 0 - self._countDrag = 0 - self._isDragging = False - self._dropTarget = self._oldSelection = None - self._dragImage = None - self._underMouse = None - - # TextCtrl initial settings for editable items - self._textCtrl = None - self._renameTimer = None - - # This one allows us to handle Freeze() and Thaw() calls - self._freezeCount = 0 - - self._findPrefix = "" - self._findTimer = None - - self._dropEffectAboveItem = False - self._lastOnSame = False - - # Default normal and bold fonts for an item - self._hasFont = True - self._normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), - self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(), - self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) - - - # Hyperlinks things - self._hypertextfont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), - self._normalFont.GetStyle(), wx.NORMAL, True, - self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) - self._hypertextnewcolour = wx.BLUE - self._hypertextvisitedcolour = wx.Colour(200, 47, 200) - self._isonhyperlink = False - - # Default CustomTreeCtrl background colour. - self._backgroundColour = wx.WHITE - - # Background image settings - self._backgroundImage = None - self._imageStretchStyle = _StyleTile - - # Disabled items colour - self._disabledColour = wx.Colour(180, 180, 180) - - # Gradient selection colours - self._firstcolour = color= wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) - self._secondcolour = wx.WHITE - self._usegradients = False - self._gradientstyle = 0 # Horizontal Gradient - - # Vista Selection Styles - self._vistaselection = False - - # Connection lines style - grey = (160,160,160) - if wx.Platform != "__WXMAC__": - self._dottedPen = wx.Pen(grey, 1, wx.USER_DASH) - self._dottedPen.SetDashes([1,1]) - self._dottedPen.SetCap(wx.CAP_BUTT) - else: - self._dottedPen = wx.Pen(grey, 1) - - # Pen Used To Draw The Border Around Selected Items - self._borderPen = wx.BLACK_PEN - self._cursor = wx.StockCursor(wx.CURSOR_ARROW) - - # For Appended Windows - self._hasWindows = False - self._itemWithWindow = [] - - if wx.Platform == "__WXMAC__": - style &= ~TR_LINES_AT_ROOT - style |= TR_NO_LINES - - platform, major, minor = wx.GetOsVersion() - if major < 10: - style |= TR_ROW_LINES - - # Create the default check image list - self.SetImageListCheck(13, 13) - - # A constant to use my translation of RendererNative.DrawTreeItemButton - # if the wxPython version is less than 2.6.2.1. - if wx.VERSION_STRING < "2.6.2.1": - self._drawingfunction = DrawTreeItemButton - else: - self._drawingfunction = wx.RendererNative.Get().DrawTreeItemButton - - # Create our container... at last! - wx.PyScrolledWindow.__init__(self, parent, id, pos, size, style|wx.HSCROLL|wx.VSCROLL, name) - - # If the tree display has no buttons, but does have - # connecting lines, we can use a narrower layout. - # It may not be a good idea to force this... - if not self.HasButtons() and not self.HasFlag(TR_NO_LINES): - self._indent= 10 - self._spacing = 10 - - self.SetValidator(validator) - - attr = self.GetDefaultAttributes() - self.SetOwnForegroundColour(attr.colFg) - self.SetOwnBackgroundColour(wx.WHITE) - - if not self._hasFont: - self.SetOwnFont(attr.font) - - self.SetSize(size) - - # Bind the events - 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_KEY_DOWN, self.OnKeyDown) - self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) - self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) - self.Bind(EVT_TREE_ITEM_GETTOOLTIP, self.OnGetToolTip) - self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) - - # Sets the focus to ourselves: this is useful if you have items - # with associated widgets. - self.SetFocus() - - - def AcceptsFocus(self): - # overridden base class method, allows this ctrl to - # participate in the tab-order, etc. It's overridable because - # of deriving this class from wx.PyScrolledWindow... - return True - - - def OnDestroy(self, event): - """Handles the wx.EVT_WINDOW_DESTROY event.""" - - # Here there may be something I miss... do I have to destroy - # something else? - if self._renameTimer and self._renameTimer.IsRunning(): - self._renameTimer.Stop() - del self._renameTimer - - if self._findTimer and self._findTimer.IsRunning(): - self._findTimer.Stop() - del self._findTimer - - event.Skip() - - - def GetCount(self): - """Returns the global number of items in the tree.""" - - if not self._anchor: - # the tree is empty - return 0 - - count = self._anchor.GetChildrenCount() - - if not self.HasFlag(TR_HIDE_ROOT): - # take the root itself into account - count = count + 1 - - return count - - - def GetIndent(self): - """Returns the item indentation.""" - - return self._indent - - - def GetSpacing(self): - """Returns the spacing between the start and the text.""" - - return self._spacing - - - def GetRootItem(self): - """Returns the root item.""" - - return self._anchor - - - def GetSelection(self): - """Returns the current selection: TR_SINGLE only.""" - - return self._current - - - def ToggleItemSelection(self, item): - """Toggles the item selection.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - self.SelectItem(item, not self.IsSelected(item)) - - - def EnableChildren(self, item, enable=True): - """Enables/disables item children. Used internally.""" - - torefresh = False - if item.IsExpanded(): - torefresh = True - - if item.GetType() == 2 and enable and not item.IsChecked(): - # We hit a radiobutton item not checked, we don't want to - # enable the children - return - - child, cookie = self.GetFirstChild(item) - while child: - self.EnableItem(child, enable, torefresh=torefresh) - # Recurse on tree - if child.GetType != 2 or (child.GetType() == 2 and item.IsChecked()): - self.EnableChildren(child, enable) - (child, cookie) = self.GetNextChild(item, cookie) - - - def EnableItem(self, item, enable=True, torefresh=True): - """Enables/disables an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if item.IsEnabled() == enable: - return - - if not enable and item.IsSelected(): - self.SelectItem(item, False) - - item.Enable(enable) - wnd = item.GetWindow() - - # Handles the eventual window associated to the item - if wnd: - wndenable = item.GetWindowEnabled() - if enable: - if wndenable: - wnd.Enable(enable) - else: - wnd.Enable(enable) - - if torefresh: - # We have to refresh the item line - dc = wx.ClientDC(self) - self.CalculateSize(item, dc) - self.RefreshLine(item) - - - def IsItemEnabled(self, item): - """Returns whether an item is enabled or disabled.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsEnabled() - - - def SetDisabledColour(self, colour): - """Sets the items disabled colour.""" - - self._disabledColour = colour - self._dirty = True - - - def GetDisabledColour(self): - """Returns the items disabled colour.""" - - return self._disabledColour - - - def IsItemChecked(self, item): - """Returns whether an item is checked or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsChecked() - - - def CheckItem2(self, item, checked=True, torefresh=False): - """Used internally to avoid EVT_TREE_ITEM_CHECKED events.""" - - if item.GetType() == 0: - return - - item.Check(checked) - - if torefresh: - dc = wx.ClientDC(self) - self.CalculateSize(item, dc) - self.RefreshLine(item) - - - def UnCheckRadioParent(self, item, checked=False): - """Used internally to handle radio node parent correctly.""" - - e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId()) - e.SetItem(item) - e.SetEventObject(self) - - if self.GetEventHandler().ProcessEvent(e): - return False - - item.Check(checked) - self.RefreshLine(item) - self.EnableChildren(item, checked) - e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId()) - e.SetItem(item) - e.SetEventObject(self) - self.GetEventHandler().ProcessEvent(e) - - return True - - - def CheckItem(self, item, checked=True): - """ - Actually checks/uncheks an item, sending (eventually) the two - events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED. - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - # Should we raise an error here?!? - if item.GetType() == 0: - return - - if item.GetType() == 2: # it's a radio button - if not checked and item.IsChecked(): # Try To Unckeck? - if item.HasChildren(): - self.UnCheckRadioParent(item, checked) - return - else: - if not self.UnCheckRadioParent(item, checked): - return - - self.CheckSameLevel(item, False) - return - - # Radiobuttons are done, let's handle checkbuttons... - e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId()) - e.SetItem(item) - e.SetEventObject(self) - - if self.GetEventHandler().ProcessEvent(e): - # Blocked by user - return - - item.Check(checked) - dc = wx.ClientDC(self) - self.RefreshLine(item) - - if self.HasFlag(TR_AUTO_CHECK_CHILD): - ischeck = self.IsItemChecked(item) - self.AutoCheckChild(item, ischeck) - if self.HasFlag(TR_AUTO_CHECK_PARENT): - ischeck = self.IsItemChecked(item) - self.AutoCheckParent(item, ischeck) - elif self.HasFlag(TR_AUTO_TOGGLE_CHILD): - self.AutoToggleChild(item) - - e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId()) - e.SetItem(item) - e.SetEventObject(self) - self.GetEventHandler().ProcessEvent(e) - - - def AutoToggleChild(self, item): - """Transverses the tree and toggles the items. Meaningful only for check items.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - child, cookie = self.GetFirstChild(item) - - torefresh = False - if item.IsExpanded(): - torefresh = True - - # Recurse on tree - while child: - if child.GetType() == 1 and child.IsEnabled(): - self.CheckItem2(child, not child.IsChecked(), torefresh=torefresh) - self.AutoToggleChild(child) - (child, cookie) = self.GetNextChild(item, cookie) - - - def AutoCheckChild(self, item, checked): - """Transverses the tree and checks/unchecks the items. Meaningful only for check items.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - (child, cookie) = self.GetFirstChild(item) - - torefresh = False - if item.IsExpanded(): - torefresh = True - - while child: - if child.GetType() == 1 and child.IsEnabled(): - self.CheckItem2(child, checked, torefresh=torefresh) - self.AutoCheckChild(child, checked) - (child, cookie) = self.GetNextChild(item, cookie) - - - def AutoCheckParent(self, item, checked): - """Traverses up the tree and checks/unchecks parent items. - Meaningful only for check items.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - parent = item.GetParent() - if not parent or parent.GetType() != 1: - return - - (child, cookie) = self.GetFirstChild(parent) - while child: - if child.GetType() == 1 and child.IsEnabled(): - if checked != child.IsChecked(): - return - (child, cookie) = self.GetNextChild(parent, cookie) - - self.CheckItem2(parent, checked, torefresh=True) - self.AutoCheckParent(parent, checked) - - - def CheckChilds(self, item, checked=True): - """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if checked == None: - self.AutoToggleChild(item) - else: - self.AutoCheckChild(item, checked) - - - def CheckSameLevel(self, item, checked=False): - """ - Uncheck radio items which are on the same level of the checked one. - Used internally. - """ - - parent = item.GetParent() - - if not parent: - return - - torefresh = False - if parent.IsExpanded(): - torefresh = True - - (child, cookie) = self.GetFirstChild(parent) - while child: - if child.GetType() == 2 and child != item: - self.CheckItem2(child, checked, torefresh=torefresh) - if child.GetType != 2 or (child.GetType() == 2 and child.IsChecked()): - self.EnableChildren(child, checked) - (child, cookie) = self.GetNextChild(parent, cookie) - - - def EditLabel(self, item): - """Starts editing an item label.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - self.Edit(item) - - - def ShouldInheritColours(self): - """We don't inherit colours from anyone.""" - - return False - - - def SetIndent(self, indent): - """Sets item indentation.""" - - self._indent = indent - self._dirty = True - - - def SetSpacing(self, spacing): - """Sets item spacing.""" - - self._spacing = spacing - self._dirty = True - - - def HasChildren(self, item): - """Returns whether an item has children or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return len(item.GetChildren()) > 0 - - - def GetChildrenCount(self, item, recursively=True): - """Gets the item children count.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetChildrenCount(recursively) - - - def SetTreeStyle(self, styles): - """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation.""" - - # Do not try to expand the root node if it hasn't been created yet - if self._anchor and not self.HasFlag(TR_HIDE_ROOT) and styles & TR_HIDE_ROOT: - - # if we will hide the root, make sure children are visible - self._anchor.SetHasPlus() - self._anchor.Expand() - self.CalculatePositions() - - # right now, just sets the styles. Eventually, we may - # want to update the inherited styles, but right now - # none of the parents has updatable styles - - if self.HasFlag(TR_MULTIPLE) and not (styles & TR_MULTIPLE): - selections = self.GetSelections() - for select in selections[0:-1]: - self.SelectItem(select, False) - - self.SetWindowStyle(styles) - self._dirty = True - - - def GetTreeStyle(self): - """Returns the CustomTreeCtrl style.""" - - return self.GetWindowStyle() - - - def HasButtons(self): - """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag.""" - - return self.HasFlag(TR_HAS_BUTTONS) - - -# ----------------------------------------------------------------------------- -# functions to work with tree items -# ----------------------------------------------------------------------------- - - def GetItemText(self, item): - """Returns the item text.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetText() - - - def GetItemImage(self, item, which=TreeItemIcon_Normal): - """Returns the item image.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetImage(which) - - - def GetPyData(self, item): - """Returns the data associated to an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetData() - - GetItemPyData = GetPyData - - - def GetItemTextColour(self, item): - """Returns the item text colour.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.Attr().GetTextColour() - - - def GetItemBackgroundColour(self, item): - """Returns the item background colour.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.Attr().GetBackgroundColour() - - - def GetItemFont(self, item): - """Returns the item font.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.Attr().GetFont() - - - def IsItemHyperText(self, item): - """Returns whether an item is hypertext or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsHyperText() - - - def SetItemText(self, item, text): - """Sets the item text.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - dc = wx.ClientDC(self) - item.SetText(text) - self.CalculateSize(item, dc) - self.RefreshLine(item) - - - def SetItemImage(self, item, image, which=TreeItemIcon_Normal): - """Sets the item image, depending on the item state.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.SetImage(image, which) - - dc = wx.ClientDC(self) - self.CalculateSize(item, dc) - self.RefreshLine(item) - - - def SetPyData(self, item, data): - """Sets the data associated to an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.SetData(data) - - SetItemPyData = SetPyData - - - def SetItemHasChildren(self, item, has=True): - """Forces the appearance of the button next to the item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.SetHasPlus(has) - self.RefreshLine(item) - - - def SetItemBold(self, item, bold=True): - """Sets the item font bold/unbold.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - # avoid redrawing the tree if no real change - if item.IsBold() != bold: - item.SetBold(bold) - self._dirty = True - - - def SetItemItalic(self, item, italic=True): - """Sets the item font italic/non-italic.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if item.IsItalic() != italic: - itemFont = self.GetItemFont(item) - if itemFont != wx.NullFont: - style = wx.ITALIC - if not italic: - style = ~style - - item.SetItalic(italic) - itemFont.SetStyle(style) - self.SetItemFont(item, itemFont) - self._dirty = True - - - def SetItemDropHighlight(self, item, highlight=True): - """ - Gives the item the visual feedback for drag and drop operations. - This is useful when something is dragged from outside the CustomTreeCtrl. - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if highlight: - bg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) - fg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) - - item.Attr().SetTextColour(fg) - item.Attr.SetBackgroundColour(bg) - self.RefreshLine(item) - - - def SetItemTextColour(self, item, col): - """Sets the item text colour.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if self.GetItemTextColour(item) == col: - return - - item.Attr().SetTextColour(col) - self.RefreshLine(item) - - - def SetItemBackgroundColour(self, item, col): - """Sets the item background colour.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.Attr().SetBackgroundColour(col) - self.RefreshLine(item) - - - def SetItemHyperText(self, item, hyper=True): - """Sets whether the item is hypertext or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.SetHyperText(hyper) - self.RefreshLine(item) - - - def SetItemFont(self, item, font): - """Sets the item font.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if self.GetItemFont(item) == font: - return - - item.Attr().SetFont(font) - self._dirty = True - - - def SetFont(self, font): - """Sets the CustomTreeCtrl font.""" - - wx.ScrolledWindow.SetFont(self, font) - - self._normalFont = font - self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), - self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(), - self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) - - 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 an hypertext item.""" - - self._hypertextfont = font - self._dirty = True - - - def SetHyperTextNewColour(self, colour): - """Sets the colour used to render a non-visited hypertext item.""" - - 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.""" - - 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.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - item.SetVisited(visited) - self.RefreshLine(item) - - - def GetItemVisited(self, item): - """Returns whether an hypertext item was visited.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetVisited() - - - def SetHilightFocusColour(self, colour): - """ - Sets the colour used to highlight focused selected items. - This is applied only if gradient and Windows Vista styles are disabled. - """ - - self._hilightBrush = wx.Brush(colour) - self.RefreshSelected() - - - def SetHilightNonFocusColour(self, colour): - """ - Sets the colour used to highlight unfocused selected items. - This is applied only if gradient and Windows Vista styles are disabled. - """ - - self._hilightUnfocusedBrush = wx.Brush(colour) - self.RefreshSelected() - - - def GetHilightFocusColour(self): - """ - Returns the colour used to highlight focused selected items. - This is applied only if gradient and Windows Vista styles are disabled. - """ - - return self._hilightBrush.GetColour() - - - def GetHilightNonFocusColour(self): - """ - Returns the colour used to highlight unfocused selected items. - This is applied only if gradient and Windows Vista styles are disabled. - """ - - return self._hilightUnfocusedBrush.GetColour() - - - def SetFirstGradientColour(self, colour=None): - """Sets the first gradient colour.""" - - 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.""" - - if colour is None: - # No colour given, generate a slightly darker from the - # CustomTreeCtrl background colour - color = self.GetBackgroundColour() - r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) - color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) - colour = wx.Colour(color[0], color[1], color[2]) - - self._secondcolour = colour - - if self._usegradients: - self.RefreshSelected() - - - def GetFirstGradientColour(self): - """Returns the first gradient colour.""" - - return self._firstcolour - - - def GetSecondGradientColour(self): - """Returns the second gradient colour.""" - - return self._secondcolour - - - def EnableSelectionGradient(self, enable=True): - """Globally enables/disables drawing of gradient selection.""" - - self._usegradients = enable - self._vistaselection = False - self.RefreshSelected() - - - def SetGradientStyle(self, vertical=0): - """ - Sets the gradient style: - 0: horizontal gradient - 1: vertical gradient - """ - - # 0 = Horizontal, 1 = Vertical - self._gradientstyle = vertical - - if self._usegradients: - self.RefreshSelected() - - - def GetGradientStyle(self): - """ - Returns the gradient style: - 0: horizontal gradient - 1: vertical gradient - """ - - return self._gradientstyle - - - def EnableSelectionVista(self, enable=True): - """Globally enables/disables drawing of Windows Vista selection.""" - - self._usegradients = False - self._vistaselection = enable - self.RefreshSelected() - - - def SetBorderPen(self, pen): - """ - Sets the pen used to draw the selected item border. - The border pen is not used if the Windows Vista style is applied. - """ - - self._borderPen = pen - self.RefreshSelected() - - - def GetBorderPen(self): - """ - Returns the pen used to draw the selected item border. - The border pen is not used if the Windows Vista style is applied. - """ - - return self._borderPen - - - def SetConnectionPen(self, pen): - """Sets the pen used to draw the connecting lines between items.""" - - self._dottedPen = pen - self._dirty = True - - - def GetConnectionPen(self): - """Returns the pen used to draw the connecting lines between items.""" - - return self._dottedPen - - - def SetBackgroundImage(self, image): - """Sets the CustomTreeCtrl background image (can be none).""" - - self._backgroundImage = image - self.Refresh() - - - def GetBackgroundImage(self): - """Returns the CustomTreeCtrl background image (can be none).""" - - return self._backgroundImage - - - def GetItemWindow(self, item): - """Returns the window associated to the item (if any).""" - - if not item: - raise Exception("\nERROR: Invalid Item") - - return item.GetWindow() - - - def GetItemWindowEnabled(self, item): - """Returns whether the window associated to the item is enabled.""" - - if not item: - raise Exception("\nERROR: Invalid Item") - - return item.GetWindowEnabled() - - - def SetItemWindowEnabled(self, item, enable=True): - """Enables/disables the window associated to the item.""" - - if not item: - raise Exception("\nERROR: Invalid Item") - - item.SetWindowEnabled(enable) - - - def GetItemType(self, item): - """ - Returns the item type: - 0: normal - 1: checkbox item - 2: radiobutton item - """ - - if not item: - raise Exception("\nERROR: Invalid Item") - - return item.GetType() - -# ----------------------------------------------------------------------------- -# item status inquiries -# ----------------------------------------------------------------------------- - - def IsVisible(self, item): - """Returns whether the item is visible or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - # An item is only visible if it's not a descendant of a collapsed item - parent = item.GetParent() - - while parent: - - if not parent.IsExpanded(): - return False - - parent = parent.GetParent() - - startX, startY = self.GetViewStart() - clientSize = self.GetClientSize() - - rect = self.GetBoundingRect(item) - - if not rect: - return False - if rect.GetWidth() == 0 or rect.GetHeight() == 0: - return False - if rect.GetBottom() < 0 or rect.GetTop() > clientSize.y: - return False - if rect.GetRight() < 0 or rect.GetLeft() > clientSize.x: - return False - - return True - - - def ItemHasChildren(self, item): - """Returns whether the item has children or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - # consider that the item does have children if it has the "+" button: it - # might not have them (if it had never been expanded yet) but then it - # could have them as well and it's better to err on this side rather than - # disabling some operations which are restricted to the items with - # children for an item which does have them - return item.HasPlus() - - - def IsExpanded(self, item): - """Returns whether the item is expanded or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsExpanded() - - - def IsSelected(self, item): - """Returns whether the item is selected or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsSelected() - - - def IsBold(self, item): - """Returns whether the item font is bold or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsBold() - - - def IsItalic(self, item): - """Returns whether the item font is italic or not.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.IsItalic() - - -# ----------------------------------------------------------------------------- -# navigation -# ----------------------------------------------------------------------------- - - def GetItemParent(self, item): - """Gets the item parent.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - return item.GetParent() - - - def GetFirstChild(self, item): - """Gets the item first child.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - cookie = 0 - return self.GetNextChild(item, cookie) - - - def GetNextChild(self, item, cookie): - """ - Gets the item next child based on the 'cookie' parameter. - This method has no sense if you do not call GetFirstChild() before. - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - children = item.GetChildren() - - # it's ok to cast cookie to size_t, we never have indices big enough to - # overflow "void *" - - if cookie < len(children): - - return children[cookie], cookie+1 - - else: - - # there are no more of them - return None, cookie - - - def GetLastChild(self, item): - """Gets the item last child.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - children = item.GetChildren() - return (len(children) == 0 and [None] or [children[-1]])[0] - - - def GetNextSibling(self, item): - """Gets the next sibling of an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - i = item - parent = i.GetParent() - - if parent == None: - - # root item doesn't have any siblings - return None - - siblings = parent.GetChildren() - index = siblings.index(i) - - n = index + 1 - return (n == len(siblings) and [None] or [siblings[n]])[0] - - - def GetPrevSibling(self, item): - """Gets the previous sibling of an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - i = item - parent = i.GetParent() - - if parent == None: - - # root item doesn't have any siblings - return None - - siblings = parent.GetChildren() - index = siblings.index(i) - - return (index == 0 and [None] or [siblings[index-1]])[0] - - - def GetNext(self, item): - """Gets the next item. Only for internal use right now.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - i = item - - # First see if there are any children. - children = i.GetChildren() - if len(children) > 0: - return children[0] - else: - # Try a sibling of this or ancestor instead - p = item - toFind = None - while p and not toFind: - toFind = self.GetNextSibling(p) - p = self.GetItemParent(p) - - return toFind - - - def GetFirstVisibleItem(self): - """Returns the first visible item.""" - - id = self.GetRootItem() - if not id: - return id - - while id: - if self.IsVisible(id): - return id - id = self.GetNext(id) - - return None - - - def GetNextVisible(self, item): - """Returns the next visible item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - id = item - - while id: - id = self.GetNext(id) - if id and self.IsVisible(id): - return id - - return None - - - def GetPrevVisible(self, item): - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - raise Exception("\nERROR: Not Implemented") - - return None - - - def ResetTextControl(self): - """Called by TreeTextCtrl when it marks itself for deletion.""" - - self._textCtrl.Destroy() - self._textCtrl = None - - - def FindItem(self, idParent, prefixOrig): - """Finds the first item starting with the given prefix after the given item.""" - - # match is case insensitive as this is more convenient to the user: having - # to press Shift-letter to go to the item starting with a capital letter - # would be too bothersome - prefix = prefixOrig.lower() - - # determine the starting point: we shouldn't take the current item (this - # allows to switch between two items starting with the same letter just by - # pressing it) but we shouldn't jump to the next one if the user is - # continuing to type as otherwise he might easily skip the item he wanted - id = idParent - - if len(prefix) == 1: - id = self.GetNext(id) - - # look for the item starting with the given prefix after it - while id and not self.GetItemText(id).lower().startswith(prefix): - - id = self.GetNext(id) - - # if we haven't found anything... - if not id: - - # ... wrap to the beginning - id = self.GetRootItem() - if self.HasFlag(TR_HIDE_ROOT): - # can't select virtual root - id = self.GetNext(id) - - # and try all the items (stop when we get to the one we started from) - while id != idParent and not self.GetItemText(id).lower().startswith(prefix): - id = self.GetNext(id) - - return id - - -# ----------------------------------------------------------------------------- -# operations -# ----------------------------------------------------------------------------- - - def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Actually inserts an item in the tree.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if ct_type < 0 or ct_type > 2: - raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ") - - parent = parentId - - if not parent: - - # should we give a warning here? - return self.AddRoot(text, ct_type, wnd, image, selImage, data) - - self._dirty = True # do this first so stuff below doesn't cause flicker - - item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data) - - if wnd is not None: - self._hasWindows = True - self._itemWithWindow.append(item) - - parent.Insert(item, previous) - - return item - - - def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Adds a root to the CustomTreeCtrl. Only one root must exist.""" - - if self._anchor: - raise Exception("\nERROR: Tree Can Have Only One Root") - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if ct_type < 0 or ct_type > 2: - raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ") - - self._dirty = True # do this first so stuff below doesn't cause flicker - - self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data) - - if wnd is not None: - self._hasWindows = True - self._itemWithWindow.append(self._anchor) - - if self.HasFlag(TR_HIDE_ROOT): - - # if root is hidden, make sure we can navigate - # into children - self._anchor.SetHasPlus() - self._anchor.Expand() - self.CalculatePositions() - - if not self.HasFlag(TR_MULTIPLE): - - self._current = self._key_current = self._anchor - self._current.SetHilight(True) - - return self._anchor - - - def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Appends an item as a first child of parent.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data) - - - def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Auxiliary function to cope with the C++ hideous multifunction.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - parent = parentId - - if not parent: - # should we give a warning here? - return self.AddRoot(text, ct_type, wnd, image, selImage, data) - - index = -1 - if idPrevious: - - try: - index = parent.GetChildren().index(idPrevious) - except: - raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling") - - return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data) - - - def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Auxiliary function to cope with the C++ hideous multifunction.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - parent = parentId - - if not parent: - # should we give a warning here? - return self.AddRoot(text, ct_type, wnd, image, selImage, data) - - return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data) - - - def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Inserts an item after the given previous.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if type(input) == type(1): - return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data) - else: - return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data) - - - def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): - """Appends an item as a last child of its parent.""" - - if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT): - raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT") - - parent = parentId - - if not parent: - # should we give a warning here? - return self.AddRoot(text, ct_type, wnd, image, selImage, data) - - return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data) - - - def SendDeleteEvent(self, item): - """Actully sends the EVT_TREE_DELETE_ITEM event.""" - - event = TreeEvent(wxEVT_TREE_DELETE_ITEM, self.GetId()) - event._item = item - event.SetEventObject(self) - self.GetEventHandler().ProcessEvent(event) - - - def IsDescendantOf(self, parent, item): - """Checks if the given item is under another one.""" - - while item: - - if item == parent: - - # item is a descendant of parent - return True - - item = item.GetParent() - - return False - - - # Don't leave edit or selection on a child which is about to disappear - def ChildrenClosing(self, item): - """We are about to destroy the item children.""" - - if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()): - self._textCtrl.StopEditing() - - if item != self._key_current and self.IsDescendantOf(item, self._key_current): - self._key_current = None - - if self.IsDescendantOf(item, self._select_me): - self._select_me = item - - if item != self._current and self.IsDescendantOf(item, self._current): - self._current.SetHilight(False) - self._current = None - self._select_me = item - - - def DeleteChildren(self, item): - """Delete item children.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - self._dirty = True # do this first so stuff below doesn't cause flicker - - self.ChildrenClosing(item) - item.DeleteChildren(self) - - - def Delete(self, item): - """Delete an item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - self._dirty = True # do this first so stuff below doesn't cause flicker - - if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()): - # can't delete the item being edited, cancel editing it first - self._textCtrl.StopEditing() - - parent = item.GetParent() - - # don't keep stale pointers around! - if self.IsDescendantOf(item, self._key_current): - - # Don't silently change the selection: - # do it properly in idle time, so event - # handlers get called. - - # self._key_current = parent - self._key_current = None - - # self._select_me records whether we need to select - # a different item, in idle time. - if self._select_me and self.IsDescendantOf(item, self._select_me): - self._select_me = parent - - if self.IsDescendantOf(item, self._current): - - # Don't silently change the selection: - # do it properly in idle time, so event - # handlers get called. - - # self._current = parent - self._current = None - self._select_me = parent - - # remove the item from the tree - if parent: - - parent.GetChildren().remove(item) # remove by value - - else: # deleting the root - - # nothing will be left in the tree - self._anchor = None - - # and delete all of its children and the item itself now - item.DeleteChildren(self) - self.SendDeleteEvent(item) - - if item == self._select_me: - self._select_me = None - - # Remove the item with window - if item in self._itemWithWindow: - wnd = item.GetWindow() - wnd.Hide() - wnd.Destroy() - item._wnd = None - self._itemWithWindow.remove(item) - - del item - - - def DeleteAllItems(self): - """Delete all items in the CustomTreeCtrl.""" - - if self._anchor: - self.Delete(self._anchor) - - - def Expand(self, item): - """ - Expands an item, sending a EVT_TREE_ITEM_EXPANDING and - EVT_TREE_ITEM_EXPANDED events. - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem(): - raise Exception("\nERROR: Can't Expand An Hidden Root. ") - - if not item.HasPlus(): - return - - if item.IsExpanded(): - return - - event = TreeEvent(wxEVT_TREE_ITEM_EXPANDING, self.GetId()) - event._item = item - event.SetEventObject(self) - - if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed(): - # cancelled by program - return - - item.Expand() - self.CalculatePositions() - - self.RefreshSubtree(item) - - if self._hasWindows: - # We hide the associated window here, we may show it after - self.HideWindows() - - event.SetEventType(wxEVT_TREE_ITEM_EXPANDED) - self.GetEventHandler().ProcessEvent(event) - - - def ExpandAllChildren(self, item): - """Expands all the items children of the input item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if not self.HasFlag(TR_HIDE_ROOT) or item != self.GetRootItem(): - self.Expand(item) - if not self.IsExpanded(item): - return - - child, cookie = self.GetFirstChild(item) - - while child: - self.ExpandAllChildren(child) - child, cookie = self.GetNextChild(item, cookie) - - - def ExpandAll(self): - """Expands all CustomTreeCtrl items.""" - - if self._anchor: - self.ExpandAllChildren(self._anchor) - - - def Collapse(self, item): - """ - Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and - EVT_TREE_ITEM_COLLAPSED events. - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem(): - raise Exception("\nERROR: Can't Collapse An Hidden Root. ") - - if not item.IsExpanded(): - return - - event = TreeEvent(wxEVT_TREE_ITEM_COLLAPSING, self.GetId()) - event._item = item - event.SetEventObject(self) - if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed(): - # cancelled by program - return - - self.ChildrenClosing(item) - item.Collapse() - - self.CalculatePositions() - self.RefreshSubtree(item) - - if self._hasWindows: - self.HideWindows() - - event.SetEventType(wxEVT_TREE_ITEM_COLLAPSED) - self.GetEventHandler().ProcessEvent(event) - - - def CollapseAndReset(self, item): - """Collapse the given item and deletes its children.""" - - self.Collapse(item) - self.DeleteChildren(item) - - - def Toggle(self, item): - """Toggles the item state (collapsed/expanded).""" - - if item.IsExpanded(): - self.Collapse(item) - else: - self.Expand(item) - - - def HideWindows(self): - """Hides the windows associated to the items. Used internally.""" - - for child in self._itemWithWindow: - if not self.IsVisible(child): - wnd = child.GetWindow() - if wnd: - wnd.Hide() - - - def Unselect(self): - """Unselects the current selection.""" - - if self._current: - - self._current.SetHilight(False) - self.RefreshLine(self._current) - - self._current = None - self._select_me = None - - - def UnselectAllChildren(self, item): - """Unselects all the children of the given item.""" - - if item.IsSelected(): - - item.SetHilight(False) - self.RefreshLine(item) - - if item.HasChildren(): - for child in item.GetChildren(): - self.UnselectAllChildren(child) - - - def UnselectAll(self): - """Unselect all the items.""" - - rootItem = self.GetRootItem() - - # the tree might not have the root item at all - if rootItem: - self.UnselectAllChildren(rootItem) - - self.Unselect() - - # Recursive function ! - # To stop we must have crt_item start_y+client_h: - - # going up - x, y = self._anchor.GetSize(x, y, self) - y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels - x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels - item_y += _PIXELS_PER_UNIT+2 - x_pos = self.GetScrollPos(wx.HORIZONTAL) - # Item should appear at bottom - self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, (item_y+self.GetLineHeight(item)-client_h)/_PIXELS_PER_UNIT ) - - - def OnCompareItems(self, item1, item2): - """ - Returns whether 2 items have the same text. - Override this function in the derived class to change the sort order of the items - in the CustomTreeCtrl. The function should return a negative, zero or positive - value if the first item is less than, equal to or greater than the second one. - - The base class version compares items alphabetically. - """ - - return self.GetItemText(item1) == self.GetItemText(item2) - - - def SortChildren(self, item): - """ - Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl. - You should override that method to change the sort order (the default is ascending - case-sensitive alphabetical order). - """ - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - children = item.GetChildren() - - if len(children) > 1: - self._dirty = True - children.sort(self.OnCompareItems) - - - def GetImageList(self): - """Returns the normal image list.""" - - return self._imageListNormal - - - def GetButtonsImageList(self): - """Returns the buttons image list (from which application-defined button images are taken).""" - - return self._imageListButtons - - - def GetStateImageList(self): - """Returns the state image list (from which application-defined state images are taken).""" - - return self._imageListState - - - def GetImageListCheck(self): - """Returns the image list used to build the check/radio buttons.""" - - return self._imageListCheck - - - def CalculateLineHeight(self): - """Calculates the height of a line.""" - - dc = wx.ClientDC(self) - self._lineHeight = dc.GetCharHeight() - - if self._imageListNormal: - - # Calculate a self._lineHeight value from the normal Image sizes. - # May be toggle off. Then CustomTreeCtrl will spread when - # necessary (which might look ugly). - n = self._imageListNormal.GetImageCount() - - for i in xrange(n): - - width, height = self._imageListNormal.GetSize(i) - - if height > self._lineHeight: - self._lineHeight = height - - if self._imageListButtons: - - # Calculate a self._lineHeight value from the Button image sizes. - # May be toggle off. Then CustomTreeCtrl will spread when - # necessary (which might look ugly). - n = self._imageListButtons.GetImageCount() - - for i in xrange(n): - - width, height = self._imageListButtons.GetSize(i) - - if height > self._lineHeight: - self._lineHeight = height - - if self._imageListCheck: - - # Calculate a self._lineHeight value from the check/radio image sizes. - # May be toggle off. Then CustomTreeCtrl will spread when - # necessary (which might look ugly). - n = self._imageListCheck.GetImageCount() - - for i in xrange(n): - - width, height = self._imageListCheck.GetSize(i) - - if height > self._lineHeight: - self._lineHeight = height - - if self._lineHeight < 30: - self._lineHeight += 2 # at least 2 pixels - else: - self._lineHeight += self._lineHeight/10 # otherwise 10% extra spacing - - - def SetImageList(self, imageList): - """Sets the normal image list.""" - - if self._ownsImageListNormal: - del self._imageListNormal - - self._imageListNormal = imageList - self._ownsImageListNormal = False - self._dirty = True - - # Don't do any drawing if we're setting the list to NULL, - # since we may be in the process of deleting the tree control. - if imageList: - self.CalculateLineHeight() - - # We gray out the image list to use the grayed icons with disabled items - sz = imageList.GetSize(0) - self._grayedImageList = wx.ImageList(sz[0], sz[1], True, 0) - - for ii in xrange(imageList.GetImageCount()): - bmp = imageList.GetBitmap(ii) - image = wx.ImageFromBitmap(bmp) - image = GrayOut(image) - newbmp = wx.BitmapFromImage(image) - self._grayedImageList.Add(newbmp) - - - def SetStateImageList(self, imageList): - """Sets the state image list (from which application-defined state images are taken).""" - - if self._ownsImageListState: - del self._imageListState - - self._imageListState = imageList - self._ownsImageListState = False - - - def SetButtonsImageList(self, imageList): - """Sets the buttons image list (from which application-defined button images are taken).""" - - if self._ownsImageListButtons: - del self._imageListButtons - - self._imageListButtons = imageList - self._ownsImageListButtons = False - self._dirty = True - self.CalculateLineHeight() - - - def SetImageListCheck(self, sizex, sizey, imglist=None): - """Sets the check image list.""" - - if imglist is None: - - self._imageListCheck = wx.ImageList(sizex, sizey) - self._imageListCheck.Add(GetCheckedBitmap()) - self._imageListCheck.Add(GetNotCheckedBitmap()) - self._imageListCheck.Add(GetFlaggedBitmap()) - self._imageListCheck.Add(GetNotFlaggedBitmap()) - - else: - - sizex, sizey = imglist.GetSize(0) - self._imageListCheck = imglist - - # We gray out the image list to use the grayed icons with disabled items - self._grayedCheckList = wx.ImageList(sizex, sizey, True, 0) - - for ii in xrange(self._imageListCheck.GetImageCount()): - - bmp = self._imageListCheck.GetBitmap(ii) - image = wx.ImageFromBitmap(bmp) - image = GrayOut(image) - newbmp = wx.BitmapFromImage(image) - self._grayedCheckList.Add(newbmp) - - self._dirty = True - - if imglist: - self.CalculateLineHeight() - - - def AssignImageList(self, imageList): - """Assigns the normal image list.""" - - self.SetImageList(imageList) - self._ownsImageListNormal = True - - - def AssignStateImageList(self, imageList): - """Assigns the state image list.""" - - self.SetStateImageList(imageList) - self._ownsImageListState = True - - - def AssignButtonsImageList(self, imageList): - """Assigns the button image list.""" - - self.SetButtonsImageList(imageList) - self._ownsImageListButtons = True - - -# ----------------------------------------------------------------------------- -# helpers -# ----------------------------------------------------------------------------- - - def AdjustMyScrollbars(self): - """Adjust the wx.ScrolledWindow scrollbars.""" - - if self._anchor: - - x, y = self._anchor.GetSize(0, 0, self) - y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels - x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels - x_pos = self.GetScrollPos(wx.HORIZONTAL) - y_pos = self.GetScrollPos(wx.VERTICAL) - self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, y_pos) - - else: - - self.SetScrollbars(0, 0, 0, 0) - - - def GetLineHeight(self, item): - """Returns the line height for the given item.""" - - if self.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT: - return item.GetHeight() - else: - return self._lineHeight - - - def DrawVerticalGradient(self, dc, rect, hasfocus): - """Gradient fill from colour 1 to colour 2 from top to bottom.""" - - oldpen = dc.GetPen() - oldbrush = dc.GetBrush() - dc.SetPen(wx.TRANSPARENT_PEN) - - # calculate gradient coefficients - if hasfocus: - col2 = self._secondcolour - col1 = self._firstcolour - else: - col2 = self._hilightUnfocusedBrush.GetColour() - col1 = self._hilightUnfocusedBrush2.GetColour() - - r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) - r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) - - flrect = float(rect.height) - - rstep = float((r2 - r1)) / flrect - gstep = float((g2 - g1)) / flrect - bstep = float((b2 - b1)) / flrect - - rf, gf, bf = 0, 0, 0 - - for y in xrange(rect.y, rect.y + rect.height): - currCol = (r1 + rf, g1 + gf, b1 + bf) - dc.SetBrush(wx.Brush(currCol, wx.SOLID)) - dc.DrawRectangle(rect.x, y, rect.width, 1) - rf = rf + rstep - gf = gf + gstep - bf = bf + bstep - - dc.SetPen(oldpen) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.DrawRectangleRect(rect) - dc.SetBrush(oldbrush) - - - def DrawHorizontalGradient(self, dc, rect, hasfocus): - """Gradient fill from colour 1 to colour 2 from left to right.""" - - oldpen = dc.GetPen() - oldbrush = dc.GetBrush() - dc.SetPen(wx.TRANSPARENT_PEN) - - # calculate gradient coefficients - - if hasfocus: - col2 = self._secondcolour - col1 = self._firstcolour - else: - col2 = self._hilightUnfocusedBrush.GetColour() - col1 = self._hilightUnfocusedBrush2.GetColour() - - r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) - r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) - - flrect = float(rect.width) - - rstep = float((r2 - r1)) / flrect - gstep = float((g2 - g1)) / flrect - bstep = float((b2 - b1)) / flrect - - rf, gf, bf = 0, 0, 0 - - for x in xrange(rect.x, rect.x + rect.width): - currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf)) - dc.SetBrush(wx.Brush(currCol, wx.SOLID)) - dc.DrawRectangle(x, rect.y, 1, rect.height) - rf = rf + rstep - gf = gf + gstep - bf = bf + bstep - - dc.SetPen(oldpen) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.DrawRectangleRect(rect) - dc.SetBrush(oldbrush) - - - def DrawVistaRectangle(self, dc, rect, hasfocus): - """Draw the selected item(s) with the Windows Vista style.""" - - 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._lineHeight - - rstep = float((r2 - r1)) / flrect - gstep = float((g2 - g1)) / flrect - bstep = float((b2 - b1)) / flrect - - rf, gf, bf = 0, 0, 0 - dc.SetPen(wx.TRANSPARENT_PEN) - - for y in xrange(filRect.y, filRect.y + filRect.height): - currCol = (r1 + rf, g1 + gf, b1 + bf) - dc.SetBrush(wx.Brush(currCol, wx.SOLID)) - dc.DrawRectangle(filRect.x, y, filRect.width, 1) - rf = rf + rstep - gf = gf + gstep - bf = bf + bstep - - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.SetPen(wx.Pen(outer)) - dc.DrawRoundedRectangleRect(bdrRect, 3) - bdrRect.Deflate(1, 1) - dc.SetPen(wx.Pen(inner)) - dc.DrawRoundedRectangleRect(bdrRect, 2) - - dc.SetPen(oldpen) - dc.SetBrush(oldbrush) - - - def PaintItem(self, item, dc): - """Actually paint an item.""" - - attr = item.GetAttributes() - - if attr and attr.HasFont(): - dc.SetFont(attr.GetFont()) - elif item.IsBold(): - dc.SetFont(self._boldFont) - if item.IsHyperText(): - dc.SetFont(self.GetHyperTextFont()) - if item.GetVisited(): - dc.SetTextForeground(self.GetHyperTextVisitedColour()) - else: - dc.SetTextForeground(self.GetHyperTextNewColour()) - - text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText()) - - image = item.GetCurrentImage() - checkimage = item.GetCurrentCheckedImage() - image_w, image_h = 0, 0 - - if image != _NO_IMAGE: - - if self._imageListNormal: - - image_w, image_h = self._imageListNormal.GetSize(image) - image_w += 4 - - else: - - image = _NO_IMAGE - - if item.GetType() != 0: - wcheck, hcheck = self._imageListCheck.GetSize(item.GetType()) - wcheck += 4 - else: - wcheck, hcheck = 0, 0 - - total_h = self.GetLineHeight(item) - drawItemBackground = False - - if item.IsSelected(): - - # under mac selections are only a rectangle in case they don't have the focus - if wx.Platform == "__WXMAC__": - if not self._hasFocus: - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), 1, wx.SOLID)) - else: - dc.SetBrush(self._hilightBrush) - else: - dc.SetBrush((self._hasFocus and [self._hilightBrush] or [self._hilightUnfocusedBrush])[0]) - drawItemBackground = True - else: - if attr and attr.HasBackgroundColour(): - drawItemBackground = True - colBg = attr.GetBackgroundColour() - else: - colBg = self._backgroundColour - - dc.SetBrush(wx.Brush(colBg, wx.SOLID)) - dc.SetPen(wx.TRANSPARENT_PEN) - - offset = (self.HasFlag(TR_ROW_LINES) and [1] or [0])[0] - - if self.HasFlag(TR_FULL_ROW_HIGHLIGHT): - x = 0 - w, h = self.GetClientSize() - - itemrect = wx.Rect(x, item.GetY()+offset, w, total_h-offset) - - if item.IsSelected(): - if self._usegradients: - if self._gradientstyle == 0: # Horizontal - self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) - else: # Vertical - self.DrawVerticalGradient(dc, itemrect, self._hasFocus) - elif self._vistaselection: - self.DrawVistaRectangle(dc, itemrect, self._hasFocus) - else: - if wx.Platform in ["__WXGTK2__", "__WXMAC__"]: - flags = wx.CONTROL_SELECTED - if self._hasFocus: flags = flags | wx.CONTROL_FOCUSED - wx.RendererNative.Get().DrawItemSelectionRect(self, dc, itemrect, flags) - else: - dc.DrawRectangleRect(itemrect) - - else: - - if item.IsSelected(): - - # If it's selected, and there's an image, then we should - # take care to leave the area under the image painted in the - # background colour. - - wnd = item.GetWindow() - wndx = 0 - if wnd: - wndx, wndy = item.GetWindowSize() - - itemrect = wx.Rect(item.GetX() + wcheck + image_w - 2, - item.GetY()+offset, - item.GetWidth() - image_w - wcheck + 2 - wndx, - total_h-offset) - - if self._usegradients: - if self._gradientstyle == 0: # Horizontal - self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) - else: # Vertical - self.DrawVerticalGradient(dc, itemrect, self._hasFocus) - elif self._vistaselection: - self.DrawVistaRectangle(dc, itemrect, self._hasFocus) - else: - if wx.Platform in ["__WXGTK2__", "__WXMAC__"]: - flags = wx.CONTROL_SELECTED - if self._hasFocus: flags = flags | wx.CONTROL_FOCUSED - wx.RendererNative.Get().DrawItemSelectionRect(self, dc, itemrect, flags) - else: - dc.DrawRectangleRect(itemrect) - - # On GTK+ 2, drawing a 'normal' background is wrong for themes that - # don't allow backgrounds to be customized. Not drawing the background, - # except for custom item backgrounds, works for both kinds of theme. - elif drawItemBackground: - - minusicon = wcheck + image_w - 2 - itemrect = wx.Rect(item.GetX()+minusicon, - item.GetY()+offset, - item.GetWidth()-minusicon, - total_h-offset) - - if self._usegradients and self._hasFocus: - if self._gradientstyle == 0: # Horizontal - self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) - else: # Vertical - self.DrawVerticalGradient(dc, itemrect, self._hasFocus) - else: - dc.DrawRectangleRect(itemrect) - - if image != _NO_IMAGE: - - dc.SetClippingRegion(item.GetX(), item.GetY(), wcheck+image_w-2, total_h) - if item.IsEnabled(): - imglist = self._imageListNormal - else: - imglist = self._grayedImageList - - imglist.Draw(image, dc, - item.GetX() + wcheck, - item.GetY() + ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0], - wx.IMAGELIST_DRAW_TRANSPARENT) - - dc.DestroyClippingRegion() - - if wcheck: - if item.IsEnabled(): - imglist = self._imageListCheck - else: - imglist = self._grayedCheckList - - imglist.Draw(checkimage, dc, - item.GetX(), - item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0], - wx.IMAGELIST_DRAW_TRANSPARENT) - - dc.SetBackgroundMode(wx.TRANSPARENT) - extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0] - - textrect = wx.Rect(wcheck + image_w + item.GetX(), item.GetY() + extraH, text_w, text_h) - - if not item.IsEnabled(): - foreground = dc.GetTextForeground() - dc.SetTextForeground(self._disabledColour) - dc.DrawLabel(item.GetText(), textrect) - dc.SetTextForeground(foreground) - else: - if wx.Platform == "__WXMAC__" and item.IsSelected() and self._hasFocus: - dc.SetTextForeground(wx.WHITE) - dc.DrawLabel(item.GetText(), textrect) - - wnd = item.GetWindow() - if wnd: - wndx = wcheck + image_w + item.GetX() + text_w + 4 - xa, ya = self.CalcScrolledPosition((0, item.GetY())) - wndx += xa - if item.GetHeight() > item.GetWindowSize()[1]: - ya += (item.GetHeight() - item.GetWindowSize()[1])/2 - - if not wnd.IsShown(): - wnd.Show() - if wnd.GetPosition() != (wndx, ya): - wnd.SetPosition((wndx, ya)) - - # restore normal font - dc.SetFont(self._normalFont) - - - # Now y stands for the top of the item, whereas it used to stand for middle ! - def PaintLevel(self, item, dc, level, y): - """Paint a level of CustomTreeCtrl.""" - - x = level*self._indent - - if not self.HasFlag(TR_HIDE_ROOT): - - x += self._indent - - elif level == 0: - - # always expand hidden root - origY = y - children = item.GetChildren() - count = len(children) - - if count > 0: - n = 0 - while n < count: - oldY = y - y = self.PaintLevel(children[n], dc, 1, y) - n = n + 1 - - if not self.HasFlag(TR_NO_LINES) and self.HasFlag(TR_LINES_AT_ROOT) and count > 0: - - # draw line down to last child - origY += self.GetLineHeight(children[0])>>1 - oldY += self.GetLineHeight(children[n-1])>>1 - oldPen = dc.GetPen() - dc.SetPen(self._dottedPen) - dc.DrawLine(3, origY, 3, oldY) - dc.SetPen(oldPen) - - return y - - item.SetX(x+self._spacing) - item.SetY(y) - - h = self.GetLineHeight(item) - y_top = y - y_mid = y_top + (h>>1) - y += h - - exposed_x = dc.LogicalToDeviceX(0) - exposed_y = dc.LogicalToDeviceY(y_top) - - if self.IsExposed(exposed_x, exposed_y, 10000, h): # 10000 = very much - if wx.Platform == "__WXMAC__": - # don't draw rect outline if we already have the - # background color under Mac - pen = ((item.IsSelected() and self._hasFocus) and [self._borderPen] or [wx.TRANSPARENT_PEN])[0] - else: - pen = self._borderPen - - if item.IsSelected(): - if (wx.Platform == "__WXMAC__" and self._hasFocus): - colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) - else: - colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) - else: - attr = item.GetAttributes() - if attr and attr.HasTextColour(): - colText = attr.GetTextColour() - else: - colText = self.GetForegroundColour() - - if self._vistaselection: - colText = wx.BLACK - - # prepare to draw - dc.SetTextForeground(colText) - dc.SetPen(pen) - oldpen = pen - - # draw - self.PaintItem(item, dc) - - if self.HasFlag(TR_ROW_LINES): - - # if the background colour is white, choose a - # contrasting color for the lines - medium_grey = wx.Pen(wx.Colour(200, 200, 200)) - dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0]) - dc.DrawLine(0, y_top, 10000, y_top) - dc.DrawLine(0, y, 10000, y) - - # restore DC objects - dc.SetBrush(wx.WHITE_BRUSH) - dc.SetTextForeground(wx.BLACK) - - if not self.HasFlag(TR_NO_LINES): - - # draw the horizontal line here - dc.SetPen(self._dottedPen) - x_start = x - if x > self._indent: - x_start -= self._indent - elif self.HasFlag(TR_LINES_AT_ROOT): - x_start = 3 - dc.DrawLine(x_start, y_mid, x + self._spacing, y_mid) - dc.SetPen(oldpen) - - # should the item show a button? - if item.HasPlus() and self.HasButtons(): - - if self._imageListButtons: - - # draw the image button here - image_h = 0 - image_w = 0 - image = (item.IsExpanded() and [TreeItemIcon_Expanded] or [TreeItemIcon_Normal])[0] - if item.IsSelected(): - image += TreeItemIcon_Selected - TreeItemIcon_Normal - - image_w, image_h = self._imageListButtons.GetSize(image) - xx = x - image_w/2 - yy = y_mid - image_h/2 - - dc.SetClippingRegion(xx, yy, image_w, image_h) - self._imageListButtons.Draw(image, dc, xx, yy, - wx.IMAGELIST_DRAW_TRANSPARENT) - dc.DestroyClippingRegion() - - else: # no custom buttons - - if self.HasFlag(TR_TWIST_BUTTONS): - # We draw something like the Mac twist buttons - - dc.SetPen(wx.BLACK_PEN) - dc.SetBrush(self._hilightBrush) - button = [wx.Point(), wx.Point(), wx.Point()] - - if item.IsExpanded(): - button[0].x = x - 5 - button[0].y = y_mid - 3 - button[1].x = x + 5 - button[1].y = button[0].y - button[2].x = x - button[2].y = button[0].y + 6 - else: - button[0].x = x - 3 - button[0].y = y_mid - 5 - button[1].x = button[0].x - button[1].y = y_mid + 5 - button[2].x = button[0].x + 5 - button[2].y = y_mid - - dc.DrawPolygon(button) - - else: - # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows - - wImage = 9 - hImage = 9 - - flag = 0 - - if item.IsExpanded(): - flag |= _CONTROL_EXPANDED - if item == self._underMouse: - flag |= _CONTROL_CURRENT - - self._drawingfunction(self, dc, wx.Rect(x - wImage/2, y_mid - hImage/2,wImage, hImage), flag) - - if item.IsExpanded(): - - children = item.GetChildren() - count = len(children) - - if count > 0: - - n = 0 - level = level + 1 - - while n < count: - oldY = y - y = self.PaintLevel(children[n], dc, level, y) - n = n + 1 - - if not self.HasFlag(TR_NO_LINES) and count > 0: - - # draw line down to last child - oldY += self.GetLineHeight(children[n-1])>>1 - if self.HasButtons(): - y_mid += 5 - - # Only draw the portion of the line that is visible, in case it is huge - xOrigin, yOrigin = dc.GetDeviceOrigin() - yOrigin = abs(yOrigin) - width, height = self.GetClientSize() - - # Move end points to the begining/end of the view? - if y_mid < yOrigin: - y_mid = yOrigin - if oldY > yOrigin + height: - oldY = yOrigin + height - - # after the adjustments if y_mid is larger than oldY then the line - # isn't visible at all so don't draw anything - if y_mid < oldY: - dc.SetPen(self._dottedPen) - dc.DrawLine(x, y_mid, x, oldY) - - return y - - -# ----------------------------------------------------------------------------- -# wxWidgets callbacks -# ----------------------------------------------------------------------------- - - def OnPaint(self, event): - """Handles the wx.EVT_PAINT event.""" - - dc = wx.PaintDC(self) - self.PrepareDC(dc) - - if not self._anchor: - return - - dc.SetFont(self._normalFont) - dc.SetPen(self._dottedPen) - - y = 2 - self.PaintLevel(self._anchor, dc, 0, y) - - - def OnEraseBackground(self, event): - """Handles the wx.EVT_ERASE_BACKGROUND event.""" - - # 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... - - if not self._backgroundImage: - event.Skip() - return - - if self._imageStretchStyle == _StyleTile: - dc = event.GetDC() - - if not dc: - dc = wx.ClientDC(self) - rect = self.GetUpdateRegion().GetBox() - dc.SetClippingRect(rect) - - self.TileBackground(dc) - - - def TileBackground(self, dc): - """Tiles the background image to fill all the available area.""" - - 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 OnSetFocus(self, event): - """Handles the wx.EVT_SET_FOCUS event.""" - - self._hasFocus = True - self.RefreshSelected() - event.Skip() - - - def OnKillFocus(self, event): - """Handles the wx.EVT_KILL_FOCUS event.""" - - self._hasFocus = False - self.RefreshSelected() - event.Skip() - - - def OnKeyDown(self, event): - """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event.""" - - te = TreeEvent(wxEVT_TREE_KEY_DOWN, self.GetId()) - te._evtKey = event - te.SetEventObject(self) - - if self.GetEventHandler().ProcessEvent(te): - # intercepted by the user code - return - - if self._current is None or self._key_current is None: - - event.Skip() - return - - # how should the selection work for this event? - is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), event.ShiftDown(), event.CmdDown()) - - # + : Expand - # - : Collaspe - # * : Expand all/Collapse all - # ' ' | return : activate - # up : go up (not last children!) - # down : go down - # left : go to parent - # right : open if parent and go next - # home : go to root - # end : go to last item without opening parents - # alnum : start or continue searching for the item with this prefix - - keyCode = event.GetKeyCode() - - if keyCode in [ord("+"), wx.WXK_ADD]: # "+" - if self._current.HasPlus() and not self.IsExpanded(self._current) and self.IsItemEnabled(self._current): - self.Expand(self._current) - - elif keyCode in [ord("*"), wx.WXK_MULTIPLY]: # "*" - if not self.IsExpanded(self._current) and self.IsItemEnabled(self._current): - # expand all - self.ExpandAll(self._current) - - elif keyCode in [ord("-"), wx.WXK_SUBTRACT]: # "-" - if self.IsExpanded(self._current): - self.Collapse(self._current) - - elif keyCode == wx.WXK_MENU: - # Use the item's bounding rectangle to determine position for the event - itemRect = self.GetBoundingRect(self._current, True) - event = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId()) - event._item = self._current - # Use the left edge, vertical middle - event._pointDrag = wx.Point(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight()/2) - event.SetEventObject(self) - self.GetEventHandler().ProcessEvent(event) - - elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]: - - if not self.IsItemEnabled(self._current): - event.Skip() - return - - if not event.HasModifiers(): - event = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId()) - event._item = self._current - event.SetEventObject(self) - self.GetEventHandler().ProcessEvent(event) - - if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0: - checked = not self.IsItemChecked(self._current) - self.CheckItem(self._current, checked) - - # in any case, also generate the normal key event for this key, - # even if we generated the ACTIVATED event above: this is what - # wxMSW does and it makes sense because you might not want to - # process ACTIVATED event at all and handle Space and Return - # directly (and differently) which would be impossible otherwise - event.Skip() - - # up goes to the previous sibling or to the last - # of its children if it's expanded - elif keyCode == wx.WXK_UP: - prev = self.GetPrevSibling(self._key_current) - if not prev: - prev = self.GetItemParent(self._key_current) - if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT): - return - - if prev: - current = self._key_current - # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be? - if current == self.GetFirstChild(prev)[0] and self.IsItemEnabled(prev): - # otherwise we return to where we came from - self.DoSelectItem(prev, unselect_others, extended_select) - self._key_current = prev - - else: - current = self._key_current - - # We are going to another parent node - while self.IsExpanded(prev) and self.HasChildren(prev): - child = self.GetLastChild(prev) - if child: - prev = child - current = prev - - # Try to get the previous siblings and see if they are active - while prev and not self.IsItemEnabled(prev): - prev = self.GetPrevSibling(prev) - - if not prev: - # No previous siblings active: go to the parent and up - prev = self.GetItemParent(current) - while prev and not self.IsItemEnabled(prev): - prev = self.GetItemParent(prev) - - if prev: - self.DoSelectItem(prev, unselect_others, extended_select) - self._key_current = prev - - # left arrow goes to the parent - elif keyCode == wx.WXK_LEFT: - - prev = self.GetItemParent(self._current) - if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT): - # don't go to root if it is hidden - prev = self.GetPrevSibling(self._current) - - if self.IsExpanded(self._current): - self.Collapse(self._current) - else: - if prev and self.IsItemEnabled(prev): - self.DoSelectItem(prev, unselect_others, extended_select) - - elif keyCode == wx.WXK_RIGHT: - # this works the same as the down arrow except that we - # also expand the item if it wasn't expanded yet - if self.IsExpanded(self._current) and self.HasChildren(self._current): - child, cookie = self.GetFirstChild(self._key_current) - if self.IsItemEnabled(child): - self.DoSelectItem(child, unselect_others, extended_select) - self._key_current = child - else: - self.Expand(self._current) - # fall through - - elif keyCode == wx.WXK_DOWN: - if self.IsExpanded(self._key_current) and self.HasChildren(self._key_current): - - child = self.GetNextActiveItem(self._key_current) - - if child: - self.DoSelectItem(child, unselect_others, extended_select) - self._key_current = child - - else: - - next = self.GetNextSibling(self._key_current) - - if not next: - current = self._key_current - while current and not next: - current = self.GetItemParent(current) - if current: - next = self.GetNextSibling(current) - if not next or not self.IsItemEnabled(next): - next = None - - else: - while next and not self.IsItemEnabled(next): - next = self.GetNext(next) - - if next: - self.DoSelectItem(next, unselect_others, extended_select) - self._key_current = next - - - # selects the last visible tree item - elif keyCode == wx.WXK_END: - - last = self.GetRootItem() - - while last and self.IsExpanded(last): - - lastChild = self.GetLastChild(last) - - # it may happen if the item was expanded but then all of - # its children have been deleted - so IsExpanded() returned - # true, but GetLastChild() returned invalid item - if not lastChild: - break - - last = lastChild - - if last and self.IsItemEnabled(last): - - self.DoSelectItem(last, unselect_others, extended_select) - - # selects the root item - elif keyCode == wx.WXK_HOME: - - prev = self.GetRootItem() - - if not prev: - return - - if self.HasFlag(TR_HIDE_ROOT): - prev, cookie = self.GetFirstChild(prev) - if not prev: - return - - if self.IsItemEnabled(prev): - self.DoSelectItem(prev, unselect_others, extended_select) - - else: - - if not event.HasModifiers() and ((keyCode >= ord('0') and keyCode <= ord('9')) or \ - (keyCode >= ord('a') and keyCode <= ord('z')) or \ - (keyCode >= ord('A') and keyCode <= ord('Z'))): - - # find the next item starting with the given prefix - ch = chr(keyCode) - id = self.FindItem(self._current, self._findPrefix + ch) - - if not id: - # no such item - return - - if self.IsItemEnabled(id): - self.SelectItem(id) - self._findPrefix += ch - - # also start the timer to reset the current prefix if the user - # doesn't press any more alnum keys soon -- we wouldn't want - # to use this prefix for a new item search - if not self._findTimer: - self._findTimer = TreeFindTimer(self) - - self._findTimer.Start(_DELAY, wx.TIMER_ONE_SHOT) - - else: - - event.Skip() - - - def GetNextActiveItem(self, item, down=True): - """Returns the next active item. Used Internally at present. """ - - if down: - sibling = self.GetNextSibling - else: - sibling = self.GetPrevSibling - - if self.GetItemType(item) == 2 and not self.IsItemChecked(item): - # Is an unchecked radiobutton... all its children are inactive - # try to get the next/previous sibling - found = 0 - - while 1: - child = sibling(item) - if (child and self.IsItemEnabled(child)) or not child: - break - item = child - - else: - # Tha's not a radiobutton... but some of its children can be - # inactive - child, cookie = self.GetFirstChild(item) - while child and not self.IsItemEnabled(child): - child, cookie = self.GetNextChild(item, cookie) - - if child and self.IsItemEnabled(child): - return child - - return None - - - def HitTest(self, point, flags=0): - """ - Calculates which (if any) item is under the given point, returning the tree item - at this point plus extra information flags. Flags is a bitlist of the following: - - TREE_HITTEST_ABOVE above the client area - TREE_HITTEST_BELOW below the client area - TREE_HITTEST_NOWHERE no item has been hit - TREE_HITTEST_ONITEMBUTTON on the button associated to an item - TREE_HITTEST_ONITEMICON on the icon associated to an item - TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present - TREE_HITTEST_ONITEMINDENT on the indent associated to an item - TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item - TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item - TREE_HITTEST_TOLEFT on the left of the client area - TREE_HITTEST_TORIGHT on the right of the client area - TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item - TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item - TREE_HITTEST_ONITEM anywhere on the item - - Note: both the item (if any, None otherwise) and the flag are always returned as a tuple. - """ - - w, h = self.GetSize() - flags = 0 - - if point.x < 0: - flags |= TREE_HITTEST_TOLEFT - if point.x > w: - flags |= TREE_HITTEST_TORIGHT - if point.y < 0: - flags |= TREE_HITTEST_ABOVE - if point.y > h: - flags |= TREE_HITTEST_BELOW - - if flags: - return None, flags - - if self._anchor == None: - flags = TREE_HITTEST_NOWHERE - return None, flags - - hit, flags = self._anchor.HitTest(self.CalcUnscrolledPosition(point), self, flags, 0) - - if hit == None: - flags = TREE_HITTEST_NOWHERE - return None, flags - - if not self.IsItemEnabled(hit): - return None, flags - - return hit, flags - - - def GetBoundingRect(self, item, textOnly=False): - """Gets the bounding rectangle of the item.""" - - if not item: - raise Exception("\nERROR: Invalid Tree Item. ") - - i = item - - startX, startY = self.GetViewStart() - rect = wx.Rect() - - rect.x = i.GetX() - startX*_PIXELS_PER_UNIT - rect.y = i.GetY() - startY*_PIXELS_PER_UNIT - rect.width = i.GetWidth() - rect.height = self.GetLineHeight(i) - - return rect - - - def Edit(self, item): - """ - Internal function. Starts the editing of an item label, sending a - EVT_TREE_BEGIN_LABEL_EDIT event. - """ - - te = TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT, self.GetId()) - te._item = item - te.SetEventObject(self) - if self.GetEventHandler().ProcessEvent(te) and not te.IsAllowed(): - # vetoed by user - 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: - if wx.Platform in ["__WXMSW__", "__WXMAC__"]: - self.Update() - else: - wx.YieldIfNeeded() - - if self._textCtrl != None and item != self._textCtrl.item(): - self._textCtrl.StopEditing() - - self._textCtrl = TreeTextCtrl(self, item=item) - self._textCtrl.SetFocus() - - - def GetEditControl(self): - """ - Returns a pointer to the edit TextCtrl if the item is being edited or - None otherwise (it is assumed that no more than one item may be edited - simultaneously). - """ - - return self._textCtrl - - - def OnRenameAccept(self, item, value): - """ - Called by TreeTextCtrl, to accept the changes and to send the - EVT_TREE_END_LABEL_EDIT event. - """ - - le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId()) - le._item = item - le.SetEventObject(self) - le._label = value - le._editCancelled = False - - return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed() - - - def OnRenameCancelled(self, item): - """ - Called by TreeTextCtrl, to cancel the changes and to send the - EVT_TREE_END_LABEL_EDIT event. - """ - - # let owner know that the edit was cancelled - le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId()) - le._item = item - le.SetEventObject(self) - le._label = "" - le._editCancelled = True - - self.GetEventHandler().ProcessEvent(le) - - - def OnRenameTimer(self): - """The timer for renaming has expired. Start editing.""" - - self.Edit(self._current) - - - def OnMouse(self, event): - """Handles a bunch of wx.EVT_MOUSE_EVENTS events.""" - - if not self._anchor: - return - - pt = self.CalcUnscrolledPosition(event.GetPosition()) - - # Is the mouse over a tree item button? - flags = 0 - thisItem, flags = self._anchor.HitTest(pt, self, flags, 0) - underMouse = thisItem - underMouseChanged = underMouse != self._underMouse - - if underMouse and (flags & TREE_HITTEST_ONITEM) and not event.LeftIsDown() and \ - not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()): - underMouse = underMouse - else: - underMouse = None - - if underMouse != self._underMouse: - if self._underMouse: - # unhighlight old item - self._underMouse = None - - self._underMouse = underMouse - - # Determines what item we are hovering over and need a tooltip for - hoverItem = thisItem - - # We do not want a tooltip if we are dragging, or if the rename timer is running - if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()): - - if hoverItem is not None: - # Ask the tree control what tooltip (if any) should be shown - hevent = TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP, self.GetId()) - hevent._item = hoverItem - hevent.SetEventObject(self) - - if self.GetEventHandler().ProcessEvent(hevent) and hevent.IsAllowed(): - self.SetToolTip(hevent._label) - - if hoverItem.IsHyperText() and (flags & TREE_HITTEST_ONITEMLABEL) and hoverItem.IsEnabled(): - self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - self._isonhyperlink = True - else: - if self._isonhyperlink: - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - self._isonhyperlink = False - - # we process left mouse up event (enables in-place edit), right down - # (pass to the user code), left dbl click (activate item) and - # dragging/moving events for items drag-and-drop - - if not (event.LeftDown() or event.LeftUp() or event.RightDown() or event.LeftDClick() or \ - event.Dragging() or ((event.Moving() or event.RightUp()) and self._isDragging)): - - event.Skip() - return - - flags = 0 - item, flags = self._anchor.HitTest(pt, self, flags, 0) - - if event.Dragging() and not self._isDragging and ((flags & TREE_HITTEST_ONITEMICON) or (flags & TREE_HITTEST_ONITEMLABEL)): - - if self._dragCount == 0: - self._dragStart = pt - - self._countDrag = 0 - self._dragCount = self._dragCount + 1 - - if self._dragCount != 3: - # wait until user drags a bit further... - return - - command = (event.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG] or [wxEVT_TREE_BEGIN_DRAG])[0] - - nevent = TreeEvent(command, self.GetId()) - nevent._item = self._current - nevent.SetEventObject(self) - newpt = self.CalcScrolledPosition(pt) - nevent.SetPoint(newpt) - - # by default the dragging is not supported, the user code must - # explicitly allow the event for it to take place - nevent.Veto() - - if self.GetEventHandler().ProcessEvent(nevent) and nevent.IsAllowed(): - - # we're going to drag this item - self._isDragging = True - - # remember the old cursor because we will change it while - # dragging - self._oldCursor = self._cursor - - # in a single selection control, hide the selection temporarily - if not (self.GetTreeStyle() & TR_MULTIPLE): - self._oldSelection = self.GetSelection() - - if self._oldSelection: - - self._oldSelection.SetHilight(False) - self.RefreshLine(self._oldSelection) - else: - selections = self.GetSelections() - if len(selections) == 1: - self._oldSelection = selections[0] - self._oldSelection.SetHilight(False) - self.RefreshLine(self._oldSelection) - - if self._dragImage: - del self._dragImage - - # Create the custom draw image from the icons and the text of the item - self._dragImage = DragImage(self, self._current) - self._dragImage.BeginDrag(wx.Point(0,0), self) - self._dragImage.Show() - self._dragImage.Move(self.CalcScrolledPosition(pt)) - - elif event.Dragging() and self._isDragging: - - self._dragImage.Move(self.CalcScrolledPosition(pt)) - - if self._countDrag == 0 and item: - self._oldItem = item - - if item != self._dropTarget: - - # unhighlight the previous drop target - if self._dropTarget: - self._dropTarget.SetHilight(False) - self.RefreshLine(self._dropTarget) - if item: - item.SetHilight(True) - self.RefreshLine(item) - self._countDrag = self._countDrag + 1 - self._dropTarget = item - - self.Update() - - if self._countDrag >= 3: - # Here I am trying to avoid ugly repainting problems... hope it works - self.RefreshLine(self._oldItem) - self._countDrag = 0 - - elif (event.LeftUp() or event.RightUp()) and self._isDragging: - - if self._dragImage: - self._dragImage.EndDrag() - - if self._dropTarget: - self._dropTarget.SetHilight(False) - - if self._oldSelection: - - self._oldSelection.SetHilight(True) - self.RefreshLine(self._oldSelection) - self._oldSelection = None - - # generate the drag end event - event = TreeEvent(wxEVT_TREE_END_DRAG, self.GetId()) - event._item = item - event._pointDrag = self.CalcScrolledPosition(pt) - event.SetEventObject(self) - - self.GetEventHandler().ProcessEvent(event) - - self._isDragging = False - self._dropTarget = None - - self.SetCursor(self._oldCursor) - - if wx.Platform in ["__WXMSW__", "__WXMAC__"]: - self.Refresh() - else: - # Probably this is not enough on GTK. Try a Refresh() if it does not work. - wx.YieldIfNeeded() - - else: - - # If we got to this point, we are not dragging or moving the mouse. - # Because the code in carbon/toplevel.cpp will only set focus to the tree - # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work. - # We skip even if we didn't hit an item because we still should - # restore focus to the tree control even if we didn't exactly hit an item. - if event.LeftDown(): - self._hasFocus = True - self.SetFocusIgnoringChildren() - event.Skip() - - # here we process only the messages which happen on tree items - - self._dragCount = 0 - - if item == None: - if self._textCtrl != None and item != self._textCtrl.item(): - self._textCtrl.StopEditing() - return # we hit the blank area - - if event.RightDown(): - - if self._textCtrl != None and item != self._textCtrl.item(): - self._textCtrl.StopEditing() - - self._hasFocus = True - self.SetFocusIgnoringChildren() - - # 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.IsSelected(item): - - self.DoSelectItem(item, True, False) - - nevent = TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, self.GetId()) - nevent._item = item - nevent._pointDrag = self.CalcScrolledPosition(pt) - nevent.SetEventObject(self) - event.Skip(not self.GetEventHandler().ProcessEvent(nevent)) - - # Consistent with MSW (for now), send the ITEM_MENU *after* - # the RIGHT_CLICK event. TODO: This behaviour may change. - nevent2 = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId()) - nevent2._item = item - nevent2._pointDrag = self.CalcScrolledPosition(pt) - nevent2.SetEventObject(self) - self.GetEventHandler().ProcessEvent(nevent2) - - elif event.LeftUp(): - - # this facilitates multiple-item drag-and-drop - - if self.HasFlag(TR_MULTIPLE): - - selections = self.GetSelections() - - if len(selections) > 1 and not event.CmdDown() and not event.ShiftDown(): - - self.DoSelectItem(item, True, False) - - if self._lastOnSame: - - if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasFlag(TR_EDIT_LABELS): - - if self._renameTimer: - - if self._renameTimer.IsRunning(): - - self._renameTimer.Stop() - - else: - - self._renameTimer = TreeRenameTimer(self) - - self._renameTimer.Start(_DELAY, True) - - self._lastOnSame = False - - - else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick() - - if not item or not item.IsEnabled(): - if self._textCtrl != None and item != self._textCtrl.item(): - self._textCtrl.StopEditing() - return - - if self._textCtrl != None and item != self._textCtrl.item(): - self._textCtrl.StopEditing() - - self._hasFocus = True - self.SetFocusIgnoringChildren() - - if event.LeftDown(): - - self._lastOnSame = item == self._current - - if flags & TREE_HITTEST_ONITEMBUTTON: - - # only toggle the item for a single click, double click on - # the button doesn't do anything (it toggles the item twice) - if event.LeftDown(): - - self.Toggle(item) - - # don't select the item if the button was clicked - return - - if item.GetType() > 0 and (flags & TREE_HITTEST_ONITEMCHECKICON): - - if event.LeftDown(): - - self.CheckItem(item, not self.IsItemChecked(item)) - - return - - # clear the previously selected items, if the - # user clicked outside of the present selection. - # otherwise, perform the deselection on mouse-up. - # this allows multiple drag and drop to work. - # but if Cmd is down, toggle selection of the clicked item - if not self.IsSelected(item) or event.CmdDown(): - - if flags & TREE_HITTEST_ONITEM: - # how should the selection work for this event? - if item.IsHyperText(): - self.SetItemVisited(item, True) - - is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), - event.ShiftDown(), - event.CmdDown()) - - self.DoSelectItem(item, unselect_others, extended_select) - - # For some reason, Windows isn't recognizing a left double-click, - # so we need to simulate it here. Allow 200 milliseconds for now. - if event.LeftDClick(): - - # double clicking should not start editing the item label - if self._renameTimer: - self._renameTimer.Stop() - - self._lastOnSame = False - - # send activate event first - nevent = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId()) - nevent._item = item - nevent._pointDrag = self.CalcScrolledPosition(pt) - nevent.SetEventObject(self) - if not self.GetEventHandler().ProcessEvent(nevent): - - # if the user code didn't process the activate event, - # handle it ourselves by toggling the item when it is - # double clicked -## if item.HasPlus(): - self.Toggle(item) - - - def OnInternalIdle(self): - """Performs operations in idle time (essentially drawing).""" - - # Check if we need to select the root item - # because nothing else has been selected. - # Delaying it means that we can invoke event handlers - # as required, when a first item is selected. - if not self.HasFlag(TR_MULTIPLE) and not self.GetSelection(): - - if self._select_me: - self.SelectItem(self._select_me) - elif self.GetRootItem(): - self.SelectItem(self.GetRootItem()) - - # after all changes have been done to the tree control, - # we actually redraw the tree when everything is over - - if not self._dirty: - return - if self._freezeCount: - return - - self._dirty = False - - self.CalculatePositions() - self.Refresh() - self.AdjustMyScrollbars() - -# event.Skip() - - - def CalculateSize(self, item, dc): - """Calculates overall position and size of an item.""" - - attr = item.GetAttributes() - - if attr and attr.HasFont(): - dc.SetFont(attr.GetFont()) - elif item.IsBold(): - dc.SetFont(self._boldFont) - else: - dc.SetFont(self._normalFont) - - text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText()) - text_h+=2 - - # restore normal font - dc.SetFont(self._normalFont) - - image_w, image_h = 0, 0 - image = item.GetCurrentImage() - - if image != _NO_IMAGE: - - if self._imageListNormal: - - image_w, image_h = self._imageListNormal.GetSize(image) - image_w += 4 - - total_h = ((image_h > text_h) and [image_h] or [text_h])[0] - - checkimage = item.GetCurrentCheckedImage() - if checkimage is not None: - wcheck, hcheck = self._imageListCheck.GetSize(checkimage) - wcheck += 4 - else: - wcheck = 0 - - if total_h < 30: - total_h += 2 # at least 2 pixels - else: - total_h += total_h/10 # otherwise 10% extra spacing - - if total_h > self._lineHeight: - self._lineHeight = total_h - - if not item.GetWindow(): - item.SetWidth(image_w+text_w+wcheck+2) - item.SetHeight(total_h) - else: - item.SetWidth(item.GetWindowSize()[0]+image_w+text_w+wcheck+2) - item.SetHeight(max(total_h, item.GetWindowSize()[1])) - - - def CalculateLevel(self, item, dc, level, y): - """Calculates the level of an item.""" - - x = level*self._indent - - if not self.HasFlag(TR_HIDE_ROOT): - - x += self._indent - - elif level == 0: - - # a hidden root is not evaluated, but its - # children are always calculated - children = item.GetChildren() - count = len(children) - level = level + 1 - for n in xrange(count): - y = self.CalculateLevel(children[n], dc, level, y) # recurse - - return y - - self.CalculateSize(item, dc) - - # set its position - item.SetX(x+self._spacing) - item.SetY(y) - y += self.GetLineHeight(item) - - if not item.IsExpanded(): - # we don't need to calculate collapsed branches - return y - - children = item.GetChildren() - count = len(children) - level = level + 1 - for n in xrange(count): - y = self.CalculateLevel(children[n], dc, level, y) # recurse - - return y - - - def CalculatePositions(self): - """Calculates all the positions of the visible items.""" - - if not self._anchor: - return - - dc = wx.ClientDC(self) - self.PrepareDC(dc) - - dc.SetFont(self._normalFont) - dc.SetPen(self._dottedPen) - y = 2 - y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion - - - def RefreshSubtree(self, item): - """Refreshes a damaged subtree of an item.""" - - if self._dirty: - return - if self._freezeCount: - return - - client = self.GetClientSize() - - rect = wx.Rect() - x, rect.y = self.CalcScrolledPosition(0, item.GetY()) - rect.width = client.x - rect.height = client.y - - self.Refresh(True, rect) - self.AdjustMyScrollbars() - - - def RefreshLine(self, item): - """Refreshes a damaged item line.""" - - if self._dirty: - return - if self._freezeCount: - return - - rect = wx.Rect() - x, rect.y = self.CalcScrolledPosition(0, item.GetY()) - rect.width = self.GetClientSize().x - rect.height = self.GetLineHeight(item) - - self.Refresh(True, rect) - - - def RefreshSelected(self): - """Refreshes a damaged selected item line.""" - - if self._freezeCount: - return - - # TODO: this is awfully inefficient, we should keep the list of all - # selected items internally, should be much faster - if self._anchor: - self.RefreshSelectedUnder(self._anchor) - - - def RefreshSelectedUnder(self, item): - """Refreshes the selected items under the given item.""" - - if self._freezeCount: - return - - if item.IsSelected(): - self.RefreshLine(item) - - children = item.GetChildren() - for child in children: - self.RefreshSelectedUnder(child) - - - def Freeze(self): - """Freeze CustomTreeCtrl.""" - - self._freezeCount = self._freezeCount + 1 - - - def Thaw(self): - """Thaw CustomTreeCtrl.""" - - if self._freezeCount == 0: - raise Exception("\nERROR: Thawing Unfrozen Tree Control?") - - self._freezeCount = self._freezeCount - 1 - - if not self._freezeCount: - self.Refresh() - - - # ---------------------------------------------------------------------------- - # changing colours: we need to refresh the tree control - # ---------------------------------------------------------------------------- - - def SetBackgroundColour(self, colour): - """Changes the background colour of CustomTreeCtrl.""" - - if not wx.Window.SetBackgroundColour(self, colour): - return False - - if self._freezeCount: - return True - - self.Refresh() - - return True - - - def SetForegroundColour(self, colour): - """Changes the foreground colour of CustomTreeCtrl.""" - - if not wx.Window.SetForegroundColour(self, colour): - return False - - if self._freezeCount: - return True - - self.Refresh() - - return True - - - def OnGetToolTip(self, event): - """ - Process the tooltip event, to speed up event processing. Does not actually - get a tooltip. - """ - - event.Veto() - - - def DoGetBestSize(self): - """Something is better than nothing...""" - - # something is better than nothing... - # 100x80 is what the MSW version will get from the default - # wxControl::DoGetBestSize - - return wx.Size(100, 80) - - - def GetClassDefaultAttributes(self): - """Gets the class default attributes.""" - - attr = wx.VisualAttributes() - attr.colFg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) - attr.colBg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_LISTBOX) - attr.font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - return attr - - GetClassDefaultAttributes = classmethod(GetClassDefaultAttributes) - -