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.
1935 def OnDestroy(self
, event
):
1936 """Handles the wx.EVT_WINDOW_DESTROY event."""
1938 # Here there may be something I miss... do I have to destroy
1940 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1941 self
._renameTimer
.Stop()
1942 del self
._renameTimer
1944 if self
._findTimer
and self
._findTimer
.IsRunning():
1945 self
._findTimer
.Stop()
1952 """Returns the global number of items in the tree."""
1954 if not self
._anchor
:
1958 count
= self
._anchor
.GetChildrenCount()
1960 if not self
.HasFlag(TR_HIDE_ROOT
):
1961 # take the root itself into account
1967 def GetIndent(self
):
1968 """Returns the item indentation."""
1973 def GetSpacing(self
):
1974 """Returns the spacing between the start and the text."""
1976 return self
._spacing
1979 def GetRootItem(self
):
1980 """Returns the root item."""
1985 def GetSelection(self
):
1986 """Returns the current selection: TR_SINGLE only."""
1988 return self
._current
1991 def ToggleItemSelection(self
, item
):
1992 """Toggles the item selection."""
1995 raise "\nERROR: Invalid Tree Item. "
1997 self
.SelectItem(item
, not self
.IsSelected(item
))
2000 def EnableChildren(self
, item
, enable
=True):
2001 """Enables/disables item children. Used internally."""
2004 if item
.IsExpanded():
2007 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2008 # We hit a radiobutton item not checked, we don't want to
2009 # enable the children
2012 child
, cookie
= self
.GetFirstChild(item
)
2014 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2016 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2017 self
.EnableChildren(child
, enable
)
2018 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2021 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2022 """Enables/disables an item."""
2025 raise "\nERROR: Invalid Tree Item. "
2027 if item
.IsEnabled() == enable
:
2030 if not enable
and item
.IsSelected():
2031 self
.SelectItem(item
, False)
2034 wnd
= item
.GetWindow()
2036 # Handles the eventual window associated to the item
2038 wndenable
= item
.GetWindowEnabled()
2046 # We have to refresh the item line
2047 dc
= wx
.ClientDC(self
)
2048 self
.CalculateSize(item
, dc
)
2049 self
.RefreshLine(item
)
2052 def IsEnabled(self
, item
):
2053 """Returns whether an item is enabled or disabled."""
2056 raise "\nERROR: Invalid Tree Item. "
2058 return item
.IsEnabled()
2061 def SetDisabledColour(self
, colour
):
2062 """Sets the items disabled colour."""
2064 self
._disabledColour
= colour
2068 def GetDisabledColour(self
):
2069 """Returns the items disabled colour."""
2071 return self
._disabledColour
2074 def IsItemChecked(self
, item
):
2075 """Returns whether an item is checked or not."""
2078 raise "\nERROR: Invalid Tree Item. "
2080 return item
.IsChecked()
2083 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2084 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2086 if item
.GetType() == 0:
2092 dc
= wx
.ClientDC(self
)
2093 self
.CalculateSize(item
, dc
)
2094 self
.RefreshLine(item
)
2097 def UnCheckRadioParent(self
, item
, checked
=False):
2098 """Used internally to handle radio node parent correctly."""
2100 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2102 e
.SetEventObject(self
)
2104 if self
.GetEventHandler().ProcessEvent(e
):
2108 dc
= wx
.ClientDC(self
)
2109 self
.RefreshLine(item
)
2110 self
.EnableChildren(item
, checked
)
2111 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2113 e
.SetEventObject(self
)
2114 self
.GetEventHandler().ProcessEvent(e
)
2119 def CheckItem(self
, item
, checked
=True):
2121 Actually checks/uncheks an item, sending (eventually) the two
2122 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2126 raise "\nERROR: Invalid Tree Item. "
2128 # Should we raise an error here?!?
2129 if item
.GetType() == 0:
2132 if item
.GetType() == 2: # it's a radio button
2133 if not checked
and item
.IsChecked(): # Try To Unckeck?
2134 if item
.HasChildren():
2135 self
.UnCheckRadioParent(item
, checked
)
2138 if not self
.UnCheckRadioParent(item
, checked
):
2141 self
.CheckSameLevel(item
, False)
2144 # Radiobuttons are done, let's handle checkbuttons...
2145 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2147 e
.SetEventObject(self
)
2149 if self
.GetEventHandler().ProcessEvent(e
):
2154 dc
= wx
.ClientDC(self
)
2155 self
.RefreshLine(item
)
2157 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2158 ischeck
= self
.IsItemChecked(item
)
2159 self
.AutoCheckChild(item
, ischeck
)
2160 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2161 self
.AutoToggleChild(item
)
2163 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2165 e
.SetEventObject(self
)
2166 self
.GetEventHandler().ProcessEvent(e
)
2169 def AutoToggleChild(self
, item
):
2170 """Transverses the tree and toggles the items. Meaningful only for check items."""
2173 raise "\nERROR: Invalid Tree Item. "
2175 child
, cookie
= self
.GetFirstChild(item
)
2178 if item
.IsExpanded():
2183 if child
.GetType() == 1 and child
.IsEnabled():
2184 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2185 self
.AutoToggleChild(child
)
2186 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2189 def AutoCheckChild(self
, item
, checked
):
2190 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2193 raise "\nERROR: Invalid Tree Item. "
2195 (child
, cookie
) = self
.GetFirstChild(item
)
2198 if item
.IsExpanded():
2202 if child
.GetType() == 1 and child
.IsEnabled():
2203 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2204 self
.AutoCheckChild(child
, checked
)
2205 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2208 def CheckChilds(self
, item
, checked
=True):
2209 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2212 raise "\nERROR: Invalid Tree Item. "
2215 self
.AutoToggleChild(item
)
2217 self
.AutoCheckChild(item
, checked
)
2220 def CheckSameLevel(self
, item
, checked
=False):
2222 Uncheck radio items which are on the same level of the checked one.
2226 parent
= item
.GetParent()
2232 if parent
.IsExpanded():
2235 (child
, cookie
) = self
.GetFirstChild(parent
)
2237 if child
.GetType() == 2 and child
!= item
:
2238 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2239 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2240 self
.EnableChildren(child
, checked
)
2241 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2244 def EditLabel(self
, item
):
2245 """Starts editing an item label."""
2248 raise "\nERROR: Invalid Tree Item. "
2253 def ShouldInheritColours(self
):
2254 """We don't inherit colours from anyone."""
2259 def SetIndent(self
, indent
):
2260 """Sets item indentation."""
2262 self
._indent
= indent
2266 def SetSpacing(self
, spacing
):
2267 """Sets item spacing."""
2269 self
._spacing
= spacing
2273 def HasFlag(self
, flag
):
2274 """Returns whether CustomTreeCtrl has a flag."""
2276 return self
._windowStyle
& flag
2279 def HasChildren(self
, item
):
2280 """Returns whether an item has children or not."""
2283 raise "\nERROR: Invalid Tree Item. "
2285 return len(item
.GetChildren()) > 0
2288 def GetChildrenCount(self
, item
, recursively
=True):
2289 """Gets the item children count."""
2292 raise "\nERROR: Invalid Tree Item. "
2294 return item
.GetChildrenCount(recursively
)
2297 def SetTreeStyle(self
, styles
):
2298 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2300 # Do not try to expand the root node if it hasn't been created yet
2301 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2303 # if we will hide the root, make sure children are visible
2304 self
._anchor
.SetHasPlus()
2305 self
._anchor
.Expand()
2306 self
.CalculatePositions()
2308 # right now, just sets the styles. Eventually, we may
2309 # want to update the inherited styles, but right now
2310 # none of the parents has updatable styles
2312 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2313 selections
= self
.GetSelections()
2314 for select
in selections
[0:-1]:
2315 self
.SelectItem(select
, False)
2317 self
._windowStyle
= styles
2321 def GetTreeStyle(self
):
2322 """Returns the CustomTreeCtrl style."""
2324 return self
._windowStyle
2327 def HasButtons(self
):
2328 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2330 return self
.HasFlag(TR_HAS_BUTTONS
)
2333 # -----------------------------------------------------------------------------
2334 # functions to work with tree items
2335 # -----------------------------------------------------------------------------
2337 def GetItemText(self
, item
):
2338 """Returns the item text."""
2341 raise "\nERROR: Invalid Tree Item. "
2343 return item
.GetText()
2346 def GetItemImage(self
, item
, which
):
2347 """Returns the item image."""
2350 raise "\nERROR: Invalid Tree Item. "
2352 return item
.GetImage(which
)
2355 def GetPyData(self
, item
):
2356 """Returns the data associated to an item."""
2359 raise "\nERROR: Invalid Tree Item. "
2361 return item
.GetData()
2363 GetItemPyData
= GetPyData
2366 def GetItemTextColour(self
, item
):
2367 """Returns the item text colour."""
2370 raise "\nERROR: Invalid Tree Item. "
2372 return item
.Attr().GetTextColour()
2375 def GetItemBackgroundColour(self
, item
):
2376 """Returns the item background colour."""
2379 raise "\nERROR: Invalid Tree Item. "
2381 return item
.Attr().GetBackgroundColour()
2384 def GetItemFont(self
, item
):
2385 """Returns the item font."""
2388 raise "\nERROR: Invalid Tree Item. "
2390 return item
.Attr().GetFont()
2393 def IsItemHyperText(self
, item
):
2394 """Returns whether an item is hypertext or not."""
2397 raise "\nERROR: Invalid Tree Item. "
2399 return item
.IsHyperText()
2402 def SetItemText(self
, item
, text
):
2403 """Sets the item text."""
2406 raise "\nERROR: Invalid Tree Item. "
2408 dc
= wx
.ClientDC(self
)
2410 self
.CalculateSize(item
, dc
)
2411 self
.RefreshLine(item
)
2414 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2415 """Sets the item image, depending on the item state."""
2418 raise "\nERROR: Invalid Tree Item. "
2420 item
.SetImage(image
, which
)
2422 dc
= wx
.ClientDC(self
)
2423 self
.CalculateSize(item
, dc
)
2424 self
.RefreshLine(item
)
2427 def SetPyData(self
, item
, data
):
2428 """Sets the data associated to an item."""
2431 raise "\nERROR: Invalid Tree Item. "
2435 SetItemPyData
= SetPyData
2438 def SetItemHasChildren(self
, item
, has
=True):
2439 """Forces the appearance of the button next to the item."""
2442 raise "\nERROR: Invalid Tree Item. "
2444 item
.SetHasPlus(has
)
2445 self
.RefreshLine(item
)
2448 def SetItemBold(self
, item
, bold
=True):
2449 """Sets the item font bold/unbold."""
2452 raise "\nERROR: Invalid Tree Item. "
2454 # avoid redrawing the tree if no real change
2455 if item
.IsBold() != bold
:
2460 def SetItemItalic(self
, item
, italic
=True):
2461 """Sets the item font italic/non-italic."""
2464 raise "\nERROR: Invalid Tree Item. "
2466 if item
.IsItalic() != italic
:
2467 itemFont
= self
.GetItemFont(item
)
2468 if itemFont
!= wx
.NullFont
:
2473 item
.SetItalic(italic
)
2474 itemFont
.SetStyle(style
)
2475 self
.SetItemFont(item
, itemFont
)
2479 def SetItemDropHighlight(self
, item
, highlight
=True):
2481 Gives the item the visual feedback for drag and drop operations.
2482 This is useful when something is dragged from outside the CustomTreeCtrl.
2486 raise "\nERROR: Invalid Tree Item. "
2489 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2490 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2492 item
.Attr().SetTextColour(fg
)
2493 item
.Attr
.SetBackgroundColour(bg
)
2494 self
.RefreshLine(item
)
2497 def SetItemTextColour(self
, item
, col
):
2498 """Sets the item text colour."""
2501 raise "\nERROR: Invalid Tree Item. "
2503 if self
.GetItemTextColour(item
) == col
:
2506 item
.Attr().SetTextColour(col
)
2507 self
.RefreshLine(item
)
2510 def SetItemBackgroundColour(self
, item
, col
):
2511 """Sets the item background colour."""
2514 raise "\nERROR: Invalid Tree Item. "
2516 item
.Attr().SetBackgroundColour(col
)
2517 self
.RefreshLine(item
)
2520 def SetItemHyperText(self
, item
, hyper
=True):
2521 """Sets whether the item is hypertext or not."""
2524 raise "\nERROR: Invalid Tree Item. "
2526 item
.SetHyperText(hyper
)
2527 self
.RefreshLine(item
)
2530 def SetItemFont(self
, item
, font
):
2531 """Sets the item font."""
2534 raise "\nERROR: Invalid Tree Item. "
2536 if self
.GetItemFont(item
) == font
:
2539 item
.Attr().SetFont(font
)
2543 def SetFont(self
, font
):
2544 """Sets the CustomTreeCtrl font."""
2546 wx
.ScrolledWindow
.SetFont(self
, font
)
2548 self
._normalFont
= font
2549 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2550 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2551 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2556 def GetHyperTextFont(self
):
2557 """Returns the font used to render an hypertext item."""
2559 return self
._hypertextfont
2562 def SetHyperTextFont(self
, font
):
2563 """Sets the font used to render an hypertext item."""
2565 self
._hypertextfont
= font
2569 def SetHyperTextNewColour(self
, colour
):
2570 """Sets the colour used to render a non-visited hypertext item."""
2572 self
._hypertextnewcolour
= colour
2576 def GetHyperTextNewColour(self
):
2577 """Returns the colour used to render a non-visited hypertext item."""
2579 return self
._hypertextnewcolour
2582 def SetHyperTextVisitedColour(self
, colour
):
2583 """Sets the colour used to render a visited hypertext item."""
2585 self
._hypertextvisitedcolour
= colour
2589 def GetHyperTextVisitedColour(self
):
2590 """Returns the colour used to render a visited hypertext item."""
2592 return self
._hypertextvisitedcolour
2595 def SetItemVisited(self
, item
, visited
=True):
2596 """Sets whether an hypertext item was visited."""
2599 raise "\nERROR: Invalid Tree Item. "
2601 item
.SetVisited(visited
)
2602 self
.RefreshLine(item
)
2605 def GetItemVisited(self
, item
):
2606 """Returns whether an hypertext item was visited."""
2609 raise "\nERROR: Invalid Tree Item. "
2611 return item
.GetVisited()
2614 def SetHilightFocusColour(self
, colour
):
2616 Sets the colour used to highlight focused selected items.
2617 This is applied only if gradient and Windows Vista styles are disabled.
2620 self
._hilightBrush
= wx
.Brush(colour
)
2621 self
.RefreshSelected()
2624 def SetHilightNonFocusColour(self
, colour
):
2626 Sets the colour used to highlight unfocused selected items.
2627 This is applied only if gradient and Windows Vista styles are disabled.
2630 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2631 self
.RefreshSelected()
2634 def GetHilightFocusColour(self
):
2636 Returns the colour used to highlight focused selected items.
2637 This is applied only if gradient and Windows Vista styles are disabled.
2640 return self
._hilightBrush
.GetColour()
2643 def GetHilightNonFocusColour(self
):
2645 Returns the colour used to highlight unfocused selected items.
2646 This is applied only if gradient and Windows Vista styles are disabled.
2649 return self
._hilightUnfocusedBrush
.GetColour()
2652 def SetFirstGradientColour(self
, colour
=None):
2653 """Sets the first gradient colour."""
2656 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2658 self
._firstcolour
= colour
2659 if self
._usegradients
:
2660 self
.RefreshSelected()
2663 def SetSecondGradientColour(self
, colour
=None):
2664 """Sets the second gradient colour."""
2667 # No colour given, generate a slightly darker from the
2668 # CustomTreeCtrl background colour
2669 color
= self
.GetBackgroundColour()
2670 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2671 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2672 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2674 self
._secondcolour
= colour
2676 if self
._usegradients
:
2677 self
.RefreshSelected()
2680 def GetFirstGradientColour(self
):
2681 """Returns the first gradient colour."""
2683 return self
._firstcolour
2686 def GetSecondGradientColour(self
):
2687 """Returns the second gradient colour."""
2689 return self
._secondcolour
2692 def EnableSelectionGradient(self
, enable
=True):
2693 """Globally enables/disables drawing of gradient selection."""
2695 self
._usegradients
= enable
2696 self
._vistaselection
= False
2697 self
.RefreshSelected()
2700 def SetGradientStyle(self
, vertical
=0):
2702 Sets the gradient style:
2703 0: horizontal gradient
2704 1: vertical gradient
2707 # 0 = Horizontal, 1 = Vertical
2708 self
._gradientstyle
= vertical
2710 if self
._usegradients
:
2711 self
.RefreshSelected()
2714 def GetGradientStyle(self
):
2716 Returns the gradient style:
2717 0: horizontal gradient
2718 1: vertical gradient
2721 return self
._gradientstyle
2724 def EnableSelectionVista(self
, enable
=True):
2725 """Globally enables/disables drawing of Windows Vista selection."""
2727 self
._usegradients
= False
2728 self
._vistaselection
= enable
2729 self
.RefreshSelected()
2732 def SetBorderPen(self
, pen
):
2734 Sets the pen used to draw the selected item border.
2735 The border pen is not used if the Windows Vista style is applied.
2738 self
._borderPen
= pen
2739 self
.RefreshSelected()
2742 def GetBorderPen(self
):
2744 Returns the pen used to draw the selected item border.
2745 The border pen is not used if the Windows Vista style is applied.
2748 return self
._borderPen
2751 def SetConnectionPen(self
, pen
):
2752 """Sets the pen used to draw the connecting lines between items."""
2754 self
._dottedPen
= pen
2758 def GetConnectionPen(self
):
2759 """Returns the pen used to draw the connecting lines between items."""
2761 return self
._dottedPen
2764 def SetBackgroundImage(self
, image
):
2765 """Sets the CustomTreeCtrl background image (can be none)."""
2767 self
._backgroundImage
= image
2771 def GetBackgroundImage(self
):
2772 """Returns the CustomTreeCtrl background image (can be none)."""
2774 return self
._backgroundImage
2777 def GetItemWindow(self
, item
):
2778 """Returns the window associated to the item (if any)."""
2781 raise "\nERROR: Invalid Item"
2783 return item
.GetWindow()
2786 def GetItemWindowEnabled(self
, item
):
2787 """Returns whether the window associated to the item is enabled."""
2790 raise "\nERROR: Invalid Item"
2792 return item
.GetWindowEnabled()
2795 def SetItemWindowEnabled(self
, item
, enable
=True):
2796 """Enables/disables the window associated to the item."""
2799 raise "\nERROR: Invalid Item"
2801 item
.SetWindowEnabled(enable
)
2804 def GetItemType(self
, item
):
2806 Returns the item type:
2813 raise "\nERROR: Invalid Item"
2815 return item
.GetType()
2817 # -----------------------------------------------------------------------------
2818 # item status inquiries
2819 # -----------------------------------------------------------------------------
2821 def IsVisible(self
, item
):
2822 """Returns whether the item is visible or not."""
2825 raise "\nERROR: Invalid Tree Item. "
2827 # An item is only visible if it's not a descendant of a collapsed item
2828 parent
= item
.GetParent()
2832 if not parent
.IsExpanded():
2835 parent
= parent
.GetParent()
2837 startX
, startY
= self
.GetViewStart()
2838 clientSize
= self
.GetClientSize()
2840 rect
= self
.GetBoundingRect(item
)
2844 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2846 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2848 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2854 def ItemHasChildren(self
, item
):
2855 """Returns whether the item has children or not."""
2858 raise "\nERROR: Invalid Tree Item. "
2860 # consider that the item does have children if it has the "+" button: it
2861 # might not have them (if it had never been expanded yet) but then it
2862 # could have them as well and it's better to err on this side rather than
2863 # disabling some operations which are restricted to the items with
2864 # children for an item which does have them
2865 return item
.HasPlus()
2868 def IsExpanded(self
, item
):
2869 """Returns whether the item is expanded or not."""
2872 raise "\nERROR: Invalid Tree Item. "
2874 return item
.IsExpanded()
2877 def IsSelected(self
, item
):
2878 """Returns whether the item is selected or not."""
2881 raise "\nERROR: Invalid Tree Item. "
2883 return item
.IsSelected()
2886 def IsBold(self
, item
):
2887 """Returns whether the item font is bold or not."""
2890 raise "\nERROR: Invalid Tree Item. "
2892 return item
.IsBold()
2895 def IsItalic(self
, item
):
2896 """Returns whether the item font is italic or not."""
2899 raise "\nERROR: Invalid Tree Item. "
2901 return item
.IsItalic()
2904 # -----------------------------------------------------------------------------
2906 # -----------------------------------------------------------------------------
2908 def GetItemParent(self
, item
):
2909 """Gets the item parent."""
2912 raise "\nERROR: Invalid Tree Item. "
2914 return item
.GetParent()
2917 def GetFirstChild(self
, item
):
2918 """Gets the item first child."""
2921 raise "\nERROR: Invalid Tree Item. "
2924 return self
.GetNextChild(item
, cookie
)
2927 def GetNextChild(self
, item
, cookie
):
2929 Gets the item next child based on the 'cookie' parameter.
2930 This method has no sense if you do not call GetFirstChild() before.
2934 raise "\nERROR: Invalid Tree Item. "
2936 children
= item
.GetChildren()
2938 # it's ok to cast cookie to size_t, we never have indices big enough to
2941 if cookie
< len(children
):
2943 return children
[cookie
], cookie
+1
2947 # there are no more of them
2951 def GetLastChild(self
, item
):
2952 """Gets the item last child."""
2955 raise "\nERROR: Invalid Tree Item. "
2957 children
= item
.GetChildren()
2958 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2961 def GetNextSibling(self
, item
):
2962 """Gets the next sibling of an item."""
2965 raise "\nERROR: Invalid Tree Item. "
2968 parent
= i
.GetParent()
2972 # root item doesn't have any siblings
2975 siblings
= parent
.GetChildren()
2976 index
= siblings
.index(i
)
2979 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
2982 def GetPrevSibling(self
, item
):
2983 """Gets the previous sibling of an item."""
2986 raise "\nERROR: Invalid Tree Item. "
2989 parent
= i
.GetParent()
2993 # root item doesn't have any siblings
2996 siblings
= parent
.GetChildren()
2997 index
= siblings
.index(i
)
2999 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3002 def GetNext(self
, item
):
3003 """Gets the next item. Only for internal use right now."""
3006 raise "\nERROR: Invalid Tree Item. "
3010 # First see if there are any children.
3011 children
= i
.GetChildren()
3012 if len(children
) > 0:
3015 # Try a sibling of this or ancestor instead
3018 while p
and not toFind
:
3019 toFind
= self
.GetNextSibling(p
)
3020 p
= self
.GetItemParent(p
)
3025 def GetFirstVisibleItem(self
):
3026 """Returns the first visible item."""
3028 id = self
.GetRootItem()
3033 if self
.IsVisible(id):
3035 id = self
.GetNext(id)
3040 def GetNextVisible(self
, item
):
3041 """Returns the next visible item."""
3044 raise "\nERROR: Invalid Tree Item. "
3049 id = self
.GetNext(id)
3050 if id and self
.IsVisible(id):
3056 def GetPrevVisible(self
, item
):
3059 raise "\nERROR: Invalid Tree Item. "
3061 raise "\nERROR: Not Implemented"
3066 def ResetTextControl(self
):
3067 """Called by TreeTextCtrl when it marks itself for deletion."""
3069 self
._textCtrl
.Destroy()
3070 self
._textCtrl
= None
3073 def FindItem(self
, idParent
, prefixOrig
):
3074 """Finds the first item starting with the given prefix after the given item."""
3076 # match is case insensitive as this is more convenient to the user: having
3077 # to press Shift-letter to go to the item starting with a capital letter
3078 # would be too bothersome
3079 prefix
= prefixOrig
.lower()
3081 # determine the starting point: we shouldn't take the current item (this
3082 # allows to switch between two items starting with the same letter just by
3083 # pressing it) but we shouldn't jump to the next one if the user is
3084 # continuing to type as otherwise he might easily skip the item he wanted
3087 if len(prefix
) == 1:
3088 id = self
.GetNext(id)
3090 # look for the item starting with the given prefix after it
3091 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3093 id = self
.GetNext(id)
3095 # if we haven't found anything...
3098 # ... wrap to the beginning
3099 id = self
.GetRootItem()
3100 if self
.HasFlag(TR_HIDE_ROOT
):
3101 # can't select virtual root
3102 id = self
.GetNext(id)
3104 # and try all the items (stop when we get to the one we started from)
3105 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3106 id = self
.GetNext(id)
3111 # -----------------------------------------------------------------------------
3113 # -----------------------------------------------------------------------------
3115 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3116 """Actually inserts an item in the tree."""
3118 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3119 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3121 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3122 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3124 if ct_type
< 0 or ct_type
> 2:
3125 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3131 # should we give a warning here?
3132 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3134 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3136 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3139 self
._hasWindows
= True
3140 self
._itemWithWindow
.append(item
)
3142 parent
.Insert(item
, previous
)
3147 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3148 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3151 raise "\nERROR: Tree Can Have Only One Root"
3153 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3154 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3156 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3157 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3159 if ct_type
< 0 or ct_type
> 2:
3160 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3162 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3164 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3167 self
._hasWindows
= True
3168 self
._itemWithWindow
.append(self
._anchor
)
3170 if self
.HasFlag(TR_HIDE_ROOT
):
3172 # if root is hidden, make sure we can navigate
3174 self
._anchor
.SetHasPlus()
3175 self
._anchor
.Expand()
3176 self
.CalculatePositions()
3178 if not self
.HasFlag(TR_MULTIPLE
):
3180 self
._current
= self
._key
_current
= self
._anchor
3181 self
._current
.SetHilight(True)
3186 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3187 """Appends an item as a first child of parent."""
3189 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3190 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3192 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3193 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3195 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3198 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3199 """Auxiliary function to cope with the C++ hideous multifunction."""
3201 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3202 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3204 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3205 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3210 # should we give a warning here?
3211 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3217 index
= parent
.GetChildren().index(idPrevious
)
3219 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3221 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3224 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3225 """Auxiliary function to cope with the C++ hideous multifunction."""
3227 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3228 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3230 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3231 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3236 # should we give a warning here?
3237 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3239 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3242 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3243 """Inserts an item after the given previous."""
3245 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3246 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3248 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3249 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3251 if type(input) == type(1):
3252 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3254 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3257 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3258 """Appends an item as a last child of its parent."""
3260 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3261 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3263 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3264 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3269 # should we give a warning here?
3270 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3272 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3275 def SendDeleteEvent(self
, item
):
3276 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3278 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3280 event
.SetEventObject(self
)
3281 self
.ProcessEvent(event
)
3284 def IsDescendantOf(self
, parent
, item
):
3285 """Checks if the given item is under another one."""
3291 # item is a descendant of parent
3294 item
= item
.GetParent()
3299 # Don't leave edit or selection on a child which is about to disappear
3300 def ChildrenClosing(self
, item
):
3301 """We are about to destroy the item children."""
3303 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3304 self
._textCtrl
.StopEditing()
3306 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3307 self
._key
_current
= None
3309 if self
.IsDescendantOf(item
, self
._select
_me
):
3310 self
._select
_me
= item
3312 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3313 self
._current
.SetHilight(False)
3314 self
._current
= None
3315 self
._select
_me
= item
3318 def DeleteChildren(self
, item
):
3319 """Delete item children."""
3322 raise "\nERROR: Invalid Tree Item. "
3324 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3326 self
.ChildrenClosing(item
)
3327 item
.DeleteChildren(self
)
3330 def Delete(self
, item
):
3331 """Delete an item."""
3334 raise "\nERROR: Invalid Tree Item. "
3336 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3338 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3339 # can't delete the item being edited, cancel editing it first
3340 self
._textCtrl
.StopEditing()
3342 parent
= item
.GetParent()
3344 # don't keep stale pointers around!
3345 if self
.IsDescendantOf(item
, self
._key
_current
):
3347 # Don't silently change the selection:
3348 # do it properly in idle time, so event
3349 # handlers get called.
3351 # self._key_current = parent
3352 self
._key
_current
= None
3354 # self._select_me records whether we need to select
3355 # a different item, in idle time.
3356 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3357 self
._select
_me
= parent
3359 if self
.IsDescendantOf(item
, self
._current
):
3361 # Don't silently change the selection:
3362 # do it properly in idle time, so event
3363 # handlers get called.
3365 # self._current = parent
3366 self
._current
= None
3367 self
._select
_me
= parent
3369 # remove the item from the tree
3372 parent
.GetChildren().remove(item
) # remove by value
3374 else: # deleting the root
3376 # nothing will be left in the tree
3379 # and delete all of its children and the item itself now
3380 item
.DeleteChildren(self
)
3381 self
.SendDeleteEvent(item
)
3383 if item
== self
._select
_me
:
3384 self
._select
_me
= None
3386 # Remove the item with window
3387 if item
in self
._itemWithWindow
:
3388 wnd
= item
.GetWindow()
3392 self
._itemWithWindow
.remove(item
)
3397 def DeleteAllItems(self
):
3398 """Delete all items in the CustomTreeCtrl."""
3401 self
.Delete(self
._anchor
)
3404 def Expand(self
, item
):
3406 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3407 EVT_TREE_ITEM_EXPANDED events.
3411 raise "\nERROR: Invalid Tree Item. "
3413 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3414 raise "\nERROR: Can't Expand An Hidden Root. "
3416 if not item
.HasPlus():
3419 if item
.IsExpanded():
3422 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3424 event
.SetEventObject(self
)
3426 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3427 # cancelled by program
3431 self
.CalculatePositions()
3433 self
.RefreshSubtree(item
)
3435 if self
._hasWindows
:
3436 # We hide the associated window here, we may show it after
3439 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3440 self
.ProcessEvent(event
)
3443 def ExpandAll(self
, item
):
3444 """Expands all the items."""
3447 raise "\nERROR: Invalid Tree Item. "
3449 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= GetRootItem():
3451 if not self
.IsExpanded(item
):
3454 child
, cookie
= self
.GetFirstChild(item
)
3457 self
.ExpandAll(child
)
3458 child
, cookie
= self
.GetNextChild(item
, cookie
)
3461 def Collapse(self
, item
):
3463 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3464 EVT_TREE_ITEM_COLLAPSED events.
3468 raise "\nERROR: Invalid Tree Item. "
3470 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3471 raise "\nERROR: Can't Collapse An Hidden Root. "
3473 if not item
.IsExpanded():
3476 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3478 event
.SetEventObject(self
)
3479 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3480 # cancelled by program
3483 self
.ChildrenClosing(item
)
3486 self
.CalculatePositions()
3487 self
.RefreshSubtree(item
)
3489 if self
._hasWindows
:
3492 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3493 self
.ProcessEvent(event
)
3496 def CollapseAndReset(self
, item
):
3497 """Collapse the given item and deletes its children."""
3500 self
.DeleteChildren(item
)
3503 def Toggle(self
, item
):
3504 """Toggles the item state (collapsed/expanded)."""
3506 if item
.IsExpanded():
3512 def HideWindows(self
):
3513 """Hides the windows associated to the items. Used internally."""
3515 for child
in self
._itemWithWindow
:
3516 if not self
.IsVisible(child
):
3517 wnd
= child
.GetWindow()
3522 """Unselects the current selection."""
3526 self
._current
.SetHilight(False)
3527 self
.RefreshLine(self
._current
)
3529 self
._current
= None
3530 self
._select
_me
= None
3533 def UnselectAllChildren(self
, item
):
3534 """Unselects all the children of the given item."""
3536 if item
.IsSelected():
3538 item
.SetHilight(False)
3539 self
.RefreshLine(item
)
3541 if item
.HasChildren():
3542 for child
in item
.GetChildren():
3543 self
.UnselectAllChildren(child
)
3546 def UnselectAll(self
):
3547 """Unselect all the items."""
3549 rootItem
= self
.GetRootItem()
3551 # the tree might not have the root item at all
3553 self
.UnselectAllChildren(rootItem
)
3556 # Recursive function !
3557 # To stop we must have crt_item<last_item
3559 # Tag all next children, when no more children,
3560 # Move to parent (not to tag)
3561 # Keep going... if we found last_item, we stop.
3563 def TagNextChildren(self
, crt_item
, last_item
, select
):
3564 """Used internally."""
3566 parent
= crt_item
.GetParent()
3568 if parent
== None: # This is root item
3569 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3571 children
= parent
.GetChildren()
3572 index
= children
.index(crt_item
)
3574 count
= len(children
)
3576 for n
in xrange(index
+1, count
):
3577 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3580 return self
.TagNextChildren(parent
, last_item
, select
)
3583 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3584 """Used internally."""
3586 crt_item
.SetHilight(select
)
3587 self
.RefreshLine(crt_item
)
3589 if crt_item
== last_item
:
3592 if crt_item
.HasChildren():
3593 for child
in crt_item
.GetChildren():
3594 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3600 def SelectItemRange(self
, item1
, item2
):
3601 """Selects all the items between item1 and item2."""
3603 self
._select
_me
= None
3605 # item2 is not necessary after item1
3606 # choice first' and 'last' between item1 and item2
3607 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3608 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3610 select
= self
._current
.IsSelected()
3612 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3615 self
.TagNextChildren(first
, last
, select
)
3618 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3619 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3622 raise "\nERROR: Invalid Tree Item. "
3624 self
._select
_me
= None
3626 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3628 # to keep going anyhow !!!
3630 if item
.IsSelected():
3631 return # nothing to do
3632 unselect_others
= True
3633 extended_select
= False
3635 elif unselect_others
and item
.IsSelected():
3637 # selection change if there is more than one item currently selected
3638 if len(self
.GetSelections()) == 1:
3641 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3643 event
._itemOld
= self
._current
3644 event
.SetEventObject(self
)
3645 # TODO : Here we don't send any selection mode yet !
3647 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3650 parent
= self
.GetItemParent(item
)
3652 if not self
.IsExpanded(parent
):
3655 parent
= self
.GetItemParent(parent
)
3660 self
.Unselect() # to speed up thing
3666 if not self
._current
:
3667 self
._current
= self
._key
_current
= self
.GetRootItem()
3669 # don't change the mark (self._current)
3670 self
.SelectItemRange(self
._current
, item
)
3674 select
= True # the default
3676 # Check if we need to toggle hilight (ctrl mode)
3677 if not unselect_others
:
3678 select
= not item
.IsSelected()
3680 self
._current
= self
._key
_current
= item
3681 self
._current
.SetHilight(select
)
3682 self
.RefreshLine(self
._current
)
3684 # This can cause idle processing to select the root
3685 # if no item is selected, so it must be after the
3687 self
.EnsureVisible(item
)
3689 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3690 self
.GetEventHandler().ProcessEvent(event
)
3692 # Handles hypertext items
3693 if self
.IsItemHyperText(item
):
3694 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3696 self
.GetEventHandler().ProcessEvent(event
)
3699 def SelectItem(self
, item
, select
=True):
3700 """Selects/deselects an item."""
3703 raise "\nERROR: Invalid Tree Item. "
3707 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3711 item
.SetHilight(False)
3712 self
.RefreshLine(item
)
3715 def FillArray(self
, item
, array
=[]):
3717 Internal function. Used to populate an array of selected items when
3718 the style TR_MULTIPLE is used.
3724 if item
.IsSelected():
3727 if item
.HasChildren():
3728 for child
in item
.GetChildren():
3729 array
= self
.FillArray(child
, array
)
3734 def GetSelections(self
):
3736 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3737 the TR_MULTIPLE style set.
3741 idRoot
= self
.GetRootItem()
3743 array
= self
.FillArray(idRoot
, array
)
3745 #else: the tree is empty, so no selections
3750 def EnsureVisible(self
, item
):
3751 """Ensure that an item is visible in CustomTreeCtrl."""
3754 raise "\nERROR: Invalid Tree Item. "
3756 # first expand all parent branches
3757 parent
= item
.GetParent()
3759 if self
.HasFlag(TR_HIDE_ROOT
):
3760 while parent
and parent
!= self
._anchor
:
3762 parent
= parent
.GetParent()
3766 parent
= parent
.GetParent()
3771 def ScrollTo(self
, item
):
3772 """Scrolls the specified item into view."""
3777 # We have to call this here because the label in
3778 # question might just have been added and no screen
3779 # update taken place.
3781 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3786 # now scroll to the item
3787 item_y
= item
.GetY()
3788 start_x
, start_y
= self
.GetViewStart()
3789 start_y
*= _PIXELS_PER_UNIT
3791 client_w
, client_h
= self
.GetClientSize()
3795 if item_y
< start_y
+3:
3798 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3799 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3800 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3801 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3802 # Item should appear at top
3803 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3805 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3808 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3809 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3810 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3811 item_y
+= _PIXELS_PER_UNIT
+2
3812 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3813 # Item should appear at bottom
3814 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
)
3817 def OnCompareItems(self
, item1
, item2
):
3819 Returns whether 2 items have the same text.
3820 Override this function in the derived class to change the sort order of the items
3821 in the CustomTreeCtrl. The function should return a negative, zero or positive
3822 value if the first item is less than, equal to or greater than the second one.
3824 The base class version compares items alphabetically.
3827 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3830 def SortChildren(self
, item
):
3832 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3833 You should override that method to change the sort order (the default is ascending
3834 case-sensitive alphabetical order).
3838 raise "\nERROR: Invalid Tree Item. "
3840 children
= item
.GetChildren()
3842 if len(children
) > 1:
3844 children
.sort(self
.OnCompareItems
)
3847 def GetImageList(self
):
3848 """Returns the normal image list."""
3850 return self
._imageListNormal
3853 def GetButtonsImageList(self
):
3854 """Returns the buttons image list (from which application-defined button images are taken)."""
3856 return self
._imageListButtons
3859 def GetStateImageList(self
):
3860 """Returns the state image list (from which application-defined state images are taken)."""
3862 return self
._imageListState
3865 def GetImageListCheck(self
):
3866 """Returns the image list used to build the check/radio buttons."""
3868 return self
._imageListCheck
3871 def CalculateLineHeight(self
):
3872 """Calculates the height of a line."""
3874 dc
= wx
.ClientDC(self
)
3875 self
._lineHeight
= dc
.GetCharHeight()
3877 if self
._imageListNormal
:
3879 # Calculate a self._lineHeight value from the normal Image sizes.
3880 # May be toggle off. Then CustomTreeCtrl will spread when
3881 # necessary (which might look ugly).
3882 n
= self
._imageListNormal
.GetImageCount()
3886 width
, height
= self
._imageListNormal
.GetSize(i
)
3888 if height
> self
._lineHeight
:
3889 self
._lineHeight
= height
3891 if self
._imageListButtons
:
3893 # Calculate a self._lineHeight value from the Button image sizes.
3894 # May be toggle off. Then CustomTreeCtrl will spread when
3895 # necessary (which might look ugly).
3896 n
= self
._imageListButtons
.GetImageCount()
3900 width
, height
= self
._imageListButtons
.GetSize(i
)
3902 if height
> self
._lineHeight
:
3903 self
._lineHeight
= height
3905 if self
._imageListCheck
:
3907 # Calculate a self._lineHeight value from the check/radio image sizes.
3908 # May be toggle off. Then CustomTreeCtrl will spread when
3909 # necessary (which might look ugly).
3910 n
= self
._imageListCheck
.GetImageCount()
3914 width
, height
= self
._imageListCheck
.GetSize(i
)
3916 if height
> self
._lineHeight
:
3917 self
._lineHeight
= height
3919 if self
._lineHeight
< 30:
3920 self
._lineHeight
+= 2 # at least 2 pixels
3922 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3925 def SetImageList(self
, imageList
):
3926 """Sets the normal image list."""
3928 if self
._ownsImageListNormal
:
3929 del self
._imageListNormal
3931 self
._imageListNormal
= imageList
3932 self
._ownsImageListNormal
= False
3934 # Don't do any drawing if we're setting the list to NULL,
3935 # since we may be in the process of deleting the tree control.
3937 self
.CalculateLineHeight()
3939 # We gray out the image list to use the grayed icons with disabled items
3940 self
._grayedImageList
= wx
.ImageList(16, 16, True, 0)
3942 for ii
in xrange(imageList
.GetImageCount()):
3944 bmp
= imageList
.GetBitmap(ii
)
3945 image
= wx
.ImageFromBitmap(bmp
)
3946 image
= GrayOut(image
)
3947 newbmp
= wx
.BitmapFromImage(image
)
3948 self
._grayedImageList
.Add(newbmp
)
3951 def SetStateImageList(self
, imageList
):
3952 """Sets the state image list (from which application-defined state images are taken)."""
3954 if self
._ownsImageListState
:
3955 del self
._imageListState
3957 self
._imageListState
= imageList
3958 self
._ownsImageListState
= False
3961 def SetButtonsImageList(self
, imageList
):
3962 """Sets the buttons image list (from which application-defined button images are taken)."""
3964 if self
._ownsImageListButtons
:
3965 del self
._imageListButtons
3967 self
._imageListButtons
= imageList
3968 self
._ownsImageListButtons
= False
3970 self
.CalculateLineHeight()
3973 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
3974 """Sets the check image list."""
3978 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
3979 self
._imageListCheck
.Add(GetCheckedBitmap())
3980 self
._imageListCheck
.Add(GetNotCheckedBitmap())
3981 self
._imageListCheck
.Add(GetFlaggedBitmap())
3982 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
3986 sizex
, sizey
= imglist
.GetSize(0)
3987 self
._imageListCheck
= imglist
3989 # We gray out the image list to use the grayed icons with disabled items
3990 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
3992 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
3994 bmp
= self
._imageListCheck
.GetBitmap(ii
)
3995 image
= wx
.ImageFromBitmap(bmp
)
3996 image
= GrayOut(image
)
3997 newbmp
= wx
.BitmapFromImage(image
)
3998 self
._grayedCheckList
.Add(newbmp
)
4003 self
.CalculateLineHeight()
4006 def AssignImageList(self
, imageList
):
4007 """Assigns the normal image list."""
4009 self
.SetImageList(imageList
)
4010 self
._ownsImageListNormal
= True
4013 def AssignStateImageList(self
, imageList
):
4014 """Assigns the state image list."""
4016 self
.SetStateImageList(imageList
)
4017 self
._ownsImageListState
= True
4020 def AssignButtonsImageList(self
, imageList
):
4021 """Assigns the button image list."""
4023 self
.SetButtonsImageList(imageList
)
4024 self
._ownsImageListButtons
= True
4027 # -----------------------------------------------------------------------------
4029 # -----------------------------------------------------------------------------
4031 def AdjustMyScrollbars(self
):
4032 """Adjust the wx.ScrolledWindow scrollbars."""
4036 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4037 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4038 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4039 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4040 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4041 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4045 self
.SetScrollbars(0, 0, 0, 0)
4048 def GetLineHeight(self
, item
):
4049 """Returns the line height for the given item."""
4051 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4052 return item
.GetHeight()
4054 return self
._lineHeight
4057 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4058 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4060 dc
.DrawRectangleRect(rect
)
4061 border
= self
._borderPen
.GetWidth()
4063 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4065 # calculate gradient coefficients
4067 col2
= self
._secondcolour
4068 col1
= self
._firstcolour
4070 col2
= self
._hilightUnfocusedBrush
.GetColour()
4071 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4073 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4074 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4076 flrect
= float(rect
.height
)
4078 rstep
= float((r2
- r1
)) / flrect
4079 gstep
= float((g2
- g1
)) / flrect
4080 bstep
= float((b2
- b1
)) / flrect
4082 rf
, gf
, bf
= 0, 0, 0
4084 for y
in xrange(rect
.y
+border
, rect
.y
+ rect
.height
-border
):
4085 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4086 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4087 dc
.DrawRectangle(rect
.x
+border
, y
, rect
.width
-2*border
, 1)
4093 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4094 """Gradient fill from colour 1 to colour 2 from left to right."""
4096 dc
.DrawRectangleRect(rect
)
4097 border
= self
._borderPen
.GetWidth()
4099 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4101 # calculate gradient coefficients
4104 col2
= self
._secondcolour
4105 col1
= self
._firstcolour
4107 col2
= self
._hilightUnfocusedBrush
.GetColour()
4108 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4110 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4111 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4113 flrect
= float(rect
.width
)
4115 rstep
= float((r2
- r1
)) / flrect
4116 gstep
= float((g2
- g1
)) / flrect
4117 bstep
= float((b2
- b1
)) / flrect
4119 rf
, gf
, bf
= 0, 0, 0
4121 for x
in xrange(rect
.x
+border
, rect
.x
+ rect
.width
-border
):
4122 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4123 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4124 dc
.DrawRectangle(x
, rect
.y
+border
, 1, rect
.height
-2*border
)
4130 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4131 """Draw the selected item(s) with the Windows Vista style."""
4135 outer
= _rgbSelectOuter
4136 inner
= _rgbSelectInner
4138 bottom
= _rgbSelectBottom
4142 outer
= _rgbNoFocusOuter
4143 inner
= _rgbNoFocusInner
4144 top
= _rgbNoFocusTop
4145 bottom
= _rgbNoFocusBottom
4147 oldpen
= dc
.GetPen()
4148 oldbrush
= dc
.GetBrush()
4150 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4151 dc
.SetPen(wx
.Pen(outer
))
4152 dc
.DrawRoundedRectangleRect(rect
, 3)
4154 dc
.SetPen(wx
.Pen(inner
))
4155 dc
.DrawRoundedRectangleRect(rect
, 2)
4158 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4159 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4161 flrect
= float(rect
.height
)
4163 rstep
= float((r2
- r1
)) / flrect
4164 gstep
= float((g2
- g1
)) / flrect
4165 bstep
= float((b2
- b1
)) / flrect
4167 rf
, gf
, bf
= 0, 0, 0
4168 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4170 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4171 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4172 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4173 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4179 dc
.SetBrush(oldbrush
)
4182 def PaintItem(self
, item
, dc
):
4183 """Actually paint an item."""
4185 attr
= item
.GetAttributes()
4187 if attr
and attr
.HasFont():
4188 dc
.SetFont(attr
.GetFont())
4190 dc
.SetFont(self
._boldFont
)
4191 if item
.IsHyperText():
4192 dc
.SetFont(self
.GetHyperTextFont())
4193 if item
.GetVisited():
4194 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4196 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4198 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4200 image
= item
.GetCurrentImage()
4201 checkimage
= item
.GetCurrentCheckedImage()
4202 image_w
, image_h
= 0, 0
4204 if image
!= _NO_IMAGE
:
4206 if self
._imageListNormal
:
4208 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4215 if item
.GetType() != 0:
4216 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4219 wcheck
, hcheck
= 0, 0
4221 total_h
= self
.GetLineHeight(item
)
4222 drawItemBackground
= False
4224 if item
.IsSelected():
4226 # under mac selections are only a rectangle in case they don't have the focus
4227 if wx
.Platform
== "__WXMAC__":
4228 if not self
._hasFocus
:
4229 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4230 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4232 dc
.SetBrush(self
._hilightBrush
)
4234 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4235 drawItemBackground
= True
4237 if attr
and attr
.HasBackgroundColour():
4238 drawItemBackground
= True
4239 colBg
= attr
.GetBackgroundColour()
4241 colBg
= self
._backgroundColour
4243 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4244 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4246 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4248 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4250 oldpen
= dc
.GetPen()
4251 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4252 x
, y
= self
.GetPosition()
4253 w
, h
= self
.GetSize()
4255 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4257 if item
.IsSelected():
4258 if self
._usegradients
:
4259 if self
._gradientstyle
== 0: # Horizontal
4260 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4262 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4263 elif self
._vistaselection
:
4264 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4266 dc
.DrawRectangleRect(itemrect
)
4272 if item
.IsSelected() and image
!= _NO_IMAGE
:
4274 # If it's selected, and there's an image, then we should
4275 # take care to leave the area under the image painted in the
4276 # background colour.
4278 wnd
= item
.GetWindow()
4281 wndx
, wndy
= item
.GetWindowSize()
4283 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2, item
.GetY()+offset
,
4284 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
, total_h
-offset
)
4286 if self
._usegradients
:
4287 if self
._gradientstyle
== 0: # Horizontal
4288 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4290 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4291 elif self
._vistaselection
:
4292 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4294 dc
.DrawRectangleRect(itemrect
)
4296 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4297 # don't allow backgrounds to be customized. Not drawing the background,
4298 # except for custom item backgrounds, works for both kinds of theme.
4299 elif drawItemBackground
:
4301 minusicon
= wcheck
+ image_w
- 2
4302 itemrect
= wx
.Rect(item
.GetX()+minusicon
, item
.GetY()+offset
, item
.GetWidth()-minusicon
, total_h
-offset
)
4304 if self
._usegradients
and self
._hasFocus
:
4305 if self
._gradientstyle
== 0: # Horizontal
4306 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4308 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4310 dc
.DrawRectangleRect(itemrect
)
4312 if image
!= _NO_IMAGE
:
4314 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4315 if item
.IsEnabled():
4316 imglist
= self
._imageListNormal
4318 imglist
= self
._grayedImageList
4320 imglist
.Draw(image
, dc
,
4321 item
.GetX() + wcheck
,
4322 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4323 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4325 dc
.DestroyClippingRegion()
4328 if item
.IsEnabled():
4329 imglist
= self
._imageListCheck
4331 imglist
= self
._grayedCheckList
4333 imglist
.Draw(checkimage
, dc
,
4335 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4336 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4338 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4339 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4341 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4343 if not item
.IsEnabled():
4344 foreground
= dc
.GetTextForeground()
4345 dc
.SetTextForeground(self
._disabledColour
)
4346 dc
.DrawLabel(item
.GetText(), textrect
)
4347 dc
.SetTextForeground(foreground
)
4349 dc
.DrawLabel(item
.GetText(), textrect
)
4351 wnd
= item
.GetWindow()
4353 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4354 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4355 if not wnd
.IsShown():
4357 if wnd
.GetPosition() != (wndx
, ya
):
4358 wnd
.SetPosition((wndx
, ya
))
4360 # restore normal font
4361 dc
.SetFont(self
._normalFont
)
4364 # Now y stands for the top of the item, whereas it used to stand for middle !
4365 def PaintLevel(self
, item
, dc
, level
, y
):
4366 """Paint a level of CustomTreeCtrl."""
4368 x
= level
*self
._indent
4370 if not self
.HasFlag(TR_HIDE_ROOT
):
4376 # always expand hidden root
4378 children
= item
.GetChildren()
4379 count
= len(children
)
4385 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4388 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4390 # draw line down to last child
4391 origY
+= self
.GetLineHeight(children
[0])>>1
4392 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4393 dc
.DrawLine(3, origY
, 3, oldY
)
4397 item
.SetX(x
+self
._spacing
)
4400 h
= self
.GetLineHeight(item
)
4402 y_mid
= y_top
+ (h
>>1)
4405 exposed_x
= dc
.LogicalToDeviceX(0)
4406 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4408 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4409 if wx
.Platform
== "__WXMAC__":
4410 # don't draw rect outline if we already have the
4411 # background color under Mac
4412 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4414 pen
= self
._borderPen
4416 if item
.IsSelected():
4417 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4418 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4420 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4422 attr
= item
.GetAttributes()
4423 if attr
and attr
.HasTextColour():
4424 colText
= attr
.GetTextColour()
4426 colText
= self
.GetForegroundColour()
4428 if self
._vistaselection
:
4432 dc
.SetTextForeground(colText
)
4437 self
.PaintItem(item
, dc
)
4439 if self
.HasFlag(TR_ROW_LINES
):
4441 # if the background colour is white, choose a
4442 # contrasting color for the lines
4443 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4444 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4445 dc
.DrawLine(0, y_top
, 10000, y_top
)
4446 dc
.DrawLine(0, y
, 10000, y
)
4448 # restore DC objects
4449 dc
.SetBrush(wx
.WHITE_BRUSH
)
4450 dc
.SetTextForeground(wx
.BLACK
)
4452 if not self
.HasFlag(TR_NO_LINES
):
4454 # draw the horizontal line here
4455 dc
.SetPen(self
._dottedPen
)
4457 if x
> self
._indent
:
4458 x_start
-= self
._indent
4459 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4461 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4464 # should the item show a button?
4465 if item
.HasPlus() and self
.HasButtons():
4467 if self
._imageListButtons
:
4469 # draw the image button here
4472 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4473 if item
.IsSelected():
4474 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4476 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4478 yy
= y_mid
- image_h
/2
4480 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4481 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4482 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4483 dc
.DestroyClippingRegion()
4485 else: # no custom buttons
4487 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4488 # We draw something like the Mac twist buttons
4490 dc
.SetPen(wx
.BLACK_PEN
)
4491 dc
.SetBrush(self
._hilightBrush
)
4492 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4494 if item
.IsExpanded():
4496 button
[0].y
= y_mid
- 3
4498 button
[1].y
= button
[0].y
4500 button
[2].y
= button
[0].y
+ 6
4503 button
[0].y
= y_mid
- 5
4504 button
[1].x
= button
[0].x
4505 button
[1].y
= y_mid
+ 5
4506 button
[2].x
= button
[0].x
+ 5
4509 dc
.DrawPolygon(button
)
4512 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4519 if item
.IsExpanded():
4520 flag |
= _CONTROL_EXPANDED
4521 if item
== self
._underMouse
:
4522 flag |
= _CONTROL_CURRENT
4524 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4526 if item
.IsExpanded():
4528 children
= item
.GetChildren()
4529 count
= len(children
)
4538 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4541 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4543 # draw line down to last child
4544 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4545 if self
.HasButtons():
4548 # Only draw the portion of the line that is visible, in case it is huge
4549 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4550 yOrigin
= abs(yOrigin
)
4551 width
, height
= self
.GetClientSize()
4553 # Move end points to the begining/end of the view?
4556 if oldY
> yOrigin
+ height
:
4557 oldY
= yOrigin
+ height
4559 # after the adjustments if y_mid is larger than oldY then the line
4560 # isn't visible at all so don't draw anything
4562 dc
.SetPen(self
._dottedPen
)
4563 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4568 # -----------------------------------------------------------------------------
4569 # wxWidgets callbacks
4570 # -----------------------------------------------------------------------------
4572 def OnPaint(self
, event
):
4573 """Handles the wx.EVT_PAINT event."""
4575 dc
= wx
.PaintDC(self
)
4578 if not self
._anchor
:
4581 dc
.SetFont(self
._normalFont
)
4582 dc
.SetPen(self
._dottedPen
)
4585 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4588 def OnEraseBackground(self
, event
):
4589 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4591 # Can we actually do something here (or in OnPaint()) To Handle
4592 # background images that are stretchable or always centered?
4593 # I tried but I get enormous flickering...
4595 if not self
._backgroundImage
:
4599 if self
._imageStretchStyle
== _StyleTile
:
4603 dc
= wx
.ClientDC(self
)
4604 rect
= self
.GetUpdateRegion().GetBox()
4605 dc
.SetClippingRect(rect
)
4607 self
.TileBackground(dc
)
4610 def TileBackground(self
, dc
):
4611 """Tiles the background image to fill all the available area."""
4613 sz
= self
.GetClientSize()
4614 w
= self
._backgroundImage
.GetWidth()
4615 h
= self
._backgroundImage
.GetHeight()
4622 while y
< sz
.height
:
4623 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4629 def OnSetFocus(self
, event
):
4630 """Handles the wx.EVT_SET_FOCUS event."""
4632 self
._hasFocus
= True
4633 self
.RefreshSelected()
4637 def OnKillFocus(self
, event
):
4638 """Handles the wx.EVT_KILL_FOCUS event."""
4640 self
._hasFocus
= False
4641 self
.RefreshSelected()
4645 def OnKeyDown(self
, event
):
4646 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4648 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4650 te
.SetEventObject(self
)
4652 if self
.GetEventHandler().ProcessEvent(te
):
4653 # intercepted by the user code
4656 if self
._current
is None or self
._key
_current
is None:
4661 # how should the selection work for this event?
4662 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4666 # * : Expand all/Collapse all
4667 # ' ' | return : activate
4668 # up : go up (not last children!)
4670 # left : go to parent
4671 # right : open if parent and go next
4673 # end : go to last item without opening parents
4674 # alnum : start or continue searching for the item with this prefix
4676 keyCode
= event
.GetKeyCode()
4678 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4679 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4680 self
.Expand(self
._current
)
4682 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4683 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4685 self
.ExpandAll(self
._current
)
4687 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4688 if self
.IsExpanded(self
._current
):
4689 self
.Collapse(self
._current
)
4691 elif keyCode
== wx
.WXK_MENU
:
4692 # Use the item's bounding rectangle to determine position for the event
4693 itemRect
= self
.GetBoundingRect(self
._current
, True)
4694 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4695 event
._item
= self
._current
4696 # Use the left edge, vertical middle
4697 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4698 event
.SetEventObject(self
)
4699 self
.GetEventHandler().ProcessEvent(event
)
4701 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4703 if not self
.IsEnabled(self
._current
):
4707 if not event
.HasModifiers():
4708 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4709 event
._item
= self
._current
4710 event
.SetEventObject(self
)
4711 self
.GetEventHandler().ProcessEvent(event
)
4713 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4714 checked
= not self
.IsItemChecked(self
._current
)
4715 self
.CheckItem(self
._current
, checked
)
4717 # in any case, also generate the normal key event for this key,
4718 # even if we generated the ACTIVATED event above: this is what
4719 # wxMSW does and it makes sense because you might not want to
4720 # process ACTIVATED event at all and handle Space and Return
4721 # directly (and differently) which would be impossible otherwise
4724 # up goes to the previous sibling or to the last
4725 # of its children if it's expanded
4726 elif keyCode
== wx
.WXK_UP
:
4727 prev
= self
.GetPrevSibling(self
._key
_current
)
4729 prev
= self
.GetItemParent(self
._key
_current
)
4730 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4734 current
= self
._key
_current
4735 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4736 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4737 # otherwise we return to where we came from
4738 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4739 self
._key
_current
= prev
4742 current
= self
._key
_current
4744 # We are going to another parent node
4745 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4746 child
= self
.GetLastChild(prev
)
4751 # Try to get the previous siblings and see if they are active
4752 while prev
and not self
.IsEnabled(prev
):
4753 prev
= self
.GetPrevSibling(prev
)
4756 # No previous siblings active: go to the parent and up
4757 prev
= self
.GetItemParent(current
)
4758 while prev
and not self
.IsEnabled(prev
):
4759 prev
= self
.GetItemParent(prev
)
4762 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4763 self
._key
_current
= prev
4765 # left arrow goes to the parent
4766 elif keyCode
== wx
.WXK_LEFT
:
4768 prev
= self
.GetItemParent(self
._current
)
4769 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4770 # don't go to root if it is hidden
4771 prev
= self
.GetPrevSibling(self
._current
)
4773 if self
.IsExpanded(self
._current
):
4774 self
.Collapse(self
._current
)
4776 if prev
and self
.IsEnabled(prev
):
4777 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4779 elif keyCode
== wx
.WXK_RIGHT
:
4780 # this works the same as the down arrow except that we
4781 # also expand the item if it wasn't expanded yet
4782 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4783 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4784 if self
.IsEnabled(child
):
4785 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4786 self
._key
_current
= child
4788 self
.Expand(self
._current
)
4791 elif keyCode
== wx
.WXK_DOWN
:
4792 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4794 child
= self
.GetNextActiveItem(self
._key
_current
)
4797 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4798 self
._key
_current
= child
4802 next
= self
.GetNextSibling(self
._key
_current
)
4805 current
= self
._key
_current
4806 while current
and not next
:
4807 current
= self
.GetItemParent(current
)
4809 next
= self
.GetNextSibling(current
)
4810 if not self
.IsEnabled(next
):
4814 while next
and not self
.IsEnabled(next
):
4815 next
= self
.GetNext(next
)
4818 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4819 self
._key
_current
= next
4822 # <End> selects the last visible tree item
4823 elif keyCode
== wx
.WXK_END
:
4825 last
= self
.GetRootItem()
4827 while last
and self
.IsExpanded(last
):
4829 lastChild
= self
.GetLastChild(last
)
4831 # it may happen if the item was expanded but then all of
4832 # its children have been deleted - so IsExpanded() returned
4833 # true, but GetLastChild() returned invalid item
4839 if last
and self
.IsEnabled(last
):
4841 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4843 # <Home> selects the root item
4844 elif keyCode
== wx
.WXK_HOME
:
4846 prev
= self
.GetRootItem()
4851 if self
.HasFlag(TR_HIDE_ROOT
):
4852 prev
, cookie
= self
.GetFirstChild(prev
)
4856 if self
.IsEnabled(prev
):
4857 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4861 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4862 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4863 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4865 # find the next item starting with the given prefix
4867 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4873 if self
.IsEnabled(id):
4875 self
._findPrefix
+= ch
4877 # also start the timer to reset the current prefix if the user
4878 # doesn't press any more alnum keys soon -- we wouldn't want
4879 # to use this prefix for a new item search
4880 if not self
._findTimer
:
4881 self
._findTimer
= TreeFindTimer(self
)
4883 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4890 def GetNextActiveItem(self
, item
, down
=True):
4891 """Returns the next active item. Used Internally at present. """
4894 sibling
= self
.GetNextSibling
4896 sibling
= self
.GetPrevSibling
4898 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4899 # Is an unchecked radiobutton... all its children are inactive
4900 # try to get the next/previous sibling
4904 child
= sibling(item
)
4905 if (child
and self
.IsEnabled(child
)) or not child
:
4910 # Tha's not a radiobutton... but some of its children can be
4912 child
, cookie
= self
.GetFirstChild(item
)
4913 while child
and not self
.IsEnabled(child
):
4914 child
, cookie
= self
.GetNextChild(item
, cookie
)
4916 if child
and self
.IsEnabled(child
):
4922 def HitTest(self
, point
, flags
=0):
4924 Calculates which (if any) item is under the given point, returning the tree item
4925 at this point plus extra information flags. Flags is a bitlist of the following:
4927 TREE_HITTEST_ABOVE above the client area
4928 TREE_HITTEST_BELOW below the client area
4929 TREE_HITTEST_NOWHERE no item has been hit
4930 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4931 TREE_HITTEST_ONITEMICON on the icon associated to an item
4932 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4933 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4934 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4935 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4936 TREE_HITTEST_TOLEFT on the left of the client area
4937 TREE_HITTEST_TORIGHT on the right of the client area
4938 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4939 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4940 TREE_HITTEST_ONITEM anywhere on the item
4942 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4945 w
, h
= self
.GetSize()
4949 flags |
= TREE_HITTEST_TOLEFT
4951 flags |
= TREE_HITTEST_TORIGHT
4953 flags |
= TREE_HITTEST_ABOVE
4955 flags |
= TREE_HITTEST_BELOW
4960 if self
._anchor
== None:
4961 flags
= TREE_HITTEST_NOWHERE
4964 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
4967 flags
= TREE_HITTEST_NOWHERE
4970 if not self
.IsEnabled(hit
):
4976 def GetBoundingRect(self
, item
, textOnly
=False):
4977 """Gets the bounding rectangle of the item."""
4980 raise "\nERROR: Invalid Tree Item. "
4984 startX
, startY
= self
.GetViewStart()
4987 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
4988 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
4989 rect
.width
= i
.GetWidth()
4990 rect
.height
= self
.GetLineHeight(i
)
4995 def Edit(self
, item
):
4997 Internal function. Starts the editing of an item label, sending a
4998 EVT_TREE_BEGIN_LABEL_EDIT event.
5001 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5003 te
.SetEventObject(self
)
5004 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5008 # We have to call this here because the label in
5009 # question might just have been added and no screen
5010 # update taken place.
5012 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5017 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5018 self
._textCtrl
.StopEditing()
5020 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5021 self
._textCtrl
.SetFocus()
5024 def GetEditControl(self
):
5026 Returns a pointer to the edit TextCtrl if the item is being edited or
5027 None otherwise (it is assumed that no more than one item may be edited
5031 return self
._textCtrl
5034 def OnRenameAccept(self
, item
, value
):
5036 Called by TreeTextCtrl, to accept the changes and to send the
5037 EVT_TREE_END_LABEL_EDIT event.
5040 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5042 le
.SetEventObject(self
)
5044 le
._editCancelled
= False
5046 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5049 def OnRenameCancelled(self
, item
):
5051 Called by TreeTextCtrl, to cancel the changes and to send the
5052 EVT_TREE_END_LABEL_EDIT event.
5055 # let owner know that the edit was cancelled
5056 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5058 le
.SetEventObject(self
)
5060 le
._editCancelled
= True
5062 self
.GetEventHandler().ProcessEvent(le
)
5065 def OnRenameTimer(self
):
5066 """The timer for renaming has expired. Start editing."""
5068 self
.Edit(self
._current
)
5071 def OnMouse(self
, event
):
5072 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5074 if not self
._anchor
:
5077 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5079 # Is the mouse over a tree item button?
5081 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5082 underMouse
= thisItem
5083 underMouseChanged
= underMouse
!= self
._underMouse
5085 if underMouse
and (flags
& TREE_HITTEST_ONITEMBUTTON
) and not event
.LeftIsDown() and \
5086 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5087 underMouse
= underMouse
5091 if underMouse
!= self
._underMouse
:
5092 if self
._underMouse
:
5093 # unhighlight old item
5094 self
._underMouse
= None
5096 self
._underMouse
= underMouse
5098 # Determines what item we are hovering over and need a tooltip for
5099 hoverItem
= thisItem
5101 # We do not want a tooltip if we are dragging, or if the rename timer is running
5102 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5104 if hoverItem
is not None:
5105 # Ask the tree control what tooltip (if any) should be shown
5106 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5107 hevent
._item
= hoverItem
5108 hevent
.SetEventObject(self
)
5110 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5111 self
.SetToolTip(hevent
._label
)
5113 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5114 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5115 self
._isonhyperlink
= True
5117 if self
._isonhyperlink
:
5118 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5119 self
._isonhyperlink
= False
5121 # we process left mouse up event (enables in-place edit), right down
5122 # (pass to the user code), left dbl click (activate item) and
5123 # dragging/moving events for items drag-and-drop
5125 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5126 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5132 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5134 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5136 if self
._dragCount
== 0:
5137 self
._dragStart
= pt
5140 self
._dragCount
= self
._dragCount
+ 1
5142 if self
._dragCount
!= 3:
5143 # wait until user drags a bit further...
5146 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5148 nevent
= TreeEvent(command
, self
.GetId())
5149 nevent
._item
= self
._current
5150 nevent
.SetEventObject(self
)
5151 newpt
= self
.CalcScrolledPosition(pt
)
5152 nevent
.SetPoint(newpt
)
5154 # by default the dragging is not supported, the user code must
5155 # explicitly allow the event for it to take place
5158 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5160 # we're going to drag this item
5161 self
._isDragging
= True
5163 # remember the old cursor because we will change it while
5165 self
._oldCursor
= self
._cursor
5167 # in a single selection control, hide the selection temporarily
5168 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5169 self
._oldSelection
= self
.GetSelection()
5171 if self
._oldSelection
:
5173 self
._oldSelection
.SetHilight(False)
5174 self
.RefreshLine(self
._oldSelection
)
5176 selections
= self
.GetSelections()
5177 if len(selections
) == 1:
5178 self
._oldSelection
= selections
[0]
5179 self
._oldSelection
.SetHilight(False)
5180 self
.RefreshLine(self
._oldSelection
)
5185 # Create the custom draw image from the icons and the text of the item
5186 self
._dragImage
= DragImage(self
, self
._current
)
5187 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5188 self
._dragImage
.Show()
5189 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5191 elif event
.Dragging() and self
._isDragging
:
5193 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5195 if self
._countDrag
== 0 and item
:
5196 self
._oldItem
= item
5198 if item
!= self
._dropTarget
:
5200 # unhighlight the previous drop target
5201 if self
._dropTarget
:
5202 self
._dropTarget
.SetHilight(False)
5203 self
.RefreshLine(self
._dropTarget
)
5205 item
.SetHilight(True)
5206 self
.RefreshLine(item
)
5207 self
._countDrag
= self
._countDrag
+ 1
5208 self
._dropTarget
= item
5212 if self
._countDrag
>= 3:
5213 # Here I am trying to avoid ugly repainting problems... hope it works
5214 self
.RefreshLine(self
._oldItem
)
5217 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5220 self
._dragImage
.EndDrag()
5222 if self
._dropTarget
:
5223 self
._dropTarget
.SetHilight(False)
5225 if self
._oldSelection
:
5227 self
._oldSelection
.SetHilight(True)
5228 self
.RefreshLine(self
._oldSelection
)
5229 self
._oldSelection
= None
5231 # generate the drag end event
5232 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5234 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5235 event
.SetEventObject(self
)
5237 self
.GetEventHandler().ProcessEvent(event
)
5239 self
._isDragging
= False
5240 self
._dropTarget
= None
5242 self
.SetCursor(self
._oldCursor
)
5244 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5247 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5252 # If we got to this point, we are not dragging or moving the mouse.
5253 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5254 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5255 # We skip even if we didn't hit an item because we still should
5256 # restore focus to the tree control even if we didn't exactly hit an item.
5257 if event
.LeftDown():
5258 self
._hasFocus
= True
5259 self
.SetFocusIgnoringChildren()
5262 # here we process only the messages which happen on tree items
5267 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5268 self
._textCtrl
.StopEditing()
5269 return # we hit the blank area
5271 if event
.RightDown():
5273 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5274 self
._textCtrl
.StopEditing()
5276 self
._hasFocus
= True
5277 self
.SetFocusIgnoringChildren()
5279 # If the item is already selected, do not update the selection.
5280 # Multi-selections should not be cleared if a selected item is clicked.
5281 if not self
.IsSelected(item
):
5283 self
.DoSelectItem(item
, True, False)
5285 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5287 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5288 nevent
.SetEventObject(self
)
5289 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5291 # Consistent with MSW (for now), send the ITEM_MENU *after*
5292 # the RIGHT_CLICK event. TODO: This behaviour may change.
5293 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5294 nevent2
._item
= item
5295 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5296 nevent2
.SetEventObject(self
)
5297 self
.GetEventHandler().ProcessEvent(nevent2
)
5299 elif event
.LeftUp():
5301 # this facilitates multiple-item drag-and-drop
5303 if self
.HasFlag(TR_MULTIPLE
):
5305 selections
= self
.GetSelections()
5307 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5309 self
.DoSelectItem(item
, True, False)
5311 if self
._lastOnSame
:
5313 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5315 if self
._renameTimer
:
5317 if self
._renameTimer
.IsRunning():
5319 self
._renameTimer
.Stop()
5323 self
._renameTimer
= TreeRenameTimer(self
)
5325 self
._renameTimer
.Start(_DELAY
, True)
5327 self
._lastOnSame
= False
5330 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5332 if not item
or not item
.IsEnabled():
5333 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5334 self
._textCtrl
.StopEditing()
5337 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5338 self
._textCtrl
.StopEditing()
5340 self
._hasFocus
= True
5341 self
.SetFocusIgnoringChildren()
5343 if event
.LeftDown():
5345 self
._lastOnSame
= item
== self
._current
5347 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5349 # only toggle the item for a single click, double click on
5350 # the button doesn't do anything (it toggles the item twice)
5351 if event
.LeftDown():
5355 # don't select the item if the button was clicked
5358 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5360 if event
.LeftDown():
5362 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5366 # clear the previously selected items, if the
5367 # user clicked outside of the present selection.
5368 # otherwise, perform the deselection on mouse-up.
5369 # this allows multiple drag and drop to work.
5370 # but if Cmd is down, toggle selection of the clicked item
5371 if not self
.IsSelected(item
) or event
.CmdDown():
5373 if flags
& TREE_HITTEST_ONITEM
:
5374 # how should the selection work for this event?
5375 if item
.IsHyperText():
5376 self
.SetItemVisited(item
, True)
5378 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5382 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5384 # For some reason, Windows isn't recognizing a left double-click,
5385 # so we need to simulate it here. Allow 200 milliseconds for now.
5386 if event
.LeftDClick():
5388 # double clicking should not start editing the item label
5389 if self
._renameTimer
:
5390 self
._renameTimer
.Stop()
5392 self
._lastOnSame
= False
5394 # send activate event first
5395 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5397 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5398 nevent
.SetEventObject(self
)
5399 if not self
.GetEventHandler().ProcessEvent(nevent
):
5401 # if the user code didn't process the activate event,
5402 # handle it ourselves by toggling the item when it is
5404 ## if item.HasPlus():
5408 def OnInternalIdle(self
, event
):
5409 """Performs operations in idle time (essentially drawing)."""
5411 # Check if we need to select the root item
5412 # because nothing else has been selected.
5413 # Delaying it means that we can invoke event handlers
5414 # as required, when a first item is selected.
5415 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5418 self
.SelectItem(self
._select
_me
)
5419 elif self
.GetRootItem():
5420 self
.SelectItem(self
.GetRootItem())
5422 # after all changes have been done to the tree control,
5423 # we actually redraw the tree when everything is over
5427 if self
._freezeCount
:
5432 self
.CalculatePositions()
5434 self
.AdjustMyScrollbars()
5439 def CalculateSize(self
, item
, dc
):
5440 """Calculates overall position and size of an item."""
5442 attr
= item
.GetAttributes()
5444 if attr
and attr
.HasFont():
5445 dc
.SetFont(attr
.GetFont())
5447 dc
.SetFont(self
._boldFont
)
5449 dc
.SetFont(self
._normalFont
)
5451 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5454 # restore normal font
5455 dc
.SetFont(self
._normalFont
)
5457 image_w
, image_h
= 0, 0
5458 image
= item
.GetCurrentImage()
5460 if image
!= _NO_IMAGE
:
5462 if self
._imageListNormal
:
5464 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5467 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5469 checkimage
= item
.GetCurrentCheckedImage()
5470 if checkimage
is not None:
5471 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5477 total_h
+= 2 # at least 2 pixels
5479 total_h
+= total_h
/10 # otherwise 10% extra spacing
5481 if total_h
> self
._lineHeight
:
5482 self
._lineHeight
= total_h
5484 if not item
.GetWindow():
5485 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5486 item
.SetHeight(total_h
)
5488 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5491 def CalculateLevel(self
, item
, dc
, level
, y
):
5492 """Calculates the level of an item."""
5494 x
= level
*self
._indent
5496 if not self
.HasFlag(TR_HIDE_ROOT
):
5502 # a hidden root is not evaluated, but its
5503 # children are always calculated
5504 children
= item
.GetChildren()
5505 count
= len(children
)
5507 for n
in xrange(count
):
5508 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5512 self
.CalculateSize(item
, dc
)
5515 item
.SetX(x
+self
._spacing
)
5517 y
+= self
.GetLineHeight(item
)
5519 if not item
.IsExpanded():
5520 # we don't need to calculate collapsed branches
5523 children
= item
.GetChildren()
5524 count
= len(children
)
5526 for n
in xrange(count
):
5527 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5532 def CalculatePositions(self
):
5533 """Calculates all the positions of the visible items."""
5535 if not self
._anchor
:
5538 dc
= wx
.ClientDC(self
)
5541 dc
.SetFont(self
._normalFont
)
5542 dc
.SetPen(self
._dottedPen
)
5544 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5547 def RefreshSubtree(self
, item
):
5548 """Refreshes a damaged subtree of an item."""
5552 if self
._freezeCount
:
5555 client
= self
.GetClientSize()
5558 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5559 rect
.width
= client
.x
5560 rect
.height
= client
.y
5562 self
.Refresh(True, rect
)
5563 self
.AdjustMyScrollbars()
5566 def RefreshLine(self
, item
):
5567 """Refreshes a damaged item line."""
5571 if self
._freezeCount
:
5575 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5576 rect
.width
= self
.GetClientSize().x
5577 rect
.height
= self
.GetLineHeight(item
)
5579 self
.Refresh(True, rect
)
5582 def RefreshSelected(self
):
5583 """Refreshes a damaged selected item line."""
5585 if self
._freezeCount
:
5588 # TODO: this is awfully inefficient, we should keep the list of all
5589 # selected items internally, should be much faster
5591 self
.RefreshSelectedUnder(self
._anchor
)
5594 def RefreshSelectedUnder(self
, item
):
5595 """Refreshes the selected items under the given item."""
5597 if self
._freezeCount
:
5600 if item
.IsSelected():
5601 self
.RefreshLine(item
)
5603 children
= item
.GetChildren()
5604 for child
in children
:
5605 self
.RefreshSelectedUnder(child
)
5609 """Freeze CustomTreeCtrl."""
5611 self
._freezeCount
= self
._freezeCount
+ 1
5615 """Thaw CustomTreeCtrl."""
5617 if self
._freezeCount
== 0:
5618 raise "\nERROR: Thawing Unfrozen Tree Control?"
5620 self
._freezeCount
= self
._freezeCount
- 1
5622 if not self
._freezeCount
:
5626 # ----------------------------------------------------------------------------
5627 # changing colours: we need to refresh the tree control
5628 # ----------------------------------------------------------------------------
5630 def SetBackgroundColour(self
, colour
):
5631 """Changes the background colour of CustomTreeCtrl."""
5633 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5636 if self
._freezeCount
:
5644 def SetForegroundColour(self
, colour
):
5645 """Changes the foreground colour of CustomTreeCtrl."""
5647 if not wx
.Window
.SetForegroundColour(self
, colour
):
5650 if self
._freezeCount
:
5658 def OnGetToolTip(self
, event
):
5660 Process the tooltip event, to speed up event processing. Does not actually
5667 def DoGetBestSize(self
):
5668 """Something is better than nothing..."""
5670 # something is better than nothing...
5671 # 100x80 is what the MSW version will get from the default
5672 # wxControl::DoGetBestSize
5674 return wx
.Size(100, 80)
5677 def GetClassDefaultAttributes(self
):
5678 """Gets the class default attributes."""
5680 attr
= wx
.VisualAttributes()
5681 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5682 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5683 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)