1 # --------------------------------------------------------------------------------- #
2 # CUSTOMTREECTRL wxPython IMPLEMENTATION
3 # Inspired By And Heavily Based On wxGenericTreeCtrl.
5 # Andrea Gavana, @ 17 May 2006
6 # Latest Revision: 26 May 2006, 22.30 CET
11 # Almost All The Features Of wx.TreeCtrl Are Available, And There Is Practically
12 # No Limit In What Could Be Added To This Class. The First Things That Comes
15 # 1. Implement The Style TR_EXTENDED (I Have Never Used It, But It May Be Useful).
17 # 2. Add Support For 3-State CheckBoxes (Is That Really Useful?).
19 # 3. Try To Implement A More Flicker-Free Background Image In Cases Like
20 # Centered Or Stretched Image (Now CustomTreeCtrl Supports Only Tiled
23 # 4. Try To Mimic Windows wx.TreeCtrl Expanding/Collapsing behaviour: CustomTreeCtrl
24 # Suddenly Expands/Collapses The Nodes On Mouse Click While The Native Control
25 # Has Some Kind Of "Smooth" Expanding/Collapsing, Like A Wave. I Don't Even
26 # Know Where To Start To Do That.
28 # 5. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite
29 # Fast, But We Should See On Slower Machines.
32 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
36 # andrea.gavana@gmail.com
38 # Or, Obviously, To The wxPython Mailing List!!!
42 # --------------------------------------------------------------------------------- #
49 CustomTreeCtrl is a class that mimics the behaviour of wx.TreeCtrl, with almost the
50 same base functionalities plus some more enhancements. This class does not rely on
51 the native control, as it is a full owner-drawn tree control.
52 Apart of the base functionalities of CustomTreeCtrl (described below), in addition
53 to the standard wx.TreeCtrl behaviour this class supports:
55 * CheckBox-type items: checkboxes are easy to handle, just selected or unselected
56 state with no particular issues in handling the item's children;
58 * RadioButton-type items: since I elected to put radiobuttons in CustomTreeCtrl, I
59 needed some way to handle them, that made sense. So, I used the following approach:
60 - All peer-nodes that are radiobuttons will be mutually exclusive. In other words,
61 only one of a set of radiobuttons that share a common parent can be checked at
62 once. If a radiobutton node becomes checked, then all of its peer radiobuttons
64 - If a radiobutton node becomes unchecked, then all of its child nodes will become
67 * Hyperlink-type items: they look like an hyperlink, with the proper mouse cursor on
70 * Multiline text items.
72 * Enabling/disabling items (together with their plain or grayed out icons).
74 * Whatever non-toplevel widget can be attached next to an item.
76 * Default selection style, gradient (horizontal/vertical) selection style and Windows
77 Vista selection style.
79 * Customized drag and drop images built on the fly.
81 * Setting the CustomTreeCtrl item buttons to a personalized imagelist.
83 * Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist.
85 * Changing the style of the lines that connect the items (in terms of wx.Pen styles).
87 * Using an image as a CustomTreeCtrl background (currently only in "tile" mode).
89 And a lot more. Check the demo for an almost complete review of the functionalities.
95 CustomTreeCtrl supports all the wx.TreeCtrl styles, except:
96 - TR_EXTENDED: supports for this style is on the todo list (Am I sure of this?).
98 Plus it has 2 more styles to handle checkbox-type items:
99 - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children;
100 - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children.
102 All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl.
108 All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with
111 - EVT_TREE_GET_INFO (don't know what this means);
112 - EVT_TREE_SET_INFO (don't know what this means);
113 - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add);
114 - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below.
116 Plus, CustomTreeCtrl supports the events related to the checkbutton-type items:
118 - EVT_TREE_ITEM_CHECKING: an item is being checked;
119 - EVT_TREE_ITEM_CHECKED: an item has been checked.
121 And to hyperlink-type items:
123 - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent
124 after the EVT_TREE_SEL_CHANGED event).
130 CustomTreeCtrl has been tested on the following platforms:
131 * Windows (Windows XP);
132 * GTK (Thanks to Michele Petrazzo);
133 * Mac OS (Thanks to John Jackson).
136 Latest Revision: Andrea Gavana @ 26 May 2006, 22.30 CET
146 # ----------------------------------------------------------------------------
148 # ----------------------------------------------------------------------------
151 _PIXELS_PER_UNIT
= 10
153 # Start editing the current item after half a second (if the mouse hasn't
154 # been clicked/moved)
157 # ----------------------------------------------------------------------------
159 # ----------------------------------------------------------------------------
161 # Enum for different images associated with a treectrl item
162 TreeItemIcon_Normal
= 0 # not selected, not expanded
163 TreeItemIcon_Selected
= 1 # selected, not expanded
164 TreeItemIcon_Expanded
= 2 # not selected, expanded
165 TreeItemIcon_SelectedExpanded
= 3 # selected, expanded
167 TreeItemIcon_Checked
= 0 # check button, checked
168 TreeItemIcon_NotChecked
= 1 # check button, not checked
169 TreeItemIcon_Flagged
= 2 # radio button, selected
170 TreeItemIcon_NotFlagged
= 3 # radio button, not selected
172 # ----------------------------------------------------------------------------
173 # CustomTreeCtrl flags
174 # ----------------------------------------------------------------------------
176 TR_NO_BUTTONS
= wx
.TR_NO_BUTTONS
# for convenience
177 TR_HAS_BUTTONS
= wx
.TR_HAS_BUTTONS
# draw collapsed/expanded btns
178 TR_NO_LINES
= wx
.TR_NO_LINES
# don't draw lines at all
179 TR_LINES_AT_ROOT
= wx
.TR_LINES_AT_ROOT
# connect top-level nodes
180 TR_TWIST_BUTTONS
= wx
.TR_TWIST_BUTTONS
# still used by wxTreeListCtrl
182 TR_SINGLE
= wx
.TR_SINGLE
# for convenience
183 TR_MULTIPLE
= wx
.TR_MULTIPLE
# can select multiple items
184 TR_EXTENDED
= wx
.TR_EXTENDED
# TODO: allow extended selection
185 TR_HAS_VARIABLE_ROW_HEIGHT
= wx
.TR_HAS_VARIABLE_ROW_HEIGHT
# what it says
187 TR_EDIT_LABELS
= wx
.TR_EDIT_LABELS
# can edit item labels
188 TR_ROW_LINES
= wx
.TR_ROW_LINES
# put border around items
189 TR_HIDE_ROOT
= wx
.TR_HIDE_ROOT
# don't display root node
191 TR_FULL_ROW_HIGHLIGHT
= wx
.TR_FULL_ROW_HIGHLIGHT
# highlight full horz space
193 TR_AUTO_CHECK_CHILD
= 0x4000 # only meaningful for checkboxes
194 TR_AUTO_TOGGLE_CHILD
= 0x8000 # only meaningful for checkboxes
196 TR_DEFAULT_STYLE
= wx
.TR_DEFAULT_STYLE
# default style for the tree control
198 # Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine
199 # where exactly the specified point is situated:
201 TREE_HITTEST_ABOVE
= wx
.TREE_HITTEST_ABOVE
202 TREE_HITTEST_BELOW
= wx
.TREE_HITTEST_BELOW
203 TREE_HITTEST_NOWHERE
= wx
.TREE_HITTEST_NOWHERE
204 # on the button associated with an item.
205 TREE_HITTEST_ONITEMBUTTON
= wx
.TREE_HITTEST_ONITEMBUTTON
206 # on the bitmap associated with an item.
207 TREE_HITTEST_ONITEMICON
= wx
.TREE_HITTEST_ONITEMICON
208 # on the indent associated with an item.
209 TREE_HITTEST_ONITEMINDENT
= wx
.TREE_HITTEST_ONITEMINDENT
210 # on the label (string) associated with an item.
211 TREE_HITTEST_ONITEMLABEL
= wx
.TREE_HITTEST_ONITEMLABEL
212 # on the right of the label associated with an item.
213 TREE_HITTEST_ONITEMRIGHT
= wx
.TREE_HITTEST_ONITEMRIGHT
214 # on the label (string) associated with an item.
215 TREE_HITTEST_ONITEMSTATEICON
= wx
.TREE_HITTEST_ONITEMSTATEICON
216 # on the left of the CustomTreeCtrl.
217 TREE_HITTEST_TOLEFT
= wx
.TREE_HITTEST_TOLEFT
218 # on the right of the CustomTreeCtrl.
219 TREE_HITTEST_TORIGHT
= wx
.TREE_HITTEST_TORIGHT
220 # on the upper part (first half) of the item.
221 TREE_HITTEST_ONITEMUPPERPART
= wx
.TREE_HITTEST_ONITEMUPPERPART
222 # on the lower part (second half) of the item.
223 TREE_HITTEST_ONITEMLOWERPART
= wx
.TREE_HITTEST_ONITEMLOWERPART
224 # on the check icon, if present
225 TREE_HITTEST_ONITEMCHECKICON
= 0x4000
226 # anywhere on the item
227 TREE_HITTEST_ONITEM
= TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON
230 # Background Image Style
234 # Windows Vista Colours
235 _rgbSelectOuter
= wx
.Colour(170, 200, 245)
236 _rgbSelectInner
= wx
.Colour(230, 250, 250)
237 _rgbSelectTop
= wx
.Colour(210, 240, 250)
238 _rgbSelectBottom
= wx
.Colour(185, 215, 250)
239 _rgbNoFocusTop
= wx
.Colour(250, 250, 250)
240 _rgbNoFocusBottom
= wx
.Colour(235, 235, 235)
241 _rgbNoFocusOuter
= wx
.Colour(220, 220, 220)
242 _rgbNoFocusInner
= wx
.Colour(245, 245, 245)
244 # Flags for wx.RendererNative
245 _CONTROL_EXPANDED
= 8
246 _CONTROL_CURRENT
= 16
252 # ----------------------------------------------------------------------------
253 # CustomTreeCtrl events and binding for handling them
254 # ----------------------------------------------------------------------------
256 wxEVT_TREE_BEGIN_DRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_DRAG
257 wxEVT_TREE_BEGIN_RDRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_RDRAG
258 wxEVT_TREE_BEGIN_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
259 wxEVT_TREE_END_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_END_LABEL_EDIT
260 wxEVT_TREE_DELETE_ITEM
= wx
.wxEVT_COMMAND_TREE_DELETE_ITEM
261 wxEVT_TREE_GET_INFO
= wx
.wxEVT_COMMAND_TREE_GET_INFO
262 wxEVT_TREE_SET_INFO
= wx
.wxEVT_COMMAND_TREE_SET_INFO
263 wxEVT_TREE_ITEM_EXPANDED
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDED
264 wxEVT_TREE_ITEM_EXPANDING
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDING
265 wxEVT_TREE_ITEM_COLLAPSED
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
266 wxEVT_TREE_ITEM_COLLAPSING
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
267 wxEVT_TREE_SEL_CHANGED
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGED
268 wxEVT_TREE_SEL_CHANGING
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGING
269 wxEVT_TREE_KEY_DOWN
= wx
.wxEVT_COMMAND_TREE_KEY_DOWN
270 wxEVT_TREE_ITEM_ACTIVATED
= wx
.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
271 wxEVT_TREE_ITEM_RIGHT_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
272 wxEVT_TREE_ITEM_MIDDLE_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
273 wxEVT_TREE_END_DRAG
= wx
.wxEVT_COMMAND_TREE_END_DRAG
274 wxEVT_TREE_STATE_IMAGE_CLICK
= wx
.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
275 wxEVT_TREE_ITEM_GETTOOLTIP
= wx
.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
276 wxEVT_TREE_ITEM_MENU
= wx
.wxEVT_COMMAND_TREE_ITEM_MENU
277 wxEVT_TREE_ITEM_CHECKING
= wx
.NewEventType()
278 wxEVT_TREE_ITEM_CHECKED
= wx
.NewEventType()
279 wxEVT_TREE_ITEM_HYPERLINK
= wx
.NewEventType()
281 EVT_TREE_BEGIN_DRAG
= wx
.EVT_TREE_BEGIN_DRAG
282 EVT_TREE_BEGIN_RDRAG
= wx
.EVT_TREE_BEGIN_RDRAG
283 EVT_TREE_BEGIN_LABEL_EDIT
= wx
.EVT_TREE_BEGIN_LABEL_EDIT
284 EVT_TREE_END_LABEL_EDIT
= wx
.EVT_TREE_END_LABEL_EDIT
285 EVT_TREE_DELETE_ITEM
= wx
.EVT_TREE_DELETE_ITEM
286 EVT_TREE_GET_INFO
= wx
.EVT_TREE_GET_INFO
287 EVT_TREE_SET_INFO
= wx
.EVT_TREE_SET_INFO
288 EVT_TREE_ITEM_EXPANDED
= wx
.EVT_TREE_ITEM_EXPANDED
289 EVT_TREE_ITEM_EXPANDING
= wx
.EVT_TREE_ITEM_EXPANDING
290 EVT_TREE_ITEM_COLLAPSED
= wx
.EVT_TREE_ITEM_COLLAPSED
291 EVT_TREE_ITEM_COLLAPSING
= wx
.EVT_TREE_ITEM_COLLAPSING
292 EVT_TREE_SEL_CHANGED
= wx
.EVT_TREE_SEL_CHANGED
293 EVT_TREE_SEL_CHANGING
= wx
.EVT_TREE_SEL_CHANGING
294 EVT_TREE_KEY_DOWN
= wx
.EVT_TREE_KEY_DOWN
295 EVT_TREE_ITEM_ACTIVATED
= wx
.EVT_TREE_ITEM_ACTIVATED
296 EVT_TREE_ITEM_RIGHT_CLICK
= wx
.EVT_TREE_ITEM_RIGHT_CLICK
297 EVT_TREE_ITEM_MIDDLE_CLICK
= wx
.EVT_TREE_ITEM_MIDDLE_CLICK
298 EVT_TREE_END_DRAG
= wx
.EVT_TREE_END_DRAG
299 EVT_TREE_STATE_IMAGE_CLICK
= wx
.EVT_TREE_STATE_IMAGE_CLICK
300 EVT_TREE_ITEM_GETTOOLTIP
= wx
.EVT_TREE_ITEM_GETTOOLTIP
301 EVT_TREE_ITEM_MENU
= wx
.EVT_TREE_ITEM_MENU
302 EVT_TREE_ITEM_CHECKING
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKING
, 1)
303 EVT_TREE_ITEM_CHECKED
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKED
, 1)
304 EVT_TREE_ITEM_HYPERLINK
= wx
.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK
, 1)
307 def GetFlaggedData():
308 return zlib
.decompress(
309 'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
310 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
311 |\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\
312 \x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\
313 \xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\
314 \xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\
315 \xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\
316 \xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\
317 \r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\
318 \xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\
319 \xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\
320 \xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\
321 \x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\
322 \xb3\xb7H,a\xe1\xa9)\xdf<e$2Ble\x85\x94e\xb1\x96\xcep\xfb\xdd-D\x04\xa5\x14\
323 \xdeZ\'\xb1\x84\x85\xd8\x8bm\x84\xe6\x977\x7f8kog)\xba\xc4\xb7\xe5\xef$\xe2?\
324 \xe9\xa9\xbf\x86R\n\x11a&\x1c\xc1^lC|\r.\x02\xb3\x8b\x9b\xa6&G\x13W\xaa\xbb\
325 \x91_\x05\x0c\x1d\xbfI\xc7\xa1\x8e\xbf&a|:\x8c\xaf\xc1\x05J4\x8e\xd6>36\x192\
326 \xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\
327 \xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\
328 nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\
329 \xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\
330 \xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' )
332 def GetFlaggedBitmap():
333 return wx
.BitmapFromImage(GetFlaggedImage())
335 def GetFlaggedImage():
336 stream
= cStringIO
.StringIO(GetFlaggedData())
337 return wx
.ImageFromStream(stream
)
339 #----------------------------------------------------------------------
340 def GetNotFlaggedData():
341 return zlib
.decompress(
342 'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
343 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
344 |\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\
345 \x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\
346 \x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\
347 \x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\
348 \x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\
349 \xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\
350 \x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\
351 \xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\
352 \x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\
353 \xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\
354 \x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\
355 \x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\
356 \xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\
357 \xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\
358 \xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' )
360 def GetNotFlaggedBitmap():
361 return wx
.BitmapFromImage(GetNotFlaggedImage())
363 def GetNotFlaggedImage():
364 stream
= cStringIO
.StringIO(GetNotFlaggedData())
365 return wx
.ImageFromStream(stream
)
367 #----------------------------------------------------------------------
368 def GetCheckedData():
369 return zlib
.decompress(
370 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
371 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\
372 \x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\
373 \xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\
374 \x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\
375 \xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\
376 \xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\
377 \xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\
378 \x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\
379 \xeb\x9c\x12\x9a\x00\x92\xccS\x9f" )
381 def GetCheckedBitmap():
382 return wx
.BitmapFromImage(GetCheckedImage())
384 def GetCheckedImage():
385 stream
= cStringIO
.StringIO(GetCheckedData())
386 return wx
.ImageFromStream(stream
)
388 #----------------------------------------------------------------------
389 def GetNotCheckedData():
390 return zlib
.decompress(
391 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
392 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\
393 \xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\
394 \xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\
395 \x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\
396 \xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\
397 \xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\
400 def GetNotCheckedBitmap():
401 return wx
.BitmapFromImage(GetNotCheckedImage())
403 def GetNotCheckedImage():
404 stream
= cStringIO
.StringIO(GetNotCheckedData())
405 return wx
.ImageFromStream(stream
)
408 def GrayOut(anImage
):
410 Convert the given image (in place) to a grayed-out version,
411 appropriate for a 'disabled' appearance.
414 factor
= 0.7 # 0 < f < 1. Higher Is Grayer
416 if anImage
.HasMask():
417 maskColor
= (anImage
.GetMaskRed(), anImage
.GetMaskGreen(), anImage
.GetMaskBlue())
421 data
= map(ord, list(anImage
.GetData()))
423 for i
in range(0, len(data
), 3):
425 pixel
= (data
[i
], data
[i
+1], data
[i
+2])
426 pixel
= MakeGray(pixel
, factor
, maskColor
)
431 anImage
.SetData(''.join(map(chr, data
)))
436 def MakeGray((r
,g
,b
), factor
, maskColor
):
438 Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be
442 if (r
,g
,b
) != maskColor
:
443 return map(lambda x
: int((230 - x
) * factor
) + x
, (r
,g
,b
))
448 def DrawTreeItemButton(win
, dc
, rect
, flags
):
449 """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """
452 dc
.SetPen(wx
.GREY_PEN
)
453 dc
.SetBrush(wx
.WHITE_BRUSH
)
454 dc
.DrawRectangleRect(rect
)
457 xMiddle
= rect
.x
+ rect
.width
/2
458 yMiddle
= rect
.y
+ rect
.height
/2
460 # half of the length of the horz lines in "-" and "+"
461 halfWidth
= rect
.width
/2 - 2
462 dc
.SetPen(wx
.BLACK_PEN
)
463 dc
.DrawLine(xMiddle
- halfWidth
, yMiddle
,
464 xMiddle
+ halfWidth
+ 1, yMiddle
)
466 if not flags
& _CONTROL_EXPANDED
:
469 halfHeight
= rect
.height
/2 - 2
470 dc
.DrawLine(xMiddle
, yMiddle
- halfHeight
,
471 xMiddle
, yMiddle
+ halfHeight
+ 1)
474 #---------------------------------------------------------------------------
475 # DragImage Implementation
476 # This Class Handles The Creation Of A Custom Image In Case Of Item Drag
478 #---------------------------------------------------------------------------
480 class DragImage(wx
.DragImage
):
482 This class handles the creation of a custom image in case of item drag
486 def __init__(self
, treeCtrl
, item
):
488 Default class constructor.
489 For internal use: do not call it in your code!
492 text
= item
.GetText()
493 font
= item
.Attr().GetFont()
494 colour
= item
.Attr().GetTextColour()
498 font
= treeCtrl
._normalFont
500 backcolour
= treeCtrl
.GetBackgroundColour()
501 r
, g
, b
= int(backcolour
.Red()), int(backcolour
.Green()), int(backcolour
.Blue())
502 backcolour
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
503 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
504 self
._backgroundColour
= backcolour
506 tempdc
= wx
.ClientDC(treeCtrl
)
508 width
, height
, dummy
= tempdc
.GetMultiLineTextExtent(text
+ "M")
510 image
= item
.GetCurrentImage()
512 image_w
, image_h
= 0, 0
513 wcheck
, hcheck
= 0, 0
521 if image
!= _NO_IMAGE
:
522 if treeCtrl
._imageListNormal
:
523 image_w
, image_h
= treeCtrl
._imageListNormal
.GetSize(image
)
525 itemimage
= treeCtrl
._imageListNormal
.GetBitmap(image
)
527 checkimage
= item
.GetCurrentCheckedImage()
529 if checkimage
is not None:
530 if treeCtrl
._imageListCheck
:
531 wcheck
, hcheck
= treeCtrl
._imageListCheck
.GetSize(checkimage
)
533 itemcheck
= treeCtrl
._imageListCheck
.GetBitmap(checkimage
)
535 total_h
= max(hcheck
, height
)
536 total_h
= max(image_h
, total_h
)
540 yimagepos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0]
542 if checkimage
is not None:
544 ycheckpos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0] + 2
546 extraH
= ((total_h
> height
) and [(total_h
- height
)/2] or [0])[0]
548 xtextpos
= wcheck
+ image_w
551 total_h
= max(image_h
, hcheck
)
552 total_h
= max(total_h
, height
)
555 total_h
+= 2 # at least 2 pixels
557 total_h
+= total_h
/10 # otherwise 10% extra spacing
559 total_w
= image_w
+ wcheck
+ width
561 self
._total
_w
= total_w
562 self
._total
_h
= total_h
563 self
._itemimage
= itemimage
564 self
._itemcheck
= itemcheck
566 self
._colour
= colour
568 self
._xtextpos
= xtextpos
569 self
._ytextpos
= ytextpos
570 self
._ximagepos
= ximagepos
571 self
._yimagepos
= yimagepos
572 self
._xcheckpos
= xcheckpos
573 self
._ycheckpos
= ycheckpos
574 self
._textwidth
= width
575 self
._textheight
= height
576 self
._extraH
= extraH
578 self
._bitmap
= self
.CreateBitmap()
580 wx
.DragImage
.__init
__(self
, self
._bitmap
)
583 def CreateBitmap(self
):
584 """Actually creates the dnd bitmap."""
586 memory
= wx
.MemoryDC()
588 bitmap
= wx
.EmptyBitmap(self
._total
_w
, self
._total
_h
)
589 memory
.SelectObject(bitmap
)
591 memory
.SetTextBackground(self
._backgroundColour
)
592 memory
.SetBackground(wx
.Brush(self
._backgroundColour
))
593 memory
.SetFont(self
._font
)
594 memory
.SetTextForeground(self
._colour
)
598 memory
.DrawBitmap(self
._itemimage
, self
._ximagepos
, self
._yimagepos
, True)
601 memory
.DrawBitmap(self
._itemcheck
, self
._xcheckpos
, self
._ycheckpos
, True)
603 textrect
= wx
.Rect(self
._xtextpos
, self
._ytextpos
+self
._extraH
, self
._textwidth
, self
._textheight
)
604 memory
.DrawLabel(self
._text
, textrect
)
606 memory
.SelectObject(wx
.NullBitmap
)
611 # ----------------------------------------------------------------------------
612 # TreeItemAttr: a structure containing the visual attributes of an item
613 # ----------------------------------------------------------------------------
616 """Creates the item attributes (text colour, background colour and font)."""
618 def __init__(self
, colText
=wx
.NullColour
, colBack
=wx
.NullColour
, font
=wx
.NullFont
):
620 Default class constructor.
621 For internal use: do not call it in your code!
624 self
._colText
= colText
625 self
._colBack
= colBack
629 def SetTextColour(self
, colText
):
630 """Sets the attribute text colour."""
632 self
._colText
= colText
635 def SetBackgroundColour(self
, colBack
):
636 """Sets the attribute background colour."""
638 self
._colBack
= colBack
641 def SetFont(self
, font
):
642 """Sets the attribute font."""
648 def HasTextColour(self
):
649 """Returns whether the attribute has text colour."""
651 return self
._colText
!= wx
.NullColour
654 def HasBackgroundColour(self
):
655 """Returns whether the attribute has background colour."""
657 return self
._colBack
!= wx
.NullColour
661 """Returns whether the attribute has font."""
663 return self
._font
!= wx
.NullFont
667 def GetTextColour(self
):
668 """Returns the attribute text colour."""
673 def GetBackgroundColour(self
):
674 """Returns the attribute background colour."""
680 """Returns the attribute font."""
685 # ----------------------------------------------------------------------------
686 # CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent
688 # NB: Note That Not All The Accessors Make Sense For All The Events, See The
689 # Event Description Below.
690 # ----------------------------------------------------------------------------
692 class CommandTreeEvent(wx
.PyCommandEvent
):
694 CommandTreeEvent is a special subclassing of wx.PyCommandEvent.
695 NB: note that not all the accessors make sense for all the events, see the
696 event description for every method in this class.
699 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
700 label
=None, **kwargs
):
702 Default class constructor.
703 For internal use: do not call it in your code!
706 wx
.PyCommandEvent
.__init
__(self
, type, id, **kwargs
)
708 self
._evtKey
= evtKey
709 self
._pointDrag
= point
715 Gets the item on which the operation was performed or the newly selected
716 item for EVT_TREE_SEL_CHANGED/ING events.
722 def SetItem(self
, item
):
724 Sets the item on which the operation was performed or the newly selected
725 item for EVT_TREE_SEL_CHANGED/ING events.
731 def GetOldItem(self
):
732 """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item."""
737 def SetOldItem(self
, item
):
738 """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item."""
745 Returns the point where the mouse was when the drag operation started
746 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
749 return self
._pointDrag
752 def SetPoint(self
, pt
):
754 Sets the point where the mouse was when the drag operation started
755 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
761 def GetKeyEvent(self
):
762 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
767 def GetKeyCode(self
):
768 """Returns the integer key code (for EVT_TREE_KEY_DOWN only)."""
770 return self
._evtKey
.GetKeyCode()
773 def SetKeyEvent(self
, evt
):
774 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
780 """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
785 def SetLabel(self
, label
):
786 """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
791 def IsEditCancelled(self
):
792 """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
794 return self
._editCancelled
797 def SetEditCanceled(self
, editCancelled
):
798 """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
800 self
._editCancelled
= editCancelled
803 def SetToolTip(self
, toolTip
):
804 """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
806 self
._label
= toolTip
809 def GetToolTip(self
):
810 """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
815 # ----------------------------------------------------------------------------
816 # TreeEvent is a special class for all events associated with tree controls
818 # NB: note that not all accessors make sense for all events, see the event
820 # ----------------------------------------------------------------------------
822 class TreeEvent(CommandTreeEvent
):
824 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
825 label
=None, **kwargs
):
827 Default class constructor.
828 For internal use: do not call it in your code!
831 CommandTreeEvent
.__init
__(self
, type, id, item
, evtKey
, point
, label
, **kwargs
)
832 self
.notify
= wx
.NotifyEvent(type, id)
835 def GetNotifyEvent(self
):
836 """Returns the actual wx.NotifyEvent."""
842 """Returns whether the event is allowed or not."""
844 return self
.notify
.IsAllowed()
848 """Vetos the event."""
854 """The event is allowed."""
859 # -----------------------------------------------------------------------------
860 # Auxiliary Classes: TreeRenameTimer
861 # -----------------------------------------------------------------------------
863 class TreeRenameTimer(wx
.Timer
):
864 """Timer used for enabling in-place edit."""
866 def __init__(self
, owner
):
868 Default class constructor.
869 For internal use: do not call it in your code!
872 wx
.Timer
.__init
__(self
)
877 """The timer has expired."""
879 self
._owner
.OnRenameTimer()
882 # -----------------------------------------------------------------------------
883 # Auxiliary Classes: TreeTextCtrl
884 # This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item
885 # -----------------------------------------------------------------------------
887 class TreeTextCtrl(wx
.TextCtrl
):
888 """Control used for in-place edit."""
890 def __init__(self
, owner
, item
=None):
892 Default class constructor.
893 For internal use: do not call it in your code!
897 self
._itemEdited
= item
898 self
._startValue
= item
.GetText()
899 self
._finished
= False
900 self
._aboutToFinish
= False
902 w
= self
._itemEdited
.GetWidth()
903 h
= self
._itemEdited
.GetHeight()
905 wnd
= self
._itemEdited
.GetWindow()
907 w
= w
- self
._itemEdited
.GetWindowSize()[0]
910 x
, y
= self
._owner
.CalcScrolledPosition(item
.GetX(), item
.GetY())
915 image
= item
.GetCurrentImage()
917 if image
!= _NO_IMAGE
:
919 if self
._owner
._imageListNormal
:
920 image_w
, image_h
= self
._owner
._imageListNormal
.GetSize(image
)
925 raise "\n ERROR: You Must Create An Image List To Use Images!"
927 checkimage
= item
.GetCurrentCheckedImage()
929 if checkimage
is not None:
930 wcheck
, hcheck
= self
._owner
._imageListCheck
.GetSize(checkimage
)
936 h
= max(hcheck
, image_h
)
937 dc
= wx
.ClientDC(self
._owner
)
938 h
= max(h
, dc
.GetTextExtent("Aq")[1])
941 # FIXME: what are all these hardcoded 4, 8 and 11s really?
942 x
+= image_w
+ wcheck
943 w
-= image_w
+ 4 + wcheck
945 if wx
.Platform
== "__WXMAC__":
946 bs
= self
.DoGetBestSize()
947 # edit control height
949 diff
= h
- ( bs
.y
- 8 )
953 wx
.TextCtrl
.__init
__(self
, self
._owner
, wx
.ID_ANY
, self
._startValue
,
954 wx
.Point(x
- 4, y
), wx
.Size(w
+ 15, h
))
956 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
957 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
958 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
961 def AcceptChanges(self
):
962 """Accepts/refuses the changes made by the user."""
964 value
= self
.GetValue()
966 if value
== self
._startValue
:
967 # nothing changed, always accept
968 # when an item remains unchanged, the owner
969 # needs to be notified that the user decided
970 # not to change the tree item label, and that
971 # the edit has been cancelled
972 self
._owner
.OnRenameCancelled(self
._itemEdited
)
975 if not self
._owner
.OnRenameAccept(self
._itemEdited
, value
):
979 # accepted, do rename the item
980 self
._owner
.SetItemText(self
._itemEdited
, value
)
986 """Finish editing."""
988 if not self
._finished
:
990 ## wxPendingDelete.Append(this)
991 self
._finished
= True
992 self
._owner
.SetFocusIgnoringChildren()
993 self
._owner
.ResetTextControl()
996 def OnChar(self
, event
):
997 """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
999 keycode
= event
.GetKeyCode()
1001 if keycode
== wx
.WXK_RETURN
:
1002 self
._aboutToFinish
= True
1003 # Notify the owner about the changes
1004 self
.AcceptChanges()
1005 # Even if vetoed, close the control (consistent with MSW)
1006 wx
.CallAfter(self
.Finish
)
1008 elif keycode
== wx
.WXK_ESCAPE
:
1015 def OnKeyUp(self
, event
):
1016 """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
1018 if not self
._finished
:
1020 # auto-grow the textctrl:
1021 parentSize
= self
._owner
.GetSize()
1022 myPos
= self
.GetPosition()
1023 mySize
= self
.GetSize()
1025 sx
, sy
= self
.GetTextExtent(self
.GetValue() + "M")
1026 if myPos
.x
+ sx
> parentSize
.x
:
1027 sx
= parentSize
.x
- myPos
.x
1031 self
.SetSize((sx
, -1))
1036 def OnKillFocus(self
, event
):
1037 """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
1039 # I commented out those lines, and everything seems to work fine.
1040 # But why in the world are these lines of code here? Maybe GTK
1041 # or MAC give troubles?
1043 ## if not self._finished and not self._aboutToFinish:
1045 ## # We must finish regardless of success, otherwise we'll get
1046 ## # focus problems:
1048 ## if not self.AcceptChanges():
1049 ## self._owner.OnRenameCancelled(self._itemEdited)
1051 # We must let the native text control handle focus, too, otherwise
1052 # it could have problems with the cursor (e.g., in wxGTK).
1056 def StopEditing(self
):
1057 """Suddenly stops the editing."""
1059 self
._owner
.OnRenameCancelled(self
._itemEdited
)
1064 """Returns the item currently edited."""
1066 return self
._itemEdited
1069 # -----------------------------------------------------------------------------
1070 # Auxiliary Classes: TreeFindTimer
1071 # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
1072 # Sufficiently Long Time.
1073 # -----------------------------------------------------------------------------
1075 class TreeFindTimer(wx
.Timer
):
1077 Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
1078 for a sufficiently long time.
1081 def __init__(self
, owner
):
1083 Default class constructor.
1084 For internal use: do not call it in your code!
1087 wx
.Timer
.__init
__(self
)
1092 """The timer has expired."""
1094 self
._owner
._findPrefix
= ""
1097 # -----------------------------------------------------------------------------
1098 # GenericTreeItem Implementation.
1099 # This Class Holds All The Information And Methods For Every Single Item In
1101 # -----------------------------------------------------------------------------
1103 class GenericTreeItem
:
1105 This class holds all the information and methods for every single item in
1106 CustomTreeCtrl. No wx based.
1109 def __init__(self
, parent
, text
="", ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
1111 Default class constructor.
1112 For internal use: do not call it in your code!
1115 # since there can be very many of these, we save size by chosing
1116 # the smallest representation for the elements and by ordering
1117 # the members to avoid padding.
1118 self
._text
= text
# label to be rendered for item
1119 self
._data
= data
# user-provided data
1121 self
._children
= [] # list of children
1122 self
._parent
= parent
# parent of this item
1124 self
._attr
= None # attributes???
1126 # tree ctrl images for the normal, selected, expanded and
1127 # expanded+selected states
1128 self
._images
= [-1, -1, -1, -1]
1129 self
._images
[TreeItemIcon_Normal
] = image
1130 self
._images
[TreeItemIcon_Selected
] = selImage
1131 self
._images
[TreeItemIcon_Expanded
] = _NO_IMAGE
1132 self
._images
[TreeItemIcon_SelectedExpanded
] = _NO_IMAGE
1134 self
._checkedimages
= [None, None, None, None]
1136 self
._x
= 0 # (virtual) offset from top
1137 self
._y
= 0 # (virtual) offset from left
1138 self
._width
= 0 # width of this item
1139 self
._height
= 0 # height of this item
1141 self
._isCollapsed
= True
1142 self
._hasHilight
= False # same as focused
1143 self
._hasPlus
= False # used for item which doesn't have
1144 # children but has a [+] button
1145 self
._isBold
= False # render the label in bold font
1146 self
._isItalic
= False # render the label in italic font
1147 self
._ownsAttr
= False # delete attribute when done
1148 self
._type
= ct_type
# item type: 0=normal, 1=check, 2=radio
1149 self
._checked
= False # only meaningful for check and radio
1150 self
._enabled
= True # flag to enable/disable an item
1151 self
._hypertext
= False # indicates if the item is hypertext
1152 self
._visited
= False # visited state for an hypertext item
1155 # do not construct the array for normal items
1156 self
._checkedimages
[TreeItemIcon_Checked
] = 0
1157 self
._checkedimages
[TreeItemIcon_NotChecked
] = 1
1158 self
._checkedimages
[TreeItemIcon_Flagged
] = 2
1159 self
._checkedimages
[TreeItemIcon_NotFlagged
] = 3
1162 if parent
.GetType() == 2 and not parent
.IsChecked():
1163 # if the node parent is a radio not enabled, we are disabled
1164 self
._enabled
= False
1166 self
._wnd
= wnd
# are we holding a window?
1169 if wnd
.GetSizer(): # the window is a complex one hold by a sizer
1170 size
= wnd
.GetBestSize()
1171 else: # simple window, without sizers
1172 size
= wnd
.GetSize()
1174 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1175 # No other solution to handle the focus changing from an item in
1176 # CustomTreeCtrl and the window associated to an item
1177 # Do better strategies exist?
1178 self
._wnd
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1180 self
._height
= size
.GetHeight() + 2
1181 self
._width
= size
.GetWidth()
1182 self
._windowsize
= size
1184 # We don't show the window if the item is collapsed
1185 if self
._isCollapsed
:
1186 self
._wnd
.Show(False)
1188 # The window is enabled only if the item is enabled
1189 self
._wnd
.Enable(self
._enabled
)
1190 self
._windowenabled
= self
._enabled
1195 Returns whether the item is ok or not. Useless on Python, but added for
1196 backward compatibility with the C++ implementation.
1202 def GetChildren(self
):
1203 """Returns the item's children."""
1205 return self
._children
1209 """Returns the item text."""
1214 def GetImage(self
, which
=TreeItemIcon_Normal
):
1215 """Returns the item image for a particular state."""
1217 return self
._images
[which
]
1220 def GetCheckedImage(self
, which
=TreeItemIcon_Checked
):
1221 """Returns the item check image. Meaningful only for radio & check items."""
1223 return self
._checkedimages
[which
]
1227 """Returns the data associated to this item."""
1232 def SetImage(self
, image
, which
):
1233 """Sets the item image."""
1235 self
._images
[which
] = image
1238 def SetData(self
, data
):
1239 """Sets the data associated to this item."""
1244 def SetHasPlus(self
, has
=True):
1245 """Sets whether an item has the 'plus' button."""
1250 def SetBold(self
, bold
):
1251 """Sets the item font bold."""
1256 def SetItalic(self
, italic
):
1257 """Sets the item font italic."""
1259 self
._isItalic
= italic
1263 """Returns the x position on an item in the ScrolledWindow."""
1269 """Returns the y position on an item in the ScrolledWindow."""
1275 """Sets the x position on an item in the ScrolledWindow."""
1281 """Sets the y position on an item in the ScrolledWindow."""
1286 def GetHeight(self
):
1287 """Returns the height of the item."""
1293 """Returns the width of the item."""
1298 def SetHeight(self
, h
):
1299 """Sets the height of the item."""
1304 def SetWidth(self
, w
):
1305 """Sets the width of the item."""
1310 def SetWindow(self
, wnd
):
1311 """Sets the window associated to the item."""
1316 def GetWindow(self
):
1317 """Returns the window associated to the item."""
1322 def GetWindowEnabled(self
):
1323 """Returns whether the associated window is enabled or not."""
1326 raise "\nERROR: This Item Has No Window Associated"
1328 return self
._windowenabled
1331 def SetWindowEnabled(self
, enable
=True):
1332 """Sets whether the associated window is enabled or not."""
1335 raise "\nERROR: This Item Has No Window Associated"
1337 self
._windowenabled
= enable
1338 self
._wnd
.Enable(enable
)
1341 def GetWindowSize(self
):
1342 """Returns the associated window size."""
1344 return self
._windowsize
1347 def OnSetFocus(self
, event
):
1348 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1350 treectrl
= self
._wnd
.GetParent()
1351 select
= treectrl
.GetSelection()
1353 # If the window is associated to an item that currently is selected
1354 # (has focus) we don't kill the focus. Otherwise we do it.
1356 treectrl
._hasFocus
= False
1358 treectrl
._hasFocus
= True
1365 Returns the item type. It should be one of:
1374 def SetHyperText(self
, hyper
=True):
1375 """Sets whether the item is hypertext or not."""
1377 self
._hypertext
= hyper
1380 def SetVisited(self
, visited
=True):
1381 """Sets whether an hypertext item was visited or not."""
1383 self
._visited
= visited
1386 def GetVisited(self
):
1387 """Returns whether an hypertext item was visited or not."""
1389 return self
._visited
1392 def IsHyperText(self
):
1393 """Returns whether the item is hypetext or not."""
1395 return self
._hypertext
1398 def GetParent(self
):
1399 """Gets the item parent."""
1404 def Insert(self
, child
, index
):
1405 """Inserts an item in the item children."""
1407 self
._children
.insert(index
, child
)
1411 """Expand the item."""
1413 self
._isCollapsed
= False
1417 """Collapse the item."""
1419 self
._isCollapsed
= True
1422 def SetHilight(self
, set=True):
1423 """Sets the item focus/unfocus."""
1425 self
._hasHilight
= set
1428 def HasChildren(self
):
1429 """Returns whether the item has children or not."""
1431 return len(self
._children
) > 0
1434 def IsSelected(self
):
1435 """Returns whether the item is selected or not."""
1437 return self
._hasHilight
!= 0
1440 def IsExpanded(self
):
1441 """Returns whether the item is expanded or not."""
1443 return not self
._isCollapsed
1446 def IsChecked(self
):
1447 """Returns whether the item is checked or not."""
1449 return self
._checked
1452 def Check(self
, checked
=True):
1453 """Check an item. Meaningful only for check and radio items."""
1455 self
._checked
= checked
1459 """Returns whether the item has the plus button or not."""
1461 return self
._hasPlus
or self
.HasChildren()
1465 """Returns whether the item font is bold or not."""
1467 return self
._isBold
!= 0
1471 """Returns whether the item font is italic or not."""
1473 return self
._isItalic
!= 0
1476 def Enable(self
, enable
=True):
1477 """Enables/disables the item."""
1479 self
._enabled
= enable
1482 def IsEnabled(self
):
1483 """Returns whether the item is enabled or not."""
1485 return self
._enabled
1488 def GetAttributes(self
):
1489 """Returns the item attributes (font, colours)."""
1495 """Creates a new attribute (font, colours)."""
1499 self
._attr
= TreeItemAttr()
1500 self
._ownsAttr
= True
1505 def SetAttributes(self
, attr
):
1506 """Sets the item attributes (font, colours)."""
1512 self
._ownsAttr
= False
1515 def AssignAttributes(self
, attr
):
1516 """Assigns the item attributes (font, colours)."""
1518 self
.SetAttributes(attr
)
1519 self
._ownsAttr
= True
1522 def DeleteChildren(self
, tree
):
1523 """Deletes the item children."""
1525 for child
in self
._children
:
1527 tree
.SendDeleteEvent(child
)
1529 child
.DeleteChildren(tree
)
1531 if child
== tree
._select
_me
:
1532 tree
._select
_me
= None
1534 # We have to destroy the associated window
1535 wnd
= child
.GetWindow()
1540 if child
in tree
._itemWithWindow
:
1541 tree
._itemWithWindow
.remove(child
)
1548 def SetText(self
, text
):
1549 """Sets the item text."""
1554 def GetChildrenCount(self
, recursively
=True):
1555 """Gets the number of children."""
1557 count
= len(self
._children
)
1564 for n
in xrange(count
):
1565 total
+= self
._children
[n
].GetChildrenCount()
1570 def GetSize(self
, x
, y
, theButton
):
1571 """Returns the item size."""
1573 bottomY
= self
._y
+ theButton
.GetLineHeight(self
)
1578 width
= self
._x
+ self
._width
1583 if self
.IsExpanded():
1584 for child
in self
._children
:
1585 x
, y
= child
.GetSize(x
, y
, theButton
)
1590 def HitTest(self
, point
, theCtrl
, flags
=0, level
=0):
1592 HitTest method for an item. Called from the main window HitTest.
1593 see the CustomTreeCtrl HitTest method for the flags explanation.
1596 # for a hidden root node, don't evaluate it, but do evaluate children
1597 if not (level
== 0 and theCtrl
.HasFlag(TR_HIDE_ROOT
)):
1600 h
= theCtrl
.GetLineHeight(self
)
1602 if point
.y
> self
._y
and point
.y
< self
._y
+ h
:
1604 y_mid
= self
._y
+ h
/2
1607 flags |
= TREE_HITTEST_ONITEMUPPERPART
1609 flags |
= TREE_HITTEST_ONITEMLOWERPART
1611 xCross
= self
._x
- theCtrl
.GetSpacing()
1613 if wx
.Platform
== "__WXMAC__":
1614 # according to the drawing code the triangels are drawn
1615 # at -4 , -4 from the position up to +10/+10 max
1616 if point
.x
> xCross
-4 and point
.x
< xCross
+10 and point
.y
> y_mid
-4 and \
1617 point
.y
< y_mid
+10 and self
.HasPlus() and theCtrl
.HasButtons():
1619 flags |
= TREE_HITTEST_ONITEMBUTTON
1622 # 5 is the size of the plus sign
1623 if point
.x
> xCross
-6 and point
.x
< xCross
+6 and point
.y
> y_mid
-6 and \
1624 point
.y
< y_mid
+6 and self
.HasPlus() and theCtrl
.HasButtons():
1626 flags |
= TREE_HITTEST_ONITEMBUTTON
1629 if point
.x
>= self
._x
and point
.x
<= self
._x
+ self
._width
:
1634 # assuming every image (normal and selected) has the same size!
1635 if self
.GetImage() != _NO_IMAGE
and theCtrl
._imageListNormal
:
1636 image_w
, image_h
= theCtrl
._imageListNormal
.GetSize(self
.GetImage())
1638 if self
.GetCheckedImage() is not None:
1639 wcheck
, hcheck
= theCtrl
._imageListCheck
.GetSize(self
.GetCheckedImage())
1641 if wcheck
and point
.x
<= self
._x
+ wcheck
+ 1:
1642 flags |
= TREE_HITTEST_ONITEMCHECKICON
1645 if image_w
!= -1 and point
.x
<= self
._x
+ wcheck
+ image_w
+ 1:
1646 flags |
= TREE_HITTEST_ONITEMICON
1648 flags |
= TREE_HITTEST_ONITEMLABEL
1652 if point
.x
< self
._x
:
1653 flags |
= TREE_HITTEST_ONITEMINDENT
1654 if point
.x
> self
._x
+ self
._width
:
1655 flags |
= TREE_HITTEST_ONITEMRIGHT
1659 # if children are expanded, fall through to evaluate them
1660 if self
._isCollapsed
:
1664 for child
in self
._children
:
1665 res
, flags
= child
.HitTest(point
, theCtrl
, flags
, level
+ 1)
1672 def GetCurrentImage(self
):
1673 """Returns the current item image."""
1677 if self
.IsExpanded():
1679 if self
.IsSelected():
1681 image
= self
.GetImage(TreeItemIcon_SelectedExpanded
)
1683 if image
== _NO_IMAGE
:
1685 # we usually fall back to the normal item, but try just the
1686 # expanded one (and not selected) first in this case
1687 image
= self
.GetImage(TreeItemIcon_Expanded
)
1689 else: # not expanded
1691 if self
.IsSelected():
1692 image
= self
.GetImage(TreeItemIcon_Selected
)
1694 # maybe it doesn't have the specific image we want,
1695 # try the default one instead
1696 if image
== _NO_IMAGE
:
1697 image
= self
.GetImage()
1702 def GetCurrentCheckedImage(self
):
1703 """Returns the current item check image."""
1708 if self
.IsChecked():
1709 if self
._type
== 1: # Checkbox
1710 return self
._checkedimages
[TreeItemIcon_Checked
]
1712 return self
._checkedimages
[TreeItemIcon_Flagged
]
1714 if self
._type
== 1: # Checkbox
1715 return self
._checkedimages
[TreeItemIcon_NotChecked
]
1717 return self
._checkedimages
[TreeItemIcon_NotFlagged
]
1720 def EventFlagsToSelType(style
, shiftDown
=False, ctrlDown
=False):
1722 Translate the key or mouse event flag to the type of selection we
1726 is_multiple
= (style
& TR_MULTIPLE
) != 0
1727 extended_select
= shiftDown
and is_multiple
1728 unselect_others
= not (extended_select
or (ctrlDown
and is_multiple
))
1730 return is_multiple
, extended_select
, unselect_others
1733 # -----------------------------------------------------------------------------
1734 # CustomTreeCtrl Main Implementation.
1735 # This Is The Main Class.
1736 # -----------------------------------------------------------------------------
1738 class CustomTreeCtrl(wx
.ScrolledWindow
):
1740 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1741 style
=0, ctstyle
=TR_DEFAULT_STYLE
, validator
=wx
.DefaultValidator
,
1742 name
="CustomTreeCtrl"):
1744 Default class constructor.
1746 parent: parent window. Must not be none.
1748 id: window identifier. A value of -1 indicates a default value.
1750 pos: window position.
1752 size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
1754 style: the underlying wx.ScrolledWindow style
1756 ctstyle: CustomTreeCtrl window style. This can be one of:
1758 TR_HAS_BUTTONS # draw collapsed/expanded btns
1759 TR_NO_LINES # don't draw lines at all
1760 TR_LINES_AT_ROOT # connect top-level nodes
1761 TR_TWIST_BUTTONS # draw mac-like twist buttons
1762 TR_SINGLE # single selection mode
1763 TR_MULTIPLE # can select multiple items
1764 TR_EXTENDED # todo: allow extended selection
1765 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1766 TR_EDIT_LABELS # can edit item labels
1767 TR_ROW_LINES # put border around items
1768 TR_HIDE_ROOT # don't display root node
1769 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1770 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1771 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1773 validator: window validator.
1778 self
._current
= self
._key
_current
= self
._anchor
= self
._select
_me
= None
1779 self
._hasFocus
= False
1782 # Default line height: it will soon be changed
1783 self
._lineHeight
= 10
1784 # Item indent wrt parent
1786 # item horizontal spacing between the start and the text
1789 # Brushes for focused/unfocused items (also gradient type)
1790 self
._hilightBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
))
1791 btnshadow
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1792 self
._hilightUnfocusedBrush
= wx
.Brush(btnshadow
)
1793 r
, g
, b
= btnshadow
.Red(), btnshadow
.Green(), btnshadow
.Blue()
1794 backcolour
= ((r
>> 1) - 20, (g
>> 1) - 20, (b
>> 1) - 20)
1795 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1796 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1798 # image list for icons
1799 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1800 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1802 # Drag and drop initial settings
1805 self
._isDragging
= False
1806 self
._dropTarget
= self
._oldSelection
= None
1807 self
._dragImage
= None
1808 self
._underMouse
= None
1810 # TextCtrl initial settings for editable items
1811 self
._textCtrl
= None
1812 self
._renameTimer
= None
1814 # This one allows us to handle Freeze() and Thaw() calls
1815 self
._freezeCount
= 0
1817 self
._findPrefix
= ""
1818 self
._findTimer
= None
1820 self
._dropEffectAboveItem
= False
1821 self
._lastOnSame
= False
1823 # Default normal and bold fonts for an item
1824 self
._hasFont
= True
1825 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1826 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1827 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1828 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1832 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1833 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1834 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1835 self
._hypertextnewcolour
= wx
.BLUE
1836 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1837 self
._isonhyperlink
= False
1839 # Default CustomTreeCtrl background colour.
1840 self
._backgroundColour
= wx
.WHITE
1842 # Background image settings
1843 self
._backgroundImage
= None
1844 self
._imageStretchStyle
= _StyleTile
1846 # Disabled items colour
1847 self
._disabledColour
= wx
.Colour(180, 180, 180)
1849 # Gradient selection colours
1850 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1851 self
._secondcolour
= wx
.WHITE
1852 self
._usegradients
= False
1853 self
._gradientstyle
= 0 # Horizontal Gradient
1855 # Vista Selection Styles
1856 self
._vistaselection
= False
1858 # Connection lines style
1859 if wx
.Platform
!= "__WXMAC__":
1860 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1861 self
._dottedPen
.SetDashes([1,1])
1862 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1864 self
._dottedPen
= wx
.Pen("grey", 1)
1866 # Pen Used To Draw The Border Around Selected Items
1867 self
._borderPen
= wx
.BLACK_PEN
1868 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1870 # For Appended Windows
1871 self
._hasWindows
= False
1872 self
._itemWithWindow
= []
1874 if wx
.Platform
== "__WXMAC__":
1876 platform
, major
, minor
= wx
.GetOsVersion()
1878 ctstyle
&= ~TR_LINES_AT_ROOT
1879 ctstyle |
= TR_NO_LINES
1882 ctstyle |
= TR_ROW_LINES
1884 self
._windowStyle
= ctstyle
1886 # Create the default check image list
1887 self
.SetImageListCheck(13, 13)
1889 # A constant to use my translation of RendererNative.DrawTreeItemButton
1890 # if the wxPython version is less than 2.6.2.1.
1891 if wx
.VERSION_STRING
< "2.6.2.1":
1892 self
._drawingfunction
= DrawTreeItemButton
1894 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1896 # Create our container... at last!
1897 wx
.ScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1899 # If the tree display has no buttons, but does have
1900 # connecting lines, we can use a narrower layout.
1901 # It may not be a good idea to force this...
1902 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1906 self
.SetValidator(validator
)
1908 attr
= self
.GetDefaultAttributes()
1909 self
.SetOwnForegroundColour(attr
.colFg
)
1910 self
.SetOwnBackgroundColour(wx
.WHITE
)
1912 if not self
._hasFont
:
1913 self
.SetOwnFont(attr
.font
)
1918 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1919 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1920 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1921 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1922 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1923 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1924 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1925 self
.Bind(wx
.EVT_IDLE
, self
.OnInternalIdle
)
1926 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1928 # Sets the focus to ourselves: this is useful if you have items
1929 # with associated widgets.
1934 def OnDestroy(self
, event
):
1935 """Handles the wx.EVT_WINDOW_DESTROY event."""
1937 # Here there may be something I miss... do I have to destroy
1939 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1940 self
._renameTimer
.Stop()
1941 del self
._renameTimer
1943 if self
._findTimer
and self
._findTimer
.IsRunning():
1944 self
._findTimer
.Stop()
1951 """Returns the global number of items in the tree."""
1953 if not self
._anchor
:
1957 count
= self
._anchor
.GetChildrenCount()
1959 if not self
.HasFlag(TR_HIDE_ROOT
):
1960 # take the root itself into account
1966 def GetIndent(self
):
1967 """Returns the item indentation."""
1972 def GetSpacing(self
):
1973 """Returns the spacing between the start and the text."""
1975 return self
._spacing
1978 def GetRootItem(self
):
1979 """Returns the root item."""
1984 def GetSelection(self
):
1985 """Returns the current selection: TR_SINGLE only."""
1987 return self
._current
1990 def ToggleItemSelection(self
, item
):
1991 """Toggles the item selection."""
1994 raise "\nERROR: Invalid Tree Item. "
1996 self
.SelectItem(item
, not self
.IsSelected(item
))
1999 def EnableChildren(self
, item
, enable
=True):
2000 """Enables/disables item children. Used internally."""
2003 if item
.IsExpanded():
2006 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2007 # We hit a radiobutton item not checked, we don't want to
2008 # enable the children
2011 child
, cookie
= self
.GetFirstChild(item
)
2013 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2015 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2016 self
.EnableChildren(child
, enable
)
2017 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2020 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2021 """Enables/disables an item."""
2024 raise "\nERROR: Invalid Tree Item. "
2026 if item
.IsEnabled() == enable
:
2029 if not enable
and item
.IsSelected():
2030 self
.SelectItem(item
, False)
2033 wnd
= item
.GetWindow()
2035 # Handles the eventual window associated to the item
2037 wndenable
= item
.GetWindowEnabled()
2045 # We have to refresh the item line
2046 dc
= wx
.ClientDC(self
)
2047 self
.CalculateSize(item
, dc
)
2048 self
.RefreshLine(item
)
2051 def IsEnabled(self
, item
):
2052 """Returns whether an item is enabled or disabled."""
2055 raise "\nERROR: Invalid Tree Item. "
2057 return item
.IsEnabled()
2060 def SetDisabledColour(self
, colour
):
2061 """Sets the items disabled colour."""
2063 self
._disabledColour
= colour
2067 def GetDisabledColour(self
):
2068 """Returns the items disabled colour."""
2070 return self
._disabledColour
2073 def IsItemChecked(self
, item
):
2074 """Returns whether an item is checked or not."""
2077 raise "\nERROR: Invalid Tree Item. "
2079 return item
.IsChecked()
2082 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2083 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2085 if item
.GetType() == 0:
2091 dc
= wx
.ClientDC(self
)
2092 self
.CalculateSize(item
, dc
)
2093 self
.RefreshLine(item
)
2096 def UnCheckRadioParent(self
, item
, checked
=False):
2097 """Used internally to handle radio node parent correctly."""
2099 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2101 e
.SetEventObject(self
)
2103 if self
.GetEventHandler().ProcessEvent(e
):
2107 dc
= wx
.ClientDC(self
)
2108 self
.RefreshLine(item
)
2109 self
.EnableChildren(item
, checked
)
2110 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2112 e
.SetEventObject(self
)
2113 self
.GetEventHandler().ProcessEvent(e
)
2118 def CheckItem(self
, item
, checked
=True):
2120 Actually checks/uncheks an item, sending (eventually) the two
2121 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2125 raise "\nERROR: Invalid Tree Item. "
2127 # Should we raise an error here?!?
2128 if item
.GetType() == 0:
2131 if item
.GetType() == 2: # it's a radio button
2132 if not checked
and item
.IsChecked(): # Try To Unckeck?
2133 if item
.HasChildren():
2134 self
.UnCheckRadioParent(item
, checked
)
2137 if not self
.UnCheckRadioParent(item
, checked
):
2140 self
.CheckSameLevel(item
, False)
2143 # Radiobuttons are done, let's handle checkbuttons...
2144 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2146 e
.SetEventObject(self
)
2148 if self
.GetEventHandler().ProcessEvent(e
):
2153 dc
= wx
.ClientDC(self
)
2154 self
.RefreshLine(item
)
2156 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2157 ischeck
= self
.IsItemChecked(item
)
2158 self
.AutoCheckChild(item
, ischeck
)
2159 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2160 self
.AutoToggleChild(item
)
2162 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2164 e
.SetEventObject(self
)
2165 self
.GetEventHandler().ProcessEvent(e
)
2168 def AutoToggleChild(self
, item
):
2169 """Transverses the tree and toggles the items. Meaningful only for check items."""
2172 raise "\nERROR: Invalid Tree Item. "
2174 child
, cookie
= self
.GetFirstChild(item
)
2177 if item
.IsExpanded():
2182 if child
.GetType() == 1 and child
.IsEnabled():
2183 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2184 self
.AutoToggleChild(child
)
2185 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2188 def AutoCheckChild(self
, item
, checked
):
2189 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2192 raise "\nERROR: Invalid Tree Item. "
2194 (child
, cookie
) = self
.GetFirstChild(item
)
2197 if item
.IsExpanded():
2201 if child
.GetType() == 1 and child
.IsEnabled():
2202 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2203 self
.AutoCheckChild(child
, checked
)
2204 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2207 def CheckChilds(self
, item
, checked
=True):
2208 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2211 raise "\nERROR: Invalid Tree Item. "
2214 self
.AutoToggleChild(item
)
2216 self
.AutoCheckChild(item
, checked
)
2219 def CheckSameLevel(self
, item
, checked
=False):
2221 Uncheck radio items which are on the same level of the checked one.
2225 parent
= item
.GetParent()
2231 if parent
.IsExpanded():
2234 (child
, cookie
) = self
.GetFirstChild(parent
)
2236 if child
.GetType() == 2 and child
!= item
:
2237 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2238 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2239 self
.EnableChildren(child
, checked
)
2240 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2243 def EditLabel(self
, item
):
2244 """Starts editing an item label."""
2247 raise "\nERROR: Invalid Tree Item. "
2252 def ShouldInheritColours(self
):
2253 """We don't inherit colours from anyone."""
2258 def SetIndent(self
, indent
):
2259 """Sets item indentation."""
2261 self
._indent
= indent
2265 def SetSpacing(self
, spacing
):
2266 """Sets item spacing."""
2268 self
._spacing
= spacing
2272 def HasFlag(self
, flag
):
2273 """Returns whether CustomTreeCtrl has a flag."""
2275 return self
._windowStyle
& flag
2278 def HasChildren(self
, item
):
2279 """Returns whether an item has children or not."""
2282 raise "\nERROR: Invalid Tree Item. "
2284 return len(item
.GetChildren()) > 0
2287 def GetChildrenCount(self
, item
, recursively
=True):
2288 """Gets the item children count."""
2291 raise "\nERROR: Invalid Tree Item. "
2293 return item
.GetChildrenCount(recursively
)
2296 def SetTreeStyle(self
, styles
):
2297 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2299 # Do not try to expand the root node if it hasn't been created yet
2300 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2302 # if we will hide the root, make sure children are visible
2303 self
._anchor
.SetHasPlus()
2304 self
._anchor
.Expand()
2305 self
.CalculatePositions()
2307 # right now, just sets the styles. Eventually, we may
2308 # want to update the inherited styles, but right now
2309 # none of the parents has updatable styles
2311 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2312 selections
= self
.GetSelections()
2313 for select
in selections
[0:-1]:
2314 self
.SelectItem(select
, False)
2316 self
._windowStyle
= styles
2320 def GetTreeStyle(self
):
2321 """Returns the CustomTreeCtrl style."""
2323 return self
._windowStyle
2326 def HasButtons(self
):
2327 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2329 return self
.HasFlag(TR_HAS_BUTTONS
)
2332 # -----------------------------------------------------------------------------
2333 # functions to work with tree items
2334 # -----------------------------------------------------------------------------
2336 def GetItemText(self
, item
):
2337 """Returns the item text."""
2340 raise "\nERROR: Invalid Tree Item. "
2342 return item
.GetText()
2345 def GetItemImage(self
, item
, which
):
2346 """Returns the item image."""
2349 raise "\nERROR: Invalid Tree Item. "
2351 return item
.GetImage(which
)
2354 def GetPyData(self
, item
):
2355 """Returns the data associated to an item."""
2358 raise "\nERROR: Invalid Tree Item. "
2360 return item
.GetData()
2362 GetItemPyData
= GetPyData
2365 def GetItemTextColour(self
, item
):
2366 """Returns the item text colour."""
2369 raise "\nERROR: Invalid Tree Item. "
2371 return item
.Attr().GetTextColour()
2374 def GetItemBackgroundColour(self
, item
):
2375 """Returns the item background colour."""
2378 raise "\nERROR: Invalid Tree Item. "
2380 return item
.Attr().GetBackgroundColour()
2383 def GetItemFont(self
, item
):
2384 """Returns the item font."""
2387 raise "\nERROR: Invalid Tree Item. "
2389 return item
.Attr().GetFont()
2392 def IsItemHyperText(self
, item
):
2393 """Returns whether an item is hypertext or not."""
2396 raise "\nERROR: Invalid Tree Item. "
2398 return item
.IsHyperText()
2401 def SetItemText(self
, item
, text
):
2402 """Sets the item text."""
2405 raise "\nERROR: Invalid Tree Item. "
2407 dc
= wx
.ClientDC(self
)
2409 self
.CalculateSize(item
, dc
)
2410 self
.RefreshLine(item
)
2413 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2414 """Sets the item image, depending on the item state."""
2417 raise "\nERROR: Invalid Tree Item. "
2419 item
.SetImage(image
, which
)
2421 dc
= wx
.ClientDC(self
)
2422 self
.CalculateSize(item
, dc
)
2423 self
.RefreshLine(item
)
2426 def SetPyData(self
, item
, data
):
2427 """Sets the data associated to an item."""
2430 raise "\nERROR: Invalid Tree Item. "
2434 SetItemPyData
= SetPyData
2437 def SetItemHasChildren(self
, item
, has
=True):
2438 """Forces the appearance of the button next to the item."""
2441 raise "\nERROR: Invalid Tree Item. "
2443 item
.SetHasPlus(has
)
2444 self
.RefreshLine(item
)
2447 def SetItemBold(self
, item
, bold
=True):
2448 """Sets the item font bold/unbold."""
2451 raise "\nERROR: Invalid Tree Item. "
2453 # avoid redrawing the tree if no real change
2454 if item
.IsBold() != bold
:
2459 def SetItemItalic(self
, item
, italic
=True):
2460 """Sets the item font italic/non-italic."""
2463 raise "\nERROR: Invalid Tree Item. "
2465 if item
.IsItalic() != italic
:
2466 itemFont
= self
.GetItemFont(item
)
2467 if itemFont
!= wx
.NullFont
:
2472 item
.SetItalic(italic
)
2473 itemFont
.SetStyle(style
)
2474 self
.SetItemFont(item
, itemFont
)
2478 def SetItemDropHighlight(self
, item
, highlight
=True):
2480 Gives the item the visual feedback for drag and drop operations.
2481 This is useful when something is dragged from outside the CustomTreeCtrl.
2485 raise "\nERROR: Invalid Tree Item. "
2488 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2489 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2491 item
.Attr().SetTextColour(fg
)
2492 item
.Attr
.SetBackgroundColour(bg
)
2493 self
.RefreshLine(item
)
2496 def SetItemTextColour(self
, item
, col
):
2497 """Sets the item text colour."""
2500 raise "\nERROR: Invalid Tree Item. "
2502 if self
.GetItemTextColour(item
) == col
:
2505 item
.Attr().SetTextColour(col
)
2506 self
.RefreshLine(item
)
2509 def SetItemBackgroundColour(self
, item
, col
):
2510 """Sets the item background colour."""
2513 raise "\nERROR: Invalid Tree Item. "
2515 item
.Attr().SetBackgroundColour(col
)
2516 self
.RefreshLine(item
)
2519 def SetItemHyperText(self
, item
, hyper
=True):
2520 """Sets whether the item is hypertext or not."""
2523 raise "\nERROR: Invalid Tree Item. "
2525 item
.SetHyperText(hyper
)
2526 self
.RefreshLine(item
)
2529 def SetItemFont(self
, item
, font
):
2530 """Sets the item font."""
2533 raise "\nERROR: Invalid Tree Item. "
2535 if self
.GetItemFont(item
) == font
:
2538 item
.Attr().SetFont(font
)
2542 def SetFont(self
, font
):
2543 """Sets the CustomTreeCtrl font."""
2545 wx
.ScrolledWindow
.SetFont(self
, font
)
2547 self
._normalFont
= font
2548 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2549 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2550 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2555 def GetHyperTextFont(self
):
2556 """Returns the font used to render an hypertext item."""
2558 return self
._hypertextfont
2561 def SetHyperTextFont(self
, font
):
2562 """Sets the font used to render an hypertext item."""
2564 self
._hypertextfont
= font
2568 def SetHyperTextNewColour(self
, colour
):
2569 """Sets the colour used to render a non-visited hypertext item."""
2571 self
._hypertextnewcolour
= colour
2575 def GetHyperTextNewColour(self
):
2576 """Returns the colour used to render a non-visited hypertext item."""
2578 return self
._hypertextnewcolour
2581 def SetHyperTextVisitedColour(self
, colour
):
2582 """Sets the colour used to render a visited hypertext item."""
2584 self
._hypertextvisitedcolour
= colour
2588 def GetHyperTextVisitedColour(self
):
2589 """Returns the colour used to render a visited hypertext item."""
2591 return self
._hypertextvisitedcolour
2594 def SetItemVisited(self
, item
, visited
=True):
2595 """Sets whether an hypertext item was visited."""
2598 raise "\nERROR: Invalid Tree Item. "
2600 item
.SetVisited(visited
)
2601 self
.RefreshLine(item
)
2604 def GetItemVisited(self
, item
):
2605 """Returns whether an hypertext item was visited."""
2608 raise "\nERROR: Invalid Tree Item. "
2610 return item
.GetVisited()
2613 def SetHilightFocusColour(self
, colour
):
2615 Sets the colour used to highlight focused selected items.
2616 This is applied only if gradient and Windows Vista styles are disabled.
2619 self
._hilightBrush
= wx
.Brush(colour
)
2620 self
.RefreshSelected()
2623 def SetHilightNonFocusColour(self
, colour
):
2625 Sets the colour used to highlight unfocused selected items.
2626 This is applied only if gradient and Windows Vista styles are disabled.
2629 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2630 self
.RefreshSelected()
2633 def GetHilightFocusColour(self
):
2635 Returns the colour used to highlight focused selected items.
2636 This is applied only if gradient and Windows Vista styles are disabled.
2639 return self
._hilightBrush
.GetColour()
2642 def GetHilightNonFocusColour(self
):
2644 Returns the colour used to highlight unfocused selected items.
2645 This is applied only if gradient and Windows Vista styles are disabled.
2648 return self
._hilightUnfocusedBrush
.GetColour()
2651 def SetFirstGradientColour(self
, colour
=None):
2652 """Sets the first gradient colour."""
2655 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2657 self
._firstcolour
= colour
2658 if self
._usegradients
:
2659 self
.RefreshSelected()
2662 def SetSecondGradientColour(self
, colour
=None):
2663 """Sets the second gradient colour."""
2666 # No colour given, generate a slightly darker from the
2667 # CustomTreeCtrl background colour
2668 color
= self
.GetBackgroundColour()
2669 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2670 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2671 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2673 self
._secondcolour
= colour
2675 if self
._usegradients
:
2676 self
.RefreshSelected()
2679 def GetFirstGradientColour(self
):
2680 """Returns the first gradient colour."""
2682 return self
._firstcolour
2685 def GetSecondGradientColour(self
):
2686 """Returns the second gradient colour."""
2688 return self
._secondcolour
2691 def EnableSelectionGradient(self
, enable
=True):
2692 """Globally enables/disables drawing of gradient selection."""
2694 self
._usegradients
= enable
2695 self
._vistaselection
= False
2696 self
.RefreshSelected()
2699 def SetGradientStyle(self
, vertical
=0):
2701 Sets the gradient style:
2702 0: horizontal gradient
2703 1: vertical gradient
2706 # 0 = Horizontal, 1 = Vertical
2707 self
._gradientstyle
= vertical
2709 if self
._usegradients
:
2710 self
.RefreshSelected()
2713 def GetGradientStyle(self
):
2715 Returns the gradient style:
2716 0: horizontal gradient
2717 1: vertical gradient
2720 return self
._gradientstyle
2723 def EnableSelectionVista(self
, enable
=True):
2724 """Globally enables/disables drawing of Windows Vista selection."""
2726 self
._usegradients
= False
2727 self
._vistaselection
= enable
2728 self
.RefreshSelected()
2731 def SetBorderPen(self
, pen
):
2733 Sets the pen used to draw the selected item border.
2734 The border pen is not used if the Windows Vista style is applied.
2737 self
._borderPen
= pen
2738 self
.RefreshSelected()
2741 def GetBorderPen(self
):
2743 Returns the pen used to draw the selected item border.
2744 The border pen is not used if the Windows Vista style is applied.
2747 return self
._borderPen
2750 def SetConnectionPen(self
, pen
):
2751 """Sets the pen used to draw the connecting lines between items."""
2753 self
._dottedPen
= pen
2757 def GetConnectionPen(self
):
2758 """Returns the pen used to draw the connecting lines between items."""
2760 return self
._dottedPen
2763 def SetBackgroundImage(self
, image
):
2764 """Sets the CustomTreeCtrl background image (can be none)."""
2766 self
._backgroundImage
= image
2770 def GetBackgroundImage(self
):
2771 """Returns the CustomTreeCtrl background image (can be none)."""
2773 return self
._backgroundImage
2776 def GetItemWindow(self
, item
):
2777 """Returns the window associated to the item (if any)."""
2780 raise "\nERROR: Invalid Item"
2782 return item
.GetWindow()
2785 def GetItemWindowEnabled(self
, item
):
2786 """Returns whether the window associated to the item is enabled."""
2789 raise "\nERROR: Invalid Item"
2791 return item
.GetWindowEnabled()
2794 def SetItemWindowEnabled(self
, item
, enable
=True):
2795 """Enables/disables the window associated to the item."""
2798 raise "\nERROR: Invalid Item"
2800 item
.SetWindowEnabled(enable
)
2803 def GetItemType(self
, item
):
2805 Returns the item type:
2812 raise "\nERROR: Invalid Item"
2814 return item
.GetType()
2816 # -----------------------------------------------------------------------------
2817 # item status inquiries
2818 # -----------------------------------------------------------------------------
2820 def IsVisible(self
, item
):
2821 """Returns whether the item is visible or not."""
2824 raise "\nERROR: Invalid Tree Item. "
2826 # An item is only visible if it's not a descendant of a collapsed item
2827 parent
= item
.GetParent()
2831 if not parent
.IsExpanded():
2834 parent
= parent
.GetParent()
2836 startX
, startY
= self
.GetViewStart()
2837 clientSize
= self
.GetClientSize()
2839 rect
= self
.GetBoundingRect(item
)
2843 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2845 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2847 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2853 def ItemHasChildren(self
, item
):
2854 """Returns whether the item has children or not."""
2857 raise "\nERROR: Invalid Tree Item. "
2859 # consider that the item does have children if it has the "+" button: it
2860 # might not have them (if it had never been expanded yet) but then it
2861 # could have them as well and it's better to err on this side rather than
2862 # disabling some operations which are restricted to the items with
2863 # children for an item which does have them
2864 return item
.HasPlus()
2867 def IsExpanded(self
, item
):
2868 """Returns whether the item is expanded or not."""
2871 raise "\nERROR: Invalid Tree Item. "
2873 return item
.IsExpanded()
2876 def IsSelected(self
, item
):
2877 """Returns whether the item is selected or not."""
2880 raise "\nERROR: Invalid Tree Item. "
2882 return item
.IsSelected()
2885 def IsBold(self
, item
):
2886 """Returns whether the item font is bold or not."""
2889 raise "\nERROR: Invalid Tree Item. "
2891 return item
.IsBold()
2894 def IsItalic(self
, item
):
2895 """Returns whether the item font is italic or not."""
2898 raise "\nERROR: Invalid Tree Item. "
2900 return item
.IsItalic()
2903 # -----------------------------------------------------------------------------
2905 # -----------------------------------------------------------------------------
2907 def GetItemParent(self
, item
):
2908 """Gets the item parent."""
2911 raise "\nERROR: Invalid Tree Item. "
2913 return item
.GetParent()
2916 def GetFirstChild(self
, item
):
2917 """Gets the item first child."""
2920 raise "\nERROR: Invalid Tree Item. "
2923 return self
.GetNextChild(item
, cookie
)
2926 def GetNextChild(self
, item
, cookie
):
2928 Gets the item next child based on the 'cookie' parameter.
2929 This method has no sense if you do not call GetFirstChild() before.
2933 raise "\nERROR: Invalid Tree Item. "
2935 children
= item
.GetChildren()
2937 # it's ok to cast cookie to size_t, we never have indices big enough to
2940 if cookie
< len(children
):
2942 return children
[cookie
], cookie
+1
2946 # there are no more of them
2950 def GetLastChild(self
, item
):
2951 """Gets the item last child."""
2954 raise "\nERROR: Invalid Tree Item. "
2956 children
= item
.GetChildren()
2957 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2960 def GetNextSibling(self
, item
):
2961 """Gets the next sibling of an item."""
2964 raise "\nERROR: Invalid Tree Item. "
2967 parent
= i
.GetParent()
2971 # root item doesn't have any siblings
2974 siblings
= parent
.GetChildren()
2975 index
= siblings
.index(i
)
2978 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
2981 def GetPrevSibling(self
, item
):
2982 """Gets the previous sibling of an item."""
2985 raise "\nERROR: Invalid Tree Item. "
2988 parent
= i
.GetParent()
2992 # root item doesn't have any siblings
2995 siblings
= parent
.GetChildren()
2996 index
= siblings
.index(i
)
2998 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3001 def GetNext(self
, item
):
3002 """Gets the next item. Only for internal use right now."""
3005 raise "\nERROR: Invalid Tree Item. "
3009 # First see if there are any children.
3010 children
= i
.GetChildren()
3011 if len(children
) > 0:
3014 # Try a sibling of this or ancestor instead
3017 while p
and not toFind
:
3018 toFind
= self
.GetNextSibling(p
)
3019 p
= self
.GetItemParent(p
)
3024 def GetFirstVisibleItem(self
):
3025 """Returns the first visible item."""
3027 id = self
.GetRootItem()
3032 if self
.IsVisible(id):
3034 id = self
.GetNext(id)
3039 def GetNextVisible(self
, item
):
3040 """Returns the next visible item."""
3043 raise "\nERROR: Invalid Tree Item. "
3048 id = self
.GetNext(id)
3049 if id and self
.IsVisible(id):
3055 def GetPrevVisible(self
, item
):
3058 raise "\nERROR: Invalid Tree Item. "
3060 raise "\nERROR: Not Implemented"
3065 def ResetTextControl(self
):
3066 """Called by TreeTextCtrl when it marks itself for deletion."""
3068 self
._textCtrl
.Destroy()
3069 self
._textCtrl
= None
3072 def FindItem(self
, idParent
, prefixOrig
):
3073 """Finds the first item starting with the given prefix after the given item."""
3075 # match is case insensitive as this is more convenient to the user: having
3076 # to press Shift-letter to go to the item starting with a capital letter
3077 # would be too bothersome
3078 prefix
= prefixOrig
.lower()
3080 # determine the starting point: we shouldn't take the current item (this
3081 # allows to switch between two items starting with the same letter just by
3082 # pressing it) but we shouldn't jump to the next one if the user is
3083 # continuing to type as otherwise he might easily skip the item he wanted
3086 if len(prefix
) == 1:
3087 id = self
.GetNext(id)
3089 # look for the item starting with the given prefix after it
3090 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3092 id = self
.GetNext(id)
3094 # if we haven't found anything...
3097 # ... wrap to the beginning
3098 id = self
.GetRootItem()
3099 if self
.HasFlag(TR_HIDE_ROOT
):
3100 # can't select virtual root
3101 id = self
.GetNext(id)
3103 # and try all the items (stop when we get to the one we started from)
3104 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3105 id = self
.GetNext(id)
3110 # -----------------------------------------------------------------------------
3112 # -----------------------------------------------------------------------------
3114 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3115 """Actually inserts an item in the tree."""
3117 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3118 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3120 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3121 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3123 if ct_type
< 0 or ct_type
> 2:
3124 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3130 # should we give a warning here?
3131 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3133 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3135 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3138 self
._hasWindows
= True
3139 self
._itemWithWindow
.append(item
)
3141 parent
.Insert(item
, previous
)
3146 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3147 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3150 raise "\nERROR: Tree Can Have Only One Root"
3152 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3153 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3155 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3156 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3158 if ct_type
< 0 or ct_type
> 2:
3159 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3161 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3163 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3166 self
._hasWindows
= True
3167 self
._itemWithWindow
.append(self
._anchor
)
3169 if self
.HasFlag(TR_HIDE_ROOT
):
3171 # if root is hidden, make sure we can navigate
3173 self
._anchor
.SetHasPlus()
3174 self
._anchor
.Expand()
3175 self
.CalculatePositions()
3177 if not self
.HasFlag(TR_MULTIPLE
):
3179 self
._current
= self
._key
_current
= self
._anchor
3180 self
._current
.SetHilight(True)
3185 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3186 """Appends an item as a first child of parent."""
3188 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3189 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3191 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3192 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3194 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3197 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3198 """Auxiliary function to cope with the C++ hideous multifunction."""
3200 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3201 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3203 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3204 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3209 # should we give a warning here?
3210 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3216 index
= parent
.GetChildren().index(idPrevious
)
3218 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3220 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3223 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3224 """Auxiliary function to cope with the C++ hideous multifunction."""
3226 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3227 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3229 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3230 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3235 # should we give a warning here?
3236 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3238 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3241 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3242 """Inserts an item after the given previous."""
3244 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3245 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3247 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3248 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3250 if type(input) == type(1):
3251 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3253 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3256 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3257 """Appends an item as a last child of its parent."""
3259 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3260 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3262 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3263 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3268 # should we give a warning here?
3269 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3271 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3274 def SendDeleteEvent(self
, item
):
3275 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3277 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3279 event
.SetEventObject(self
)
3280 self
.ProcessEvent(event
)
3283 def IsDescendantOf(self
, parent
, item
):
3284 """Checks if the given item is under another one."""
3290 # item is a descendant of parent
3293 item
= item
.GetParent()
3298 # Don't leave edit or selection on a child which is about to disappear
3299 def ChildrenClosing(self
, item
):
3300 """We are about to destroy the item children."""
3302 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3303 self
._textCtrl
.StopEditing()
3305 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3306 self
._key
_current
= None
3308 if self
.IsDescendantOf(item
, self
._select
_me
):
3309 self
._select
_me
= item
3311 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3312 self
._current
.SetHilight(False)
3313 self
._current
= None
3314 self
._select
_me
= item
3317 def DeleteChildren(self
, item
):
3318 """Delete item children."""
3321 raise "\nERROR: Invalid Tree Item. "
3323 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3325 self
.ChildrenClosing(item
)
3326 item
.DeleteChildren(self
)
3329 def Delete(self
, item
):
3330 """Delete an item."""
3333 raise "\nERROR: Invalid Tree Item. "
3335 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3337 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3338 # can't delete the item being edited, cancel editing it first
3339 self
._textCtrl
.StopEditing()
3341 parent
= item
.GetParent()
3343 # don't keep stale pointers around!
3344 if self
.IsDescendantOf(item
, self
._key
_current
):
3346 # Don't silently change the selection:
3347 # do it properly in idle time, so event
3348 # handlers get called.
3350 # self._key_current = parent
3351 self
._key
_current
= None
3353 # self._select_me records whether we need to select
3354 # a different item, in idle time.
3355 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3356 self
._select
_me
= parent
3358 if self
.IsDescendantOf(item
, self
._current
):
3360 # Don't silently change the selection:
3361 # do it properly in idle time, so event
3362 # handlers get called.
3364 # self._current = parent
3365 self
._current
= None
3366 self
._select
_me
= parent
3368 # remove the item from the tree
3371 parent
.GetChildren().remove(item
) # remove by value
3373 else: # deleting the root
3375 # nothing will be left in the tree
3378 # and delete all of its children and the item itself now
3379 item
.DeleteChildren(self
)
3380 self
.SendDeleteEvent(item
)
3382 if item
== self
._select
_me
:
3383 self
._select
_me
= None
3385 # Remove the item with window
3386 if item
in self
._itemWithWindow
:
3387 wnd
= item
.GetWindow()
3391 self
._itemWithWindow
.remove(item
)
3396 def DeleteAllItems(self
):
3397 """Delete all items in the CustomTreeCtrl."""
3400 self
.Delete(self
._anchor
)
3403 def Expand(self
, item
):
3405 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3406 EVT_TREE_ITEM_EXPANDED events.
3410 raise "\nERROR: Invalid Tree Item. "
3412 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3413 raise "\nERROR: Can't Expand An Hidden Root. "
3415 if not item
.HasPlus():
3418 if item
.IsExpanded():
3421 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3423 event
.SetEventObject(self
)
3425 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3426 # cancelled by program
3430 self
.CalculatePositions()
3432 self
.RefreshSubtree(item
)
3434 if self
._hasWindows
:
3435 # We hide the associated window here, we may show it after
3438 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3439 self
.ProcessEvent(event
)
3442 def ExpandAll(self
, item
):
3443 """Expands all the items."""
3446 raise "\nERROR: Invalid Tree Item. "
3448 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= GetRootItem():
3450 if not self
.IsExpanded(item
):
3453 child
, cookie
= self
.GetFirstChild(item
)
3456 self
.ExpandAll(child
)
3457 child
, cookie
= self
.GetNextChild(item
, cookie
)
3460 def Collapse(self
, item
):
3462 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3463 EVT_TREE_ITEM_COLLAPSED events.
3467 raise "\nERROR: Invalid Tree Item. "
3469 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3470 raise "\nERROR: Can't Collapse An Hidden Root. "
3472 if not item
.IsExpanded():
3475 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3477 event
.SetEventObject(self
)
3478 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3479 # cancelled by program
3482 self
.ChildrenClosing(item
)
3485 self
.CalculatePositions()
3486 self
.RefreshSubtree(item
)
3488 if self
._hasWindows
:
3491 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3492 self
.ProcessEvent(event
)
3495 def CollapseAndReset(self
, item
):
3496 """Collapse the given item and deletes its children."""
3499 self
.DeleteChildren(item
)
3502 def Toggle(self
, item
):
3503 """Toggles the item state (collapsed/expanded)."""
3505 if item
.IsExpanded():
3511 def HideWindows(self
):
3512 """Hides the windows associated to the items. Used internally."""
3514 for child
in self
._itemWithWindow
:
3515 if not self
.IsVisible(child
):
3516 wnd
= child
.GetWindow()
3521 """Unselects the current selection."""
3525 self
._current
.SetHilight(False)
3526 self
.RefreshLine(self
._current
)
3528 self
._current
= None
3529 self
._select
_me
= None
3532 def UnselectAllChildren(self
, item
):
3533 """Unselects all the children of the given item."""
3535 if item
.IsSelected():
3537 item
.SetHilight(False)
3538 self
.RefreshLine(item
)
3540 if item
.HasChildren():
3541 for child
in item
.GetChildren():
3542 self
.UnselectAllChildren(child
)
3545 def UnselectAll(self
):
3546 """Unselect all the items."""
3548 rootItem
= self
.GetRootItem()
3550 # the tree might not have the root item at all
3552 self
.UnselectAllChildren(rootItem
)
3555 # Recursive function !
3556 # To stop we must have crt_item<last_item
3558 # Tag all next children, when no more children,
3559 # Move to parent (not to tag)
3560 # Keep going... if we found last_item, we stop.
3562 def TagNextChildren(self
, crt_item
, last_item
, select
):
3563 """Used internally."""
3565 parent
= crt_item
.GetParent()
3567 if parent
== None: # This is root item
3568 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3570 children
= parent
.GetChildren()
3571 index
= children
.index(crt_item
)
3573 count
= len(children
)
3575 for n
in xrange(index
+1, count
):
3576 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3579 return self
.TagNextChildren(parent
, last_item
, select
)
3582 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3583 """Used internally."""
3585 crt_item
.SetHilight(select
)
3586 self
.RefreshLine(crt_item
)
3588 if crt_item
== last_item
:
3591 if crt_item
.HasChildren():
3592 for child
in crt_item
.GetChildren():
3593 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3599 def SelectItemRange(self
, item1
, item2
):
3600 """Selects all the items between item1 and item2."""
3602 self
._select
_me
= None
3604 # item2 is not necessary after item1
3605 # choice first' and 'last' between item1 and item2
3606 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3607 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3609 select
= self
._current
.IsSelected()
3611 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3614 self
.TagNextChildren(first
, last
, select
)
3617 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3618 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3621 raise "\nERROR: Invalid Tree Item. "
3623 self
._select
_me
= None
3625 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3627 # to keep going anyhow !!!
3629 if item
.IsSelected():
3630 return # nothing to do
3631 unselect_others
= True
3632 extended_select
= False
3634 elif unselect_others
and item
.IsSelected():
3636 # selection change if there is more than one item currently selected
3637 if len(self
.GetSelections()) == 1:
3640 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3642 event
._itemOld
= self
._current
3643 event
.SetEventObject(self
)
3644 # TODO : Here we don't send any selection mode yet !
3646 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3649 parent
= self
.GetItemParent(item
)
3651 if not self
.IsExpanded(parent
):
3654 parent
= self
.GetItemParent(parent
)
3659 self
.Unselect() # to speed up thing
3665 if not self
._current
:
3666 self
._current
= self
._key
_current
= self
.GetRootItem()
3668 # don't change the mark (self._current)
3669 self
.SelectItemRange(self
._current
, item
)
3673 select
= True # the default
3675 # Check if we need to toggle hilight (ctrl mode)
3676 if not unselect_others
:
3677 select
= not item
.IsSelected()
3679 self
._current
= self
._key
_current
= item
3680 self
._current
.SetHilight(select
)
3681 self
.RefreshLine(self
._current
)
3683 # This can cause idle processing to select the root
3684 # if no item is selected, so it must be after the
3686 self
.EnsureVisible(item
)
3688 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3689 self
.GetEventHandler().ProcessEvent(event
)
3691 # Handles hypertext items
3692 if self
.IsItemHyperText(item
):
3693 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3695 self
.GetEventHandler().ProcessEvent(event
)
3698 def SelectItem(self
, item
, select
=True):
3699 """Selects/deselects an item."""
3702 raise "\nERROR: Invalid Tree Item. "
3706 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3710 item
.SetHilight(False)
3711 self
.RefreshLine(item
)
3714 def FillArray(self
, item
, array
=[]):
3716 Internal function. Used to populate an array of selected items when
3717 the style TR_MULTIPLE is used.
3723 if item
.IsSelected():
3726 if item
.HasChildren():
3727 for child
in item
.GetChildren():
3728 array
= self
.FillArray(child
, array
)
3733 def GetSelections(self
):
3735 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3736 the TR_MULTIPLE style set.
3740 idRoot
= self
.GetRootItem()
3742 array
= self
.FillArray(idRoot
, array
)
3744 #else: the tree is empty, so no selections
3749 def EnsureVisible(self
, item
):
3750 """Ensure that an item is visible in CustomTreeCtrl."""
3753 raise "\nERROR: Invalid Tree Item. "
3755 # first expand all parent branches
3756 parent
= item
.GetParent()
3758 if self
.HasFlag(TR_HIDE_ROOT
):
3759 while parent
and parent
!= self
._anchor
:
3761 parent
= parent
.GetParent()
3765 parent
= parent
.GetParent()
3770 def ScrollTo(self
, item
):
3771 """Scrolls the specified item into view."""
3776 # We have to call this here because the label in
3777 # question might just have been added and no screen
3778 # update taken place.
3780 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3785 # now scroll to the item
3786 item_y
= item
.GetY()
3787 start_x
, start_y
= self
.GetViewStart()
3788 start_y
*= _PIXELS_PER_UNIT
3790 client_w
, client_h
= self
.GetClientSize()
3794 if item_y
< start_y
+3:
3797 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3798 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3799 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3800 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3801 # Item should appear at top
3802 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3804 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3807 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3808 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3809 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3810 item_y
+= _PIXELS_PER_UNIT
+2
3811 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3812 # Item should appear at bottom
3813 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
)
3816 def OnCompareItems(self
, item1
, item2
):
3818 Returns whether 2 items have the same text.
3819 Override this function in the derived class to change the sort order of the items
3820 in the CustomTreeCtrl. The function should return a negative, zero or positive
3821 value if the first item is less than, equal to or greater than the second one.
3823 The base class version compares items alphabetically.
3826 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3829 def SortChildren(self
, item
):
3831 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3832 You should override that method to change the sort order (the default is ascending
3833 case-sensitive alphabetical order).
3837 raise "\nERROR: Invalid Tree Item. "
3839 children
= item
.GetChildren()
3841 if len(children
) > 1:
3843 children
.sort(self
.OnCompareItems
)
3846 def GetImageList(self
):
3847 """Returns the normal image list."""
3849 return self
._imageListNormal
3852 def GetButtonsImageList(self
):
3853 """Returns the buttons image list (from which application-defined button images are taken)."""
3855 return self
._imageListButtons
3858 def GetStateImageList(self
):
3859 """Returns the state image list (from which application-defined state images are taken)."""
3861 return self
._imageListState
3864 def GetImageListCheck(self
):
3865 """Returns the image list used to build the check/radio buttons."""
3867 return self
._imageListCheck
3870 def CalculateLineHeight(self
):
3871 """Calculates the height of a line."""
3873 dc
= wx
.ClientDC(self
)
3874 self
._lineHeight
= dc
.GetCharHeight()
3876 if self
._imageListNormal
:
3878 # Calculate a self._lineHeight value from the normal Image sizes.
3879 # May be toggle off. Then CustomTreeCtrl will spread when
3880 # necessary (which might look ugly).
3881 n
= self
._imageListNormal
.GetImageCount()
3885 width
, height
= self
._imageListNormal
.GetSize(i
)
3887 if height
> self
._lineHeight
:
3888 self
._lineHeight
= height
3890 if self
._imageListButtons
:
3892 # Calculate a self._lineHeight value from the Button image sizes.
3893 # May be toggle off. Then CustomTreeCtrl will spread when
3894 # necessary (which might look ugly).
3895 n
= self
._imageListButtons
.GetImageCount()
3899 width
, height
= self
._imageListButtons
.GetSize(i
)
3901 if height
> self
._lineHeight
:
3902 self
._lineHeight
= height
3904 if self
._imageListCheck
:
3906 # Calculate a self._lineHeight value from the check/radio image sizes.
3907 # May be toggle off. Then CustomTreeCtrl will spread when
3908 # necessary (which might look ugly).
3909 n
= self
._imageListCheck
.GetImageCount()
3913 width
, height
= self
._imageListCheck
.GetSize(i
)
3915 if height
> self
._lineHeight
:
3916 self
._lineHeight
= height
3918 if self
._lineHeight
< 30:
3919 self
._lineHeight
+= 2 # at least 2 pixels
3921 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3924 def SetImageList(self
, imageList
):
3925 """Sets the normal image list."""
3927 if self
._ownsImageListNormal
:
3928 del self
._imageListNormal
3930 self
._imageListNormal
= imageList
3931 self
._ownsImageListNormal
= False
3933 # Don't do any drawing if we're setting the list to NULL,
3934 # since we may be in the process of deleting the tree control.
3936 self
.CalculateLineHeight()
3938 # We gray out the image list to use the grayed icons with disabled items
3939 self
._grayedImageList
= wx
.ImageList(16, 16, True, 0)
3941 for ii
in xrange(imageList
.GetImageCount()):
3943 bmp
= imageList
.GetBitmap(ii
)
3944 image
= wx
.ImageFromBitmap(bmp
)
3945 image
= GrayOut(image
)
3946 newbmp
= wx
.BitmapFromImage(image
)
3947 self
._grayedImageList
.Add(newbmp
)
3950 def SetStateImageList(self
, imageList
):
3951 """Sets the state image list (from which application-defined state images are taken)."""
3953 if self
._ownsImageListState
:
3954 del self
._imageListState
3956 self
._imageListState
= imageList
3957 self
._ownsImageListState
= False
3960 def SetButtonsImageList(self
, imageList
):
3961 """Sets the buttons image list (from which application-defined button images are taken)."""
3963 if self
._ownsImageListButtons
:
3964 del self
._imageListButtons
3966 self
._imageListButtons
= imageList
3967 self
._ownsImageListButtons
= False
3969 self
.CalculateLineHeight()
3972 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
3973 """Sets the check image list."""
3977 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
3978 self
._imageListCheck
.Add(GetCheckedBitmap())
3979 self
._imageListCheck
.Add(GetNotCheckedBitmap())
3980 self
._imageListCheck
.Add(GetFlaggedBitmap())
3981 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
3985 sizex
, sizey
= imglist
.GetSize(0)
3986 self
._imageListCheck
= imglist
3988 # We gray out the image list to use the grayed icons with disabled items
3989 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
3991 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
3993 bmp
= self
._imageListCheck
.GetBitmap(ii
)
3994 image
= wx
.ImageFromBitmap(bmp
)
3995 image
= GrayOut(image
)
3996 newbmp
= wx
.BitmapFromImage(image
)
3997 self
._grayedCheckList
.Add(newbmp
)
4002 self
.CalculateLineHeight()
4005 def AssignImageList(self
, imageList
):
4006 """Assigns the normal image list."""
4008 self
.SetImageList(imageList
)
4009 self
._ownsImageListNormal
= True
4012 def AssignStateImageList(self
, imageList
):
4013 """Assigns the state image list."""
4015 self
.SetStateImageList(imageList
)
4016 self
._ownsImageListState
= True
4019 def AssignButtonsImageList(self
, imageList
):
4020 """Assigns the button image list."""
4022 self
.SetButtonsImageList(imageList
)
4023 self
._ownsImageListButtons
= True
4026 # -----------------------------------------------------------------------------
4028 # -----------------------------------------------------------------------------
4030 def AdjustMyScrollbars(self
):
4031 """Adjust the wx.ScrolledWindow scrollbars."""
4035 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4036 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4037 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4038 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4039 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4040 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4044 self
.SetScrollbars(0, 0, 0, 0)
4047 def GetLineHeight(self
, item
):
4048 """Returns the line height for the given item."""
4050 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4051 return item
.GetHeight()
4053 return self
._lineHeight
4056 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4057 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4059 dc
.DrawRectangleRect(rect
)
4060 border
= self
._borderPen
.GetWidth()
4062 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4064 # calculate gradient coefficients
4066 col2
= self
._secondcolour
4067 col1
= self
._firstcolour
4069 col2
= self
._hilightUnfocusedBrush
.GetColour()
4070 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4072 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4073 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4075 flrect
= float(rect
.height
)
4077 rstep
= float((r2
- r1
)) / flrect
4078 gstep
= float((g2
- g1
)) / flrect
4079 bstep
= float((b2
- b1
)) / flrect
4081 rf
, gf
, bf
= 0, 0, 0
4083 for y
in xrange(rect
.y
+border
, rect
.y
+ rect
.height
-border
):
4084 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4085 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4086 dc
.DrawRectangle(rect
.x
+border
, y
, rect
.width
-2*border
, 1)
4092 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4093 """Gradient fill from colour 1 to colour 2 from left to right."""
4095 dc
.DrawRectangleRect(rect
)
4096 border
= self
._borderPen
.GetWidth()
4098 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4100 # calculate gradient coefficients
4103 col2
= self
._secondcolour
4104 col1
= self
._firstcolour
4106 col2
= self
._hilightUnfocusedBrush
.GetColour()
4107 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4109 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4110 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4112 flrect
= float(rect
.width
)
4114 rstep
= float((r2
- r1
)) / flrect
4115 gstep
= float((g2
- g1
)) / flrect
4116 bstep
= float((b2
- b1
)) / flrect
4118 rf
, gf
, bf
= 0, 0, 0
4120 for x
in xrange(rect
.x
+border
, rect
.x
+ rect
.width
-border
):
4121 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4122 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4123 dc
.DrawRectangle(x
, rect
.y
+border
, 1, rect
.height
-2*border
)
4129 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4130 """Draw the selected item(s) with the Windows Vista style."""
4134 outer
= _rgbSelectOuter
4135 inner
= _rgbSelectInner
4137 bottom
= _rgbSelectBottom
4141 outer
= _rgbNoFocusOuter
4142 inner
= _rgbNoFocusInner
4143 top
= _rgbNoFocusTop
4144 bottom
= _rgbNoFocusBottom
4146 oldpen
= dc
.GetPen()
4147 oldbrush
= dc
.GetBrush()
4149 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4150 dc
.SetPen(wx
.Pen(outer
))
4151 dc
.DrawRoundedRectangleRect(rect
, 3)
4153 dc
.SetPen(wx
.Pen(inner
))
4154 dc
.DrawRoundedRectangleRect(rect
, 2)
4157 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4158 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4160 flrect
= float(rect
.height
)
4162 rstep
= float((r2
- r1
)) / flrect
4163 gstep
= float((g2
- g1
)) / flrect
4164 bstep
= float((b2
- b1
)) / flrect
4166 rf
, gf
, bf
= 0, 0, 0
4167 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4169 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4170 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4171 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4172 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4178 dc
.SetBrush(oldbrush
)
4181 def PaintItem(self
, item
, dc
):
4182 """Actually paint an item."""
4184 attr
= item
.GetAttributes()
4186 if attr
and attr
.HasFont():
4187 dc
.SetFont(attr
.GetFont())
4189 dc
.SetFont(self
._boldFont
)
4190 if item
.IsHyperText():
4191 dc
.SetFont(self
.GetHyperTextFont())
4192 if item
.GetVisited():
4193 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4195 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4197 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4199 image
= item
.GetCurrentImage()
4200 checkimage
= item
.GetCurrentCheckedImage()
4201 image_w
, image_h
= 0, 0
4203 if image
!= _NO_IMAGE
:
4205 if self
._imageListNormal
:
4207 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4214 if item
.GetType() != 0:
4215 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4218 wcheck
, hcheck
= 0, 0
4220 total_h
= self
.GetLineHeight(item
)
4221 drawItemBackground
= False
4223 if item
.IsSelected():
4225 # under mac selections are only a rectangle in case they don't have the focus
4226 if wx
.Platform
== "__WXMAC__":
4227 if not self
._hasFocus
:
4228 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4229 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4231 dc
.SetBrush(self
._hilightBrush
)
4233 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4234 drawItemBackground
= True
4236 if attr
and attr
.HasBackgroundColour():
4237 drawItemBackground
= True
4238 colBg
= attr
.GetBackgroundColour()
4240 colBg
= self
._backgroundColour
4242 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4243 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4245 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4247 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4249 oldpen
= dc
.GetPen()
4250 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4252 w
, h
= self
.GetSize()
4254 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4256 if item
.IsSelected():
4257 if self
._usegradients
:
4258 if self
._gradientstyle
== 0: # Horizontal
4259 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4261 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4262 elif self
._vistaselection
:
4263 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4265 dc
.DrawRectangleRect(itemrect
)
4271 if item
.IsSelected() and image
!= _NO_IMAGE
:
4273 # If it's selected, and there's an image, then we should
4274 # take care to leave the area under the image painted in the
4275 # background colour.
4277 wnd
= item
.GetWindow()
4280 wndx
, wndy
= item
.GetWindowSize()
4282 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2, item
.GetY()+offset
,
4283 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
, total_h
-offset
)
4285 if self
._usegradients
:
4286 if self
._gradientstyle
== 0: # Horizontal
4287 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4289 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4290 elif self
._vistaselection
:
4291 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4293 dc
.DrawRectangleRect(itemrect
)
4295 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4296 # don't allow backgrounds to be customized. Not drawing the background,
4297 # except for custom item backgrounds, works for both kinds of theme.
4298 elif drawItemBackground
:
4300 minusicon
= wcheck
+ image_w
- 2
4301 itemrect
= wx
.Rect(item
.GetX()+minusicon
, item
.GetY()+offset
, item
.GetWidth()-minusicon
, total_h
-offset
)
4303 if self
._usegradients
and self
._hasFocus
:
4304 if self
._gradientstyle
== 0: # Horizontal
4305 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4307 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4309 dc
.DrawRectangleRect(itemrect
)
4311 if image
!= _NO_IMAGE
:
4313 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4314 if item
.IsEnabled():
4315 imglist
= self
._imageListNormal
4317 imglist
= self
._grayedImageList
4319 imglist
.Draw(image
, dc
,
4320 item
.GetX() + wcheck
,
4321 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4322 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4324 dc
.DestroyClippingRegion()
4327 if item
.IsEnabled():
4328 imglist
= self
._imageListCheck
4330 imglist
= self
._grayedCheckList
4332 imglist
.Draw(checkimage
, dc
,
4334 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4335 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4337 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4338 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4340 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4342 if not item
.IsEnabled():
4343 foreground
= dc
.GetTextForeground()
4344 dc
.SetTextForeground(self
._disabledColour
)
4345 dc
.DrawLabel(item
.GetText(), textrect
)
4346 dc
.SetTextForeground(foreground
)
4348 dc
.DrawLabel(item
.GetText(), textrect
)
4350 wnd
= item
.GetWindow()
4352 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4353 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4354 if not wnd
.IsShown():
4356 if wnd
.GetPosition() != (wndx
, ya
):
4357 wnd
.SetPosition((wndx
, ya
))
4359 # restore normal font
4360 dc
.SetFont(self
._normalFont
)
4363 # Now y stands for the top of the item, whereas it used to stand for middle !
4364 def PaintLevel(self
, item
, dc
, level
, y
):
4365 """Paint a level of CustomTreeCtrl."""
4367 x
= level
*self
._indent
4369 if not self
.HasFlag(TR_HIDE_ROOT
):
4375 # always expand hidden root
4377 children
= item
.GetChildren()
4378 count
= len(children
)
4384 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4387 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4389 # draw line down to last child
4390 origY
+= self
.GetLineHeight(children
[0])>>1
4391 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4392 dc
.DrawLine(3, origY
, 3, oldY
)
4396 item
.SetX(x
+self
._spacing
)
4399 h
= self
.GetLineHeight(item
)
4401 y_mid
= y_top
+ (h
>>1)
4404 exposed_x
= dc
.LogicalToDeviceX(0)
4405 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4407 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4408 if wx
.Platform
== "__WXMAC__":
4409 # don't draw rect outline if we already have the
4410 # background color under Mac
4411 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4413 pen
= self
._borderPen
4415 if item
.IsSelected():
4416 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4417 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4419 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4421 attr
= item
.GetAttributes()
4422 if attr
and attr
.HasTextColour():
4423 colText
= attr
.GetTextColour()
4425 colText
= self
.GetForegroundColour()
4427 if self
._vistaselection
:
4431 dc
.SetTextForeground(colText
)
4436 self
.PaintItem(item
, dc
)
4438 if self
.HasFlag(TR_ROW_LINES
):
4440 # if the background colour is white, choose a
4441 # contrasting color for the lines
4442 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4443 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4444 dc
.DrawLine(0, y_top
, 10000, y_top
)
4445 dc
.DrawLine(0, y
, 10000, y
)
4447 # restore DC objects
4448 dc
.SetBrush(wx
.WHITE_BRUSH
)
4449 dc
.SetTextForeground(wx
.BLACK
)
4451 if not self
.HasFlag(TR_NO_LINES
):
4453 # draw the horizontal line here
4454 dc
.SetPen(self
._dottedPen
)
4456 if x
> self
._indent
:
4457 x_start
-= self
._indent
4458 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4460 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4463 # should the item show a button?
4464 if item
.HasPlus() and self
.HasButtons():
4466 if self
._imageListButtons
:
4468 # draw the image button here
4471 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4472 if item
.IsSelected():
4473 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4475 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4477 yy
= y_mid
- image_h
/2
4479 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4480 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4481 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4482 dc
.DestroyClippingRegion()
4484 else: # no custom buttons
4486 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4487 # We draw something like the Mac twist buttons
4489 dc
.SetPen(wx
.BLACK_PEN
)
4490 dc
.SetBrush(self
._hilightBrush
)
4491 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4493 if item
.IsExpanded():
4495 button
[0].y
= y_mid
- 3
4497 button
[1].y
= button
[0].y
4499 button
[2].y
= button
[0].y
+ 6
4502 button
[0].y
= y_mid
- 5
4503 button
[1].x
= button
[0].x
4504 button
[1].y
= y_mid
+ 5
4505 button
[2].x
= button
[0].x
+ 5
4508 dc
.DrawPolygon(button
)
4511 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4518 if item
.IsExpanded():
4519 flag |
= _CONTROL_EXPANDED
4520 if item
== self
._underMouse
:
4521 flag |
= _CONTROL_CURRENT
4523 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4525 if item
.IsExpanded():
4527 children
= item
.GetChildren()
4528 count
= len(children
)
4537 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4540 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4542 # draw line down to last child
4543 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4544 if self
.HasButtons():
4547 # Only draw the portion of the line that is visible, in case it is huge
4548 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4549 yOrigin
= abs(yOrigin
)
4550 width
, height
= self
.GetClientSize()
4552 # Move end points to the begining/end of the view?
4555 if oldY
> yOrigin
+ height
:
4556 oldY
= yOrigin
+ height
4558 # after the adjustments if y_mid is larger than oldY then the line
4559 # isn't visible at all so don't draw anything
4561 dc
.SetPen(self
._dottedPen
)
4562 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4567 # -----------------------------------------------------------------------------
4568 # wxWidgets callbacks
4569 # -----------------------------------------------------------------------------
4571 def OnPaint(self
, event
):
4572 """Handles the wx.EVT_PAINT event."""
4574 dc
= wx
.PaintDC(self
)
4577 if not self
._anchor
:
4580 dc
.SetFont(self
._normalFont
)
4581 dc
.SetPen(self
._dottedPen
)
4584 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4587 def OnEraseBackground(self
, event
):
4588 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4590 # Can we actually do something here (or in OnPaint()) To Handle
4591 # background images that are stretchable or always centered?
4592 # I tried but I get enormous flickering...
4594 if not self
._backgroundImage
:
4598 if self
._imageStretchStyle
== _StyleTile
:
4602 dc
= wx
.ClientDC(self
)
4603 rect
= self
.GetUpdateRegion().GetBox()
4604 dc
.SetClippingRect(rect
)
4606 self
.TileBackground(dc
)
4609 def TileBackground(self
, dc
):
4610 """Tiles the background image to fill all the available area."""
4612 sz
= self
.GetClientSize()
4613 w
= self
._backgroundImage
.GetWidth()
4614 h
= self
._backgroundImage
.GetHeight()
4621 while y
< sz
.height
:
4622 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4628 def OnSetFocus(self
, event
):
4629 """Handles the wx.EVT_SET_FOCUS event."""
4631 self
._hasFocus
= True
4632 self
.RefreshSelected()
4636 def OnKillFocus(self
, event
):
4637 """Handles the wx.EVT_KILL_FOCUS event."""
4639 self
._hasFocus
= False
4640 self
.RefreshSelected()
4644 def OnKeyDown(self
, event
):
4645 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4647 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4649 te
.SetEventObject(self
)
4651 if self
.GetEventHandler().ProcessEvent(te
):
4652 # intercepted by the user code
4655 if self
._current
is None or self
._key
_current
is None:
4660 # how should the selection work for this event?
4661 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4665 # * : Expand all/Collapse all
4666 # ' ' | return : activate
4667 # up : go up (not last children!)
4669 # left : go to parent
4670 # right : open if parent and go next
4672 # end : go to last item without opening parents
4673 # alnum : start or continue searching for the item with this prefix
4675 keyCode
= event
.GetKeyCode()
4677 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4678 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4679 self
.Expand(self
._current
)
4681 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4682 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4684 self
.ExpandAll(self
._current
)
4686 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4687 if self
.IsExpanded(self
._current
):
4688 self
.Collapse(self
._current
)
4690 elif keyCode
== wx
.WXK_MENU
:
4691 # Use the item's bounding rectangle to determine position for the event
4692 itemRect
= self
.GetBoundingRect(self
._current
, True)
4693 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4694 event
._item
= self
._current
4695 # Use the left edge, vertical middle
4696 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4697 event
.SetEventObject(self
)
4698 self
.GetEventHandler().ProcessEvent(event
)
4700 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4702 if not self
.IsEnabled(self
._current
):
4706 if not event
.HasModifiers():
4707 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4708 event
._item
= self
._current
4709 event
.SetEventObject(self
)
4710 self
.GetEventHandler().ProcessEvent(event
)
4712 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4713 checked
= not self
.IsItemChecked(self
._current
)
4714 self
.CheckItem(self
._current
, checked
)
4716 # in any case, also generate the normal key event for this key,
4717 # even if we generated the ACTIVATED event above: this is what
4718 # wxMSW does and it makes sense because you might not want to
4719 # process ACTIVATED event at all and handle Space and Return
4720 # directly (and differently) which would be impossible otherwise
4723 # up goes to the previous sibling or to the last
4724 # of its children if it's expanded
4725 elif keyCode
== wx
.WXK_UP
:
4726 prev
= self
.GetPrevSibling(self
._key
_current
)
4728 prev
= self
.GetItemParent(self
._key
_current
)
4729 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4733 current
= self
._key
_current
4734 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4735 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4736 # otherwise we return to where we came from
4737 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4738 self
._key
_current
= prev
4741 current
= self
._key
_current
4743 # We are going to another parent node
4744 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4745 child
= self
.GetLastChild(prev
)
4750 # Try to get the previous siblings and see if they are active
4751 while prev
and not self
.IsEnabled(prev
):
4752 prev
= self
.GetPrevSibling(prev
)
4755 # No previous siblings active: go to the parent and up
4756 prev
= self
.GetItemParent(current
)
4757 while prev
and not self
.IsEnabled(prev
):
4758 prev
= self
.GetItemParent(prev
)
4761 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4762 self
._key
_current
= prev
4764 # left arrow goes to the parent
4765 elif keyCode
== wx
.WXK_LEFT
:
4767 prev
= self
.GetItemParent(self
._current
)
4768 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4769 # don't go to root if it is hidden
4770 prev
= self
.GetPrevSibling(self
._current
)
4772 if self
.IsExpanded(self
._current
):
4773 self
.Collapse(self
._current
)
4775 if prev
and self
.IsEnabled(prev
):
4776 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4778 elif keyCode
== wx
.WXK_RIGHT
:
4779 # this works the same as the down arrow except that we
4780 # also expand the item if it wasn't expanded yet
4781 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4782 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4783 if self
.IsEnabled(child
):
4784 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4785 self
._key
_current
= child
4787 self
.Expand(self
._current
)
4790 elif keyCode
== wx
.WXK_DOWN
:
4791 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4793 child
= self
.GetNextActiveItem(self
._key
_current
)
4796 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4797 self
._key
_current
= child
4801 next
= self
.GetNextSibling(self
._key
_current
)
4804 current
= self
._key
_current
4805 while current
and not next
:
4806 current
= self
.GetItemParent(current
)
4808 next
= self
.GetNextSibling(current
)
4809 if not self
.IsEnabled(next
):
4813 while next
and not self
.IsEnabled(next
):
4814 next
= self
.GetNext(next
)
4817 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4818 self
._key
_current
= next
4821 # <End> selects the last visible tree item
4822 elif keyCode
== wx
.WXK_END
:
4824 last
= self
.GetRootItem()
4826 while last
and self
.IsExpanded(last
):
4828 lastChild
= self
.GetLastChild(last
)
4830 # it may happen if the item was expanded but then all of
4831 # its children have been deleted - so IsExpanded() returned
4832 # true, but GetLastChild() returned invalid item
4838 if last
and self
.IsEnabled(last
):
4840 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4842 # <Home> selects the root item
4843 elif keyCode
== wx
.WXK_HOME
:
4845 prev
= self
.GetRootItem()
4850 if self
.HasFlag(TR_HIDE_ROOT
):
4851 prev
, cookie
= self
.GetFirstChild(prev
)
4855 if self
.IsEnabled(prev
):
4856 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4860 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4861 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4862 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4864 # find the next item starting with the given prefix
4866 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4872 if self
.IsEnabled(id):
4874 self
._findPrefix
+= ch
4876 # also start the timer to reset the current prefix if the user
4877 # doesn't press any more alnum keys soon -- we wouldn't want
4878 # to use this prefix for a new item search
4879 if not self
._findTimer
:
4880 self
._findTimer
= TreeFindTimer(self
)
4882 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4889 def GetNextActiveItem(self
, item
, down
=True):
4890 """Returns the next active item. Used Internally at present. """
4893 sibling
= self
.GetNextSibling
4895 sibling
= self
.GetPrevSibling
4897 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4898 # Is an unchecked radiobutton... all its children are inactive
4899 # try to get the next/previous sibling
4903 child
= sibling(item
)
4904 if (child
and self
.IsEnabled(child
)) or not child
:
4909 # Tha's not a radiobutton... but some of its children can be
4911 child
, cookie
= self
.GetFirstChild(item
)
4912 while child
and not self
.IsEnabled(child
):
4913 child
, cookie
= self
.GetNextChild(item
, cookie
)
4915 if child
and self
.IsEnabled(child
):
4921 def HitTest(self
, point
, flags
=0):
4923 Calculates which (if any) item is under the given point, returning the tree item
4924 at this point plus extra information flags. Flags is a bitlist of the following:
4926 TREE_HITTEST_ABOVE above the client area
4927 TREE_HITTEST_BELOW below the client area
4928 TREE_HITTEST_NOWHERE no item has been hit
4929 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4930 TREE_HITTEST_ONITEMICON on the icon associated to an item
4931 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4932 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4933 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4934 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4935 TREE_HITTEST_TOLEFT on the left of the client area
4936 TREE_HITTEST_TORIGHT on the right of the client area
4937 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4938 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4939 TREE_HITTEST_ONITEM anywhere on the item
4941 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4944 w
, h
= self
.GetSize()
4948 flags |
= TREE_HITTEST_TOLEFT
4950 flags |
= TREE_HITTEST_TORIGHT
4952 flags |
= TREE_HITTEST_ABOVE
4954 flags |
= TREE_HITTEST_BELOW
4959 if self
._anchor
== None:
4960 flags
= TREE_HITTEST_NOWHERE
4963 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
4966 flags
= TREE_HITTEST_NOWHERE
4969 if not self
.IsEnabled(hit
):
4975 def GetBoundingRect(self
, item
, textOnly
=False):
4976 """Gets the bounding rectangle of the item."""
4979 raise "\nERROR: Invalid Tree Item. "
4983 startX
, startY
= self
.GetViewStart()
4986 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
4987 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
4988 rect
.width
= i
.GetWidth()
4989 rect
.height
= self
.GetLineHeight(i
)
4994 def Edit(self
, item
):
4996 Internal function. Starts the editing of an item label, sending a
4997 EVT_TREE_BEGIN_LABEL_EDIT event.
5000 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5002 te
.SetEventObject(self
)
5003 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5007 # We have to call this here because the label in
5008 # question might just have been added and no screen
5009 # update taken place.
5011 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5016 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5017 self
._textCtrl
.StopEditing()
5019 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5020 self
._textCtrl
.SetFocus()
5023 def GetEditControl(self
):
5025 Returns a pointer to the edit TextCtrl if the item is being edited or
5026 None otherwise (it is assumed that no more than one item may be edited
5030 return self
._textCtrl
5033 def OnRenameAccept(self
, item
, value
):
5035 Called by TreeTextCtrl, to accept the changes and to send the
5036 EVT_TREE_END_LABEL_EDIT event.
5039 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5041 le
.SetEventObject(self
)
5043 le
._editCancelled
= False
5045 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5048 def OnRenameCancelled(self
, item
):
5050 Called by TreeTextCtrl, to cancel the changes and to send the
5051 EVT_TREE_END_LABEL_EDIT event.
5054 # let owner know that the edit was cancelled
5055 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5057 le
.SetEventObject(self
)
5059 le
._editCancelled
= True
5061 self
.GetEventHandler().ProcessEvent(le
)
5064 def OnRenameTimer(self
):
5065 """The timer for renaming has expired. Start editing."""
5067 self
.Edit(self
._current
)
5070 def OnMouse(self
, event
):
5071 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5073 if not self
._anchor
:
5076 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5078 # Is the mouse over a tree item button?
5080 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5081 underMouse
= thisItem
5082 underMouseChanged
= underMouse
!= self
._underMouse
5084 if underMouse
and (flags
& TREE_HITTEST_ONITEMBUTTON
) and not event
.LeftIsDown() and \
5085 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5086 underMouse
= underMouse
5090 if underMouse
!= self
._underMouse
:
5091 if self
._underMouse
:
5092 # unhighlight old item
5093 self
._underMouse
= None
5095 self
._underMouse
= underMouse
5097 # Determines what item we are hovering over and need a tooltip for
5098 hoverItem
= thisItem
5100 # We do not want a tooltip if we are dragging, or if the rename timer is running
5101 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5103 if hoverItem
is not None:
5104 # Ask the tree control what tooltip (if any) should be shown
5105 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5106 hevent
._item
= hoverItem
5107 hevent
.SetEventObject(self
)
5109 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5110 self
.SetToolTip(hevent
._label
)
5112 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5113 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5114 self
._isonhyperlink
= True
5116 if self
._isonhyperlink
:
5117 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5118 self
._isonhyperlink
= False
5120 # we process left mouse up event (enables in-place edit), right down
5121 # (pass to the user code), left dbl click (activate item) and
5122 # dragging/moving events for items drag-and-drop
5124 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5125 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5131 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5133 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5135 if self
._dragCount
== 0:
5136 self
._dragStart
= pt
5139 self
._dragCount
= self
._dragCount
+ 1
5141 if self
._dragCount
!= 3:
5142 # wait until user drags a bit further...
5145 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5147 nevent
= TreeEvent(command
, self
.GetId())
5148 nevent
._item
= self
._current
5149 nevent
.SetEventObject(self
)
5150 newpt
= self
.CalcScrolledPosition(pt
)
5151 nevent
.SetPoint(newpt
)
5153 # by default the dragging is not supported, the user code must
5154 # explicitly allow the event for it to take place
5157 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5159 # we're going to drag this item
5160 self
._isDragging
= True
5162 # remember the old cursor because we will change it while
5164 self
._oldCursor
= self
._cursor
5166 # in a single selection control, hide the selection temporarily
5167 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5168 self
._oldSelection
= self
.GetSelection()
5170 if self
._oldSelection
:
5172 self
._oldSelection
.SetHilight(False)
5173 self
.RefreshLine(self
._oldSelection
)
5175 selections
= self
.GetSelections()
5176 if len(selections
) == 1:
5177 self
._oldSelection
= selections
[0]
5178 self
._oldSelection
.SetHilight(False)
5179 self
.RefreshLine(self
._oldSelection
)
5184 # Create the custom draw image from the icons and the text of the item
5185 self
._dragImage
= DragImage(self
, self
._current
)
5186 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5187 self
._dragImage
.Show()
5188 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5190 elif event
.Dragging() and self
._isDragging
:
5192 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5194 if self
._countDrag
== 0 and item
:
5195 self
._oldItem
= item
5197 if item
!= self
._dropTarget
:
5199 # unhighlight the previous drop target
5200 if self
._dropTarget
:
5201 self
._dropTarget
.SetHilight(False)
5202 self
.RefreshLine(self
._dropTarget
)
5204 item
.SetHilight(True)
5205 self
.RefreshLine(item
)
5206 self
._countDrag
= self
._countDrag
+ 1
5207 self
._dropTarget
= item
5211 if self
._countDrag
>= 3:
5212 # Here I am trying to avoid ugly repainting problems... hope it works
5213 self
.RefreshLine(self
._oldItem
)
5216 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5219 self
._dragImage
.EndDrag()
5221 if self
._dropTarget
:
5222 self
._dropTarget
.SetHilight(False)
5224 if self
._oldSelection
:
5226 self
._oldSelection
.SetHilight(True)
5227 self
.RefreshLine(self
._oldSelection
)
5228 self
._oldSelection
= None
5230 # generate the drag end event
5231 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5233 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5234 event
.SetEventObject(self
)
5236 self
.GetEventHandler().ProcessEvent(event
)
5238 self
._isDragging
= False
5239 self
._dropTarget
= None
5241 self
.SetCursor(self
._oldCursor
)
5243 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5246 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5251 # If we got to this point, we are not dragging or moving the mouse.
5252 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5253 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5254 # We skip even if we didn't hit an item because we still should
5255 # restore focus to the tree control even if we didn't exactly hit an item.
5256 if event
.LeftDown():
5257 self
._hasFocus
= True
5258 self
.SetFocusIgnoringChildren()
5261 # here we process only the messages which happen on tree items
5266 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5267 self
._textCtrl
.StopEditing()
5268 return # we hit the blank area
5270 if event
.RightDown():
5272 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5273 self
._textCtrl
.StopEditing()
5275 self
._hasFocus
= True
5276 self
.SetFocusIgnoringChildren()
5278 # If the item is already selected, do not update the selection.
5279 # Multi-selections should not be cleared if a selected item is clicked.
5280 if not self
.IsSelected(item
):
5282 self
.DoSelectItem(item
, True, False)
5284 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5286 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5287 nevent
.SetEventObject(self
)
5288 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5290 # Consistent with MSW (for now), send the ITEM_MENU *after*
5291 # the RIGHT_CLICK event. TODO: This behaviour may change.
5292 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5293 nevent2
._item
= item
5294 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5295 nevent2
.SetEventObject(self
)
5296 self
.GetEventHandler().ProcessEvent(nevent2
)
5298 elif event
.LeftUp():
5300 # this facilitates multiple-item drag-and-drop
5302 if self
.HasFlag(TR_MULTIPLE
):
5304 selections
= self
.GetSelections()
5306 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5308 self
.DoSelectItem(item
, True, False)
5310 if self
._lastOnSame
:
5312 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5314 if self
._renameTimer
:
5316 if self
._renameTimer
.IsRunning():
5318 self
._renameTimer
.Stop()
5322 self
._renameTimer
= TreeRenameTimer(self
)
5324 self
._renameTimer
.Start(_DELAY
, True)
5326 self
._lastOnSame
= False
5329 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5331 if not item
or not item
.IsEnabled():
5332 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5333 self
._textCtrl
.StopEditing()
5336 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5337 self
._textCtrl
.StopEditing()
5339 self
._hasFocus
= True
5340 self
.SetFocusIgnoringChildren()
5342 if event
.LeftDown():
5344 self
._lastOnSame
= item
== self
._current
5346 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5348 # only toggle the item for a single click, double click on
5349 # the button doesn't do anything (it toggles the item twice)
5350 if event
.LeftDown():
5354 # don't select the item if the button was clicked
5357 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5359 if event
.LeftDown():
5361 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5365 # clear the previously selected items, if the
5366 # user clicked outside of the present selection.
5367 # otherwise, perform the deselection on mouse-up.
5368 # this allows multiple drag and drop to work.
5369 # but if Cmd is down, toggle selection of the clicked item
5370 if not self
.IsSelected(item
) or event
.CmdDown():
5372 if flags
& TREE_HITTEST_ONITEM
:
5373 # how should the selection work for this event?
5374 if item
.IsHyperText():
5375 self
.SetItemVisited(item
, True)
5377 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5381 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5383 # For some reason, Windows isn't recognizing a left double-click,
5384 # so we need to simulate it here. Allow 200 milliseconds for now.
5385 if event
.LeftDClick():
5387 # double clicking should not start editing the item label
5388 if self
._renameTimer
:
5389 self
._renameTimer
.Stop()
5391 self
._lastOnSame
= False
5393 # send activate event first
5394 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5396 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5397 nevent
.SetEventObject(self
)
5398 if not self
.GetEventHandler().ProcessEvent(nevent
):
5400 # if the user code didn't process the activate event,
5401 # handle it ourselves by toggling the item when it is
5403 ## if item.HasPlus():
5407 def OnInternalIdle(self
, event
):
5408 """Performs operations in idle time (essentially drawing)."""
5410 # Check if we need to select the root item
5411 # because nothing else has been selected.
5412 # Delaying it means that we can invoke event handlers
5413 # as required, when a first item is selected.
5414 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5417 self
.SelectItem(self
._select
_me
)
5418 elif self
.GetRootItem():
5419 self
.SelectItem(self
.GetRootItem())
5421 # after all changes have been done to the tree control,
5422 # we actually redraw the tree when everything is over
5426 if self
._freezeCount
:
5431 self
.CalculatePositions()
5433 self
.AdjustMyScrollbars()
5438 def CalculateSize(self
, item
, dc
):
5439 """Calculates overall position and size of an item."""
5441 attr
= item
.GetAttributes()
5443 if attr
and attr
.HasFont():
5444 dc
.SetFont(attr
.GetFont())
5446 dc
.SetFont(self
._boldFont
)
5448 dc
.SetFont(self
._normalFont
)
5450 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5453 # restore normal font
5454 dc
.SetFont(self
._normalFont
)
5456 image_w
, image_h
= 0, 0
5457 image
= item
.GetCurrentImage()
5459 if image
!= _NO_IMAGE
:
5461 if self
._imageListNormal
:
5463 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5466 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5468 checkimage
= item
.GetCurrentCheckedImage()
5469 if checkimage
is not None:
5470 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5476 total_h
+= 2 # at least 2 pixels
5478 total_h
+= total_h
/10 # otherwise 10% extra spacing
5480 if total_h
> self
._lineHeight
:
5481 self
._lineHeight
= total_h
5483 if not item
.GetWindow():
5484 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5485 item
.SetHeight(total_h
)
5487 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5490 def CalculateLevel(self
, item
, dc
, level
, y
):
5491 """Calculates the level of an item."""
5493 x
= level
*self
._indent
5495 if not self
.HasFlag(TR_HIDE_ROOT
):
5501 # a hidden root is not evaluated, but its
5502 # children are always calculated
5503 children
= item
.GetChildren()
5504 count
= len(children
)
5506 for n
in xrange(count
):
5507 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5511 self
.CalculateSize(item
, dc
)
5514 item
.SetX(x
+self
._spacing
)
5516 y
+= self
.GetLineHeight(item
)
5518 if not item
.IsExpanded():
5519 # we don't need to calculate collapsed branches
5522 children
= item
.GetChildren()
5523 count
= len(children
)
5525 for n
in xrange(count
):
5526 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5531 def CalculatePositions(self
):
5532 """Calculates all the positions of the visible items."""
5534 if not self
._anchor
:
5537 dc
= wx
.ClientDC(self
)
5540 dc
.SetFont(self
._normalFont
)
5541 dc
.SetPen(self
._dottedPen
)
5543 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5546 def RefreshSubtree(self
, item
):
5547 """Refreshes a damaged subtree of an item."""
5551 if self
._freezeCount
:
5554 client
= self
.GetClientSize()
5557 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5558 rect
.width
= client
.x
5559 rect
.height
= client
.y
5561 self
.Refresh(True, rect
)
5562 self
.AdjustMyScrollbars()
5565 def RefreshLine(self
, item
):
5566 """Refreshes a damaged item line."""
5570 if self
._freezeCount
:
5574 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5575 rect
.width
= self
.GetClientSize().x
5576 rect
.height
= self
.GetLineHeight(item
)
5578 self
.Refresh(True, rect
)
5581 def RefreshSelected(self
):
5582 """Refreshes a damaged selected item line."""
5584 if self
._freezeCount
:
5587 # TODO: this is awfully inefficient, we should keep the list of all
5588 # selected items internally, should be much faster
5590 self
.RefreshSelectedUnder(self
._anchor
)
5593 def RefreshSelectedUnder(self
, item
):
5594 """Refreshes the selected items under the given item."""
5596 if self
._freezeCount
:
5599 if item
.IsSelected():
5600 self
.RefreshLine(item
)
5602 children
= item
.GetChildren()
5603 for child
in children
:
5604 self
.RefreshSelectedUnder(child
)
5608 """Freeze CustomTreeCtrl."""
5610 self
._freezeCount
= self
._freezeCount
+ 1
5614 """Thaw CustomTreeCtrl."""
5616 if self
._freezeCount
== 0:
5617 raise "\nERROR: Thawing Unfrozen Tree Control?"
5619 self
._freezeCount
= self
._freezeCount
- 1
5621 if not self
._freezeCount
:
5625 # ----------------------------------------------------------------------------
5626 # changing colours: we need to refresh the tree control
5627 # ----------------------------------------------------------------------------
5629 def SetBackgroundColour(self
, colour
):
5630 """Changes the background colour of CustomTreeCtrl."""
5632 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5635 if self
._freezeCount
:
5643 def SetForegroundColour(self
, colour
):
5644 """Changes the foreground colour of CustomTreeCtrl."""
5646 if not wx
.Window
.SetForegroundColour(self
, colour
):
5649 if self
._freezeCount
:
5657 def OnGetToolTip(self
, event
):
5659 Process the tooltip event, to speed up event processing. Does not actually
5666 def DoGetBestSize(self
):
5667 """Something is better than nothing..."""
5669 # something is better than nothing...
5670 # 100x80 is what the MSW version will get from the default
5671 # wxControl::DoGetBestSize
5673 return wx
.Size(100, 80)
5676 def GetClassDefaultAttributes(self
):
5677 """Gets the class default attributes."""
5679 attr
= wx
.VisualAttributes()
5680 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5681 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5682 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)