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 wx
.TextCtrl
.__init
__(self
, self
._owner
, wx
.ID_ANY
, self
._startValue
,
946 wx
.Point(x
- 4, y
), wx
.Size(w
+ 15, h
))
947 if wx
.Platform
== "__WXMAC__":
948 self
.SetFont(owner
.GetFont())
949 bs
= self
.GetBestSize()
950 self
.SetSize((-1, bs
.height
))
952 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
953 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
954 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
957 def AcceptChanges(self
):
958 """Accepts/refuses the changes made by the user."""
960 value
= self
.GetValue()
962 if value
== self
._startValue
:
963 # nothing changed, always accept
964 # when an item remains unchanged, the owner
965 # needs to be notified that the user decided
966 # not to change the tree item label, and that
967 # the edit has been cancelled
968 self
._owner
.OnRenameCancelled(self
._itemEdited
)
971 if not self
._owner
.OnRenameAccept(self
._itemEdited
, value
):
975 # accepted, do rename the item
976 self
._owner
.SetItemText(self
._itemEdited
, value
)
982 """Finish editing."""
984 if not self
._finished
:
986 ## wxPendingDelete.Append(this)
987 self
._finished
= True
988 self
._owner
.SetFocusIgnoringChildren()
989 self
._owner
.ResetTextControl()
992 def OnChar(self
, event
):
993 """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
995 keycode
= event
.GetKeyCode()
997 if keycode
== wx
.WXK_RETURN
:
998 self
._aboutToFinish
= True
999 # Notify the owner about the changes
1000 self
.AcceptChanges()
1001 # Even if vetoed, close the control (consistent with MSW)
1002 wx
.CallAfter(self
.Finish
)
1004 elif keycode
== wx
.WXK_ESCAPE
:
1011 def OnKeyUp(self
, event
):
1012 """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
1014 if not self
._finished
:
1016 # auto-grow the textctrl:
1017 parentSize
= self
._owner
.GetSize()
1018 myPos
= self
.GetPosition()
1019 mySize
= self
.GetSize()
1021 sx
, sy
= self
.GetTextExtent(self
.GetValue() + "M")
1022 if myPos
.x
+ sx
> parentSize
.x
:
1023 sx
= parentSize
.x
- myPos
.x
1027 self
.SetSize((sx
, -1))
1032 def OnKillFocus(self
, event
):
1033 """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
1035 # I commented out those lines, and everything seems to work fine.
1036 # But why in the world are these lines of code here? Maybe GTK
1037 # or MAC give troubles?
1039 ## if not self._finished and not self._aboutToFinish:
1041 ## # We must finish regardless of success, otherwise we'll get
1042 ## # focus problems:
1044 ## if not self.AcceptChanges():
1045 ## self._owner.OnRenameCancelled(self._itemEdited)
1047 # We must let the native text control handle focus, too, otherwise
1048 # it could have problems with the cursor (e.g., in wxGTK).
1052 def StopEditing(self
):
1053 """Suddenly stops the editing."""
1055 self
._owner
.OnRenameCancelled(self
._itemEdited
)
1060 """Returns the item currently edited."""
1062 return self
._itemEdited
1065 # -----------------------------------------------------------------------------
1066 # Auxiliary Classes: TreeFindTimer
1067 # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
1068 # Sufficiently Long Time.
1069 # -----------------------------------------------------------------------------
1071 class TreeFindTimer(wx
.Timer
):
1073 Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
1074 for a sufficiently long time.
1077 def __init__(self
, owner
):
1079 Default class constructor.
1080 For internal use: do not call it in your code!
1083 wx
.Timer
.__init
__(self
)
1088 """The timer has expired."""
1090 self
._owner
._findPrefix
= ""
1093 # -----------------------------------------------------------------------------
1094 # GenericTreeItem Implementation.
1095 # This Class Holds All The Information And Methods For Every Single Item In
1097 # -----------------------------------------------------------------------------
1099 class GenericTreeItem
:
1101 This class holds all the information and methods for every single item in
1102 CustomTreeCtrl. No wx based.
1105 def __init__(self
, parent
, text
="", ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
1107 Default class constructor.
1108 For internal use: do not call it in your code!
1111 # since there can be very many of these, we save size by chosing
1112 # the smallest representation for the elements and by ordering
1113 # the members to avoid padding.
1114 self
._text
= text
# label to be rendered for item
1115 self
._data
= data
# user-provided data
1117 self
._children
= [] # list of children
1118 self
._parent
= parent
# parent of this item
1120 self
._attr
= None # attributes???
1122 # tree ctrl images for the normal, selected, expanded and
1123 # expanded+selected states
1124 self
._images
= [-1, -1, -1, -1]
1125 self
._images
[TreeItemIcon_Normal
] = image
1126 self
._images
[TreeItemIcon_Selected
] = selImage
1127 self
._images
[TreeItemIcon_Expanded
] = _NO_IMAGE
1128 self
._images
[TreeItemIcon_SelectedExpanded
] = _NO_IMAGE
1130 self
._checkedimages
= [None, None, None, None]
1132 self
._x
= 0 # (virtual) offset from top
1133 self
._y
= 0 # (virtual) offset from left
1134 self
._width
= 0 # width of this item
1135 self
._height
= 0 # height of this item
1137 self
._isCollapsed
= True
1138 self
._hasHilight
= False # same as focused
1139 self
._hasPlus
= False # used for item which doesn't have
1140 # children but has a [+] button
1141 self
._isBold
= False # render the label in bold font
1142 self
._isItalic
= False # render the label in italic font
1143 self
._ownsAttr
= False # delete attribute when done
1144 self
._type
= ct_type
# item type: 0=normal, 1=check, 2=radio
1145 self
._checked
= False # only meaningful for check and radio
1146 self
._enabled
= True # flag to enable/disable an item
1147 self
._hypertext
= False # indicates if the item is hypertext
1148 self
._visited
= False # visited state for an hypertext item
1151 # do not construct the array for normal items
1152 self
._checkedimages
[TreeItemIcon_Checked
] = 0
1153 self
._checkedimages
[TreeItemIcon_NotChecked
] = 1
1154 self
._checkedimages
[TreeItemIcon_Flagged
] = 2
1155 self
._checkedimages
[TreeItemIcon_NotFlagged
] = 3
1158 if parent
.GetType() == 2 and not parent
.IsChecked():
1159 # if the node parent is a radio not enabled, we are disabled
1160 self
._enabled
= False
1162 self
._wnd
= wnd
# are we holding a window?
1165 if wnd
.GetSizer(): # the window is a complex one hold by a sizer
1166 size
= wnd
.GetBestSize()
1167 else: # simple window, without sizers
1168 size
= wnd
.GetSize()
1170 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1171 # No other solution to handle the focus changing from an item in
1172 # CustomTreeCtrl and the window associated to an item
1173 # Do better strategies exist?
1174 self
._wnd
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1176 self
._height
= size
.GetHeight() + 2
1177 self
._width
= size
.GetWidth()
1178 self
._windowsize
= size
1180 # We don't show the window if the item is collapsed
1181 if self
._isCollapsed
:
1182 self
._wnd
.Show(False)
1184 # The window is enabled only if the item is enabled
1185 self
._wnd
.Enable(self
._enabled
)
1186 self
._windowenabled
= self
._enabled
1191 Returns whether the item is ok or not. Useless on Python, but added for
1192 backward compatibility with the C++ implementation.
1198 def GetChildren(self
):
1199 """Returns the item's children."""
1201 return self
._children
1205 """Returns the item text."""
1210 def GetImage(self
, which
=TreeItemIcon_Normal
):
1211 """Returns the item image for a particular state."""
1213 return self
._images
[which
]
1216 def GetCheckedImage(self
, which
=TreeItemIcon_Checked
):
1217 """Returns the item check image. Meaningful only for radio & check items."""
1219 return self
._checkedimages
[which
]
1223 """Returns the data associated to this item."""
1228 def SetImage(self
, image
, which
):
1229 """Sets the item image."""
1231 self
._images
[which
] = image
1234 def SetData(self
, data
):
1235 """Sets the data associated to this item."""
1240 def SetHasPlus(self
, has
=True):
1241 """Sets whether an item has the 'plus' button."""
1246 def SetBold(self
, bold
):
1247 """Sets the item font bold."""
1252 def SetItalic(self
, italic
):
1253 """Sets the item font italic."""
1255 self
._isItalic
= italic
1259 """Returns the x position on an item in the ScrolledWindow."""
1265 """Returns the y position on an item in the ScrolledWindow."""
1271 """Sets the x position on an item in the ScrolledWindow."""
1277 """Sets the y position on an item in the ScrolledWindow."""
1282 def GetHeight(self
):
1283 """Returns the height of the item."""
1289 """Returns the width of the item."""
1294 def SetHeight(self
, h
):
1295 """Sets the height of the item."""
1300 def SetWidth(self
, w
):
1301 """Sets the width of the item."""
1306 def SetWindow(self
, wnd
):
1307 """Sets the window associated to the item."""
1312 def GetWindow(self
):
1313 """Returns the window associated to the item."""
1318 def GetWindowEnabled(self
):
1319 """Returns whether the associated window is enabled or not."""
1322 raise "\nERROR: This Item Has No Window Associated"
1324 return self
._windowenabled
1327 def SetWindowEnabled(self
, enable
=True):
1328 """Sets whether the associated window is enabled or not."""
1331 raise "\nERROR: This Item Has No Window Associated"
1333 self
._windowenabled
= enable
1334 self
._wnd
.Enable(enable
)
1337 def GetWindowSize(self
):
1338 """Returns the associated window size."""
1340 return self
._windowsize
1343 def OnSetFocus(self
, event
):
1344 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1346 treectrl
= self
._wnd
.GetParent()
1347 select
= treectrl
.GetSelection()
1349 # If the window is associated to an item that currently is selected
1350 # (has focus) we don't kill the focus. Otherwise we do it.
1352 treectrl
._hasFocus
= False
1354 treectrl
._hasFocus
= True
1361 Returns the item type. It should be one of:
1370 def SetHyperText(self
, hyper
=True):
1371 """Sets whether the item is hypertext or not."""
1373 self
._hypertext
= hyper
1376 def SetVisited(self
, visited
=True):
1377 """Sets whether an hypertext item was visited or not."""
1379 self
._visited
= visited
1382 def GetVisited(self
):
1383 """Returns whether an hypertext item was visited or not."""
1385 return self
._visited
1388 def IsHyperText(self
):
1389 """Returns whether the item is hypetext or not."""
1391 return self
._hypertext
1394 def GetParent(self
):
1395 """Gets the item parent."""
1400 def Insert(self
, child
, index
):
1401 """Inserts an item in the item children."""
1403 self
._children
.insert(index
, child
)
1407 """Expand the item."""
1409 self
._isCollapsed
= False
1413 """Collapse the item."""
1415 self
._isCollapsed
= True
1418 def SetHilight(self
, set=True):
1419 """Sets the item focus/unfocus."""
1421 self
._hasHilight
= set
1424 def HasChildren(self
):
1425 """Returns whether the item has children or not."""
1427 return len(self
._children
) > 0
1430 def IsSelected(self
):
1431 """Returns whether the item is selected or not."""
1433 return self
._hasHilight
!= 0
1436 def IsExpanded(self
):
1437 """Returns whether the item is expanded or not."""
1439 return not self
._isCollapsed
1442 def IsChecked(self
):
1443 """Returns whether the item is checked or not."""
1445 return self
._checked
1448 def Check(self
, checked
=True):
1449 """Check an item. Meaningful only for check and radio items."""
1451 self
._checked
= checked
1455 """Returns whether the item has the plus button or not."""
1457 return self
._hasPlus
or self
.HasChildren()
1461 """Returns whether the item font is bold or not."""
1463 return self
._isBold
!= 0
1467 """Returns whether the item font is italic or not."""
1469 return self
._isItalic
!= 0
1472 def Enable(self
, enable
=True):
1473 """Enables/disables the item."""
1475 self
._enabled
= enable
1478 def IsEnabled(self
):
1479 """Returns whether the item is enabled or not."""
1481 return self
._enabled
1484 def GetAttributes(self
):
1485 """Returns the item attributes (font, colours)."""
1491 """Creates a new attribute (font, colours)."""
1495 self
._attr
= TreeItemAttr()
1496 self
._ownsAttr
= True
1501 def SetAttributes(self
, attr
):
1502 """Sets the item attributes (font, colours)."""
1508 self
._ownsAttr
= False
1511 def AssignAttributes(self
, attr
):
1512 """Assigns the item attributes (font, colours)."""
1514 self
.SetAttributes(attr
)
1515 self
._ownsAttr
= True
1518 def DeleteChildren(self
, tree
):
1519 """Deletes the item children."""
1521 for child
in self
._children
:
1523 tree
.SendDeleteEvent(child
)
1525 child
.DeleteChildren(tree
)
1527 if child
== tree
._select
_me
:
1528 tree
._select
_me
= None
1530 # We have to destroy the associated window
1531 wnd
= child
.GetWindow()
1536 if child
in tree
._itemWithWindow
:
1537 tree
._itemWithWindow
.remove(child
)
1544 def SetText(self
, text
):
1545 """Sets the item text."""
1550 def GetChildrenCount(self
, recursively
=True):
1551 """Gets the number of children."""
1553 count
= len(self
._children
)
1560 for n
in xrange(count
):
1561 total
+= self
._children
[n
].GetChildrenCount()
1566 def GetSize(self
, x
, y
, theButton
):
1567 """Returns the item size."""
1569 bottomY
= self
._y
+ theButton
.GetLineHeight(self
)
1574 width
= self
._x
+ self
._width
1579 if self
.IsExpanded():
1580 for child
in self
._children
:
1581 x
, y
= child
.GetSize(x
, y
, theButton
)
1586 def HitTest(self
, point
, theCtrl
, flags
=0, level
=0):
1588 HitTest method for an item. Called from the main window HitTest.
1589 see the CustomTreeCtrl HitTest method for the flags explanation.
1592 # for a hidden root node, don't evaluate it, but do evaluate children
1593 if not (level
== 0 and theCtrl
.HasFlag(TR_HIDE_ROOT
)):
1596 h
= theCtrl
.GetLineHeight(self
)
1598 if point
.y
> self
._y
and point
.y
< self
._y
+ h
:
1600 y_mid
= self
._y
+ h
/2
1603 flags |
= TREE_HITTEST_ONITEMUPPERPART
1605 flags |
= TREE_HITTEST_ONITEMLOWERPART
1607 xCross
= self
._x
- theCtrl
.GetSpacing()
1609 if wx
.Platform
== "__WXMAC__":
1610 # according to the drawing code the triangels are drawn
1611 # at -4 , -4 from the position up to +10/+10 max
1612 if point
.x
> xCross
-4 and point
.x
< xCross
+10 and point
.y
> y_mid
-4 and \
1613 point
.y
< y_mid
+10 and self
.HasPlus() and theCtrl
.HasButtons():
1615 flags |
= TREE_HITTEST_ONITEMBUTTON
1618 # 5 is the size of the plus sign
1619 if point
.x
> xCross
-6 and point
.x
< xCross
+6 and point
.y
> y_mid
-6 and \
1620 point
.y
< y_mid
+6 and self
.HasPlus() and theCtrl
.HasButtons():
1622 flags |
= TREE_HITTEST_ONITEMBUTTON
1625 if point
.x
>= self
._x
and point
.x
<= self
._x
+ self
._width
:
1630 # assuming every image (normal and selected) has the same size!
1631 if self
.GetImage() != _NO_IMAGE
and theCtrl
._imageListNormal
:
1632 image_w
, image_h
= theCtrl
._imageListNormal
.GetSize(self
.GetImage())
1634 if self
.GetCheckedImage() is not None:
1635 wcheck
, hcheck
= theCtrl
._imageListCheck
.GetSize(self
.GetCheckedImage())
1637 if wcheck
and point
.x
<= self
._x
+ wcheck
+ 1:
1638 flags |
= TREE_HITTEST_ONITEMCHECKICON
1641 if image_w
!= -1 and point
.x
<= self
._x
+ wcheck
+ image_w
+ 1:
1642 flags |
= TREE_HITTEST_ONITEMICON
1644 flags |
= TREE_HITTEST_ONITEMLABEL
1648 if point
.x
< self
._x
:
1649 flags |
= TREE_HITTEST_ONITEMINDENT
1650 if point
.x
> self
._x
+ self
._width
:
1651 flags |
= TREE_HITTEST_ONITEMRIGHT
1655 # if children are expanded, fall through to evaluate them
1656 if self
._isCollapsed
:
1660 for child
in self
._children
:
1661 res
, flags
= child
.HitTest(point
, theCtrl
, flags
, level
+ 1)
1668 def GetCurrentImage(self
):
1669 """Returns the current item image."""
1673 if self
.IsExpanded():
1675 if self
.IsSelected():
1677 image
= self
.GetImage(TreeItemIcon_SelectedExpanded
)
1679 if image
== _NO_IMAGE
:
1681 # we usually fall back to the normal item, but try just the
1682 # expanded one (and not selected) first in this case
1683 image
= self
.GetImage(TreeItemIcon_Expanded
)
1685 else: # not expanded
1687 if self
.IsSelected():
1688 image
= self
.GetImage(TreeItemIcon_Selected
)
1690 # maybe it doesn't have the specific image we want,
1691 # try the default one instead
1692 if image
== _NO_IMAGE
:
1693 image
= self
.GetImage()
1698 def GetCurrentCheckedImage(self
):
1699 """Returns the current item check image."""
1704 if self
.IsChecked():
1705 if self
._type
== 1: # Checkbox
1706 return self
._checkedimages
[TreeItemIcon_Checked
]
1708 return self
._checkedimages
[TreeItemIcon_Flagged
]
1710 if self
._type
== 1: # Checkbox
1711 return self
._checkedimages
[TreeItemIcon_NotChecked
]
1713 return self
._checkedimages
[TreeItemIcon_NotFlagged
]
1716 def EventFlagsToSelType(style
, shiftDown
=False, ctrlDown
=False):
1718 Translate the key or mouse event flag to the type of selection we
1722 is_multiple
= (style
& TR_MULTIPLE
) != 0
1723 extended_select
= shiftDown
and is_multiple
1724 unselect_others
= not (extended_select
or (ctrlDown
and is_multiple
))
1726 return is_multiple
, extended_select
, unselect_others
1729 # -----------------------------------------------------------------------------
1730 # CustomTreeCtrl Main Implementation.
1731 # This Is The Main Class.
1732 # -----------------------------------------------------------------------------
1734 class CustomTreeCtrl(wx
.PyScrolledWindow
):
1736 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1737 style
=0, ctstyle
=TR_DEFAULT_STYLE
, validator
=wx
.DefaultValidator
,
1738 name
="CustomTreeCtrl"):
1740 Default class constructor.
1742 parent: parent window. Must not be none.
1744 id: window identifier. A value of -1 indicates a default value.
1746 pos: window position.
1748 size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
1750 style: the underlying wx.ScrolledWindow style
1752 ctstyle: CustomTreeCtrl window style. This can be one of:
1754 TR_HAS_BUTTONS # draw collapsed/expanded btns
1755 TR_NO_LINES # don't draw lines at all
1756 TR_LINES_AT_ROOT # connect top-level nodes
1757 TR_TWIST_BUTTONS # draw mac-like twist buttons
1758 TR_SINGLE # single selection mode
1759 TR_MULTIPLE # can select multiple items
1760 TR_EXTENDED # todo: allow extended selection
1761 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1762 TR_EDIT_LABELS # can edit item labels
1763 TR_ROW_LINES # put border around items
1764 TR_HIDE_ROOT # don't display root node
1765 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1766 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1767 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1769 validator: window validator.
1774 self
._current
= self
._key
_current
= self
._anchor
= self
._select
_me
= None
1775 self
._hasFocus
= False
1778 # Default line height: it will soon be changed
1779 self
._lineHeight
= 10
1780 # Item indent wrt parent
1782 # item horizontal spacing between the start and the text
1785 # Brushes for focused/unfocused items (also gradient type)
1786 self
._hilightBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
))
1787 btnshadow
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1788 self
._hilightUnfocusedBrush
= wx
.Brush(btnshadow
)
1789 r
, g
, b
= btnshadow
.Red(), btnshadow
.Green(), btnshadow
.Blue()
1790 backcolour
= ((r
>> 1) - 20, (g
>> 1) - 20, (b
>> 1) - 20)
1791 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1792 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1794 # image list for icons
1795 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1796 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1798 # Drag and drop initial settings
1801 self
._isDragging
= False
1802 self
._dropTarget
= self
._oldSelection
= None
1803 self
._dragImage
= None
1804 self
._underMouse
= None
1806 # TextCtrl initial settings for editable items
1807 self
._textCtrl
= None
1808 self
._renameTimer
= None
1810 # This one allows us to handle Freeze() and Thaw() calls
1811 self
._freezeCount
= 0
1813 self
._findPrefix
= ""
1814 self
._findTimer
= None
1816 self
._dropEffectAboveItem
= False
1817 self
._lastOnSame
= False
1819 # Default normal and bold fonts for an item
1820 self
._hasFont
= True
1821 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1822 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1823 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1824 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1828 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1829 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1830 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1831 self
._hypertextnewcolour
= wx
.BLUE
1832 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1833 self
._isonhyperlink
= False
1835 # Default CustomTreeCtrl background colour.
1836 self
._backgroundColour
= wx
.WHITE
1838 # Background image settings
1839 self
._backgroundImage
= None
1840 self
._imageStretchStyle
= _StyleTile
1842 # Disabled items colour
1843 self
._disabledColour
= wx
.Colour(180, 180, 180)
1845 # Gradient selection colours
1846 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1847 self
._secondcolour
= wx
.WHITE
1848 self
._usegradients
= False
1849 self
._gradientstyle
= 0 # Horizontal Gradient
1851 # Vista Selection Styles
1852 self
._vistaselection
= False
1854 # Connection lines style
1855 if wx
.Platform
!= "__WXMAC__":
1856 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1857 self
._dottedPen
.SetDashes([1,1])
1858 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1860 self
._dottedPen
= wx
.Pen("grey", 1)
1862 # Pen Used To Draw The Border Around Selected Items
1863 self
._borderPen
= wx
.BLACK_PEN
1864 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1866 # For Appended Windows
1867 self
._hasWindows
= False
1868 self
._itemWithWindow
= []
1870 if wx
.Platform
== "__WXMAC__":
1871 ctstyle
&= ~TR_LINES_AT_ROOT
1872 ctstyle |
= TR_NO_LINES
1874 platform
, major
, minor
= wx
.GetOsVersion()
1876 ctstyle |
= TR_ROW_LINES
1878 self
._windowStyle
= ctstyle
1880 # Create the default check image list
1881 self
.SetImageListCheck(13, 13)
1883 # A constant to use my translation of RendererNative.DrawTreeItemButton
1884 # if the wxPython version is less than 2.6.2.1.
1885 if wx
.VERSION_STRING
< "2.6.2.1":
1886 self
._drawingfunction
= DrawTreeItemButton
1888 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1890 # Create our container... at last!
1891 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1893 # If the tree display has no buttons, but does have
1894 # connecting lines, we can use a narrower layout.
1895 # It may not be a good idea to force this...
1896 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1900 self
.SetValidator(validator
)
1902 attr
= self
.GetDefaultAttributes()
1903 self
.SetOwnForegroundColour(attr
.colFg
)
1904 self
.SetOwnBackgroundColour(wx
.WHITE
)
1906 if not self
._hasFont
:
1907 self
.SetOwnFont(attr
.font
)
1912 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1913 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1914 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1915 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1916 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1917 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1918 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1919 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1921 # Sets the focus to ourselves: this is useful if you have items
1922 # with associated widgets.
1926 def AcceptsFocus(self
):
1927 # overridden base class method, allows this ctrl to
1928 # participate in the tab-order, etc. It's overridable because
1929 # of deriving this class from wx.PyScrolledWindow...
1933 def OnDestroy(self
, event
):
1934 """Handles the wx.EVT_WINDOW_DESTROY event."""
1936 # Here there may be something I miss... do I have to destroy
1938 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1939 self
._renameTimer
.Stop()
1940 del self
._renameTimer
1942 if self
._findTimer
and self
._findTimer
.IsRunning():
1943 self
._findTimer
.Stop()
1950 """Returns the global number of items in the tree."""
1952 if not self
._anchor
:
1956 count
= self
._anchor
.GetChildrenCount()
1958 if not self
.HasFlag(TR_HIDE_ROOT
):
1959 # take the root itself into account
1965 def GetIndent(self
):
1966 """Returns the item indentation."""
1971 def GetSpacing(self
):
1972 """Returns the spacing between the start and the text."""
1974 return self
._spacing
1977 def GetRootItem(self
):
1978 """Returns the root item."""
1983 def GetSelection(self
):
1984 """Returns the current selection: TR_SINGLE only."""
1986 return self
._current
1989 def ToggleItemSelection(self
, item
):
1990 """Toggles the item selection."""
1993 raise "\nERROR: Invalid Tree Item. "
1995 self
.SelectItem(item
, not self
.IsSelected(item
))
1998 def EnableChildren(self
, item
, enable
=True):
1999 """Enables/disables item children. Used internally."""
2002 if item
.IsExpanded():
2005 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2006 # We hit a radiobutton item not checked, we don't want to
2007 # enable the children
2010 child
, cookie
= self
.GetFirstChild(item
)
2012 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2014 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2015 self
.EnableChildren(child
, enable
)
2016 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2019 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2020 """Enables/disables an item."""
2023 raise "\nERROR: Invalid Tree Item. "
2025 if item
.IsEnabled() == enable
:
2028 if not enable
and item
.IsSelected():
2029 self
.SelectItem(item
, False)
2032 wnd
= item
.GetWindow()
2034 # Handles the eventual window associated to the item
2036 wndenable
= item
.GetWindowEnabled()
2044 # We have to refresh the item line
2045 dc
= wx
.ClientDC(self
)
2046 self
.CalculateSize(item
, dc
)
2047 self
.RefreshLine(item
)
2050 def IsEnabled(self
, item
):
2051 """Returns whether an item is enabled or disabled."""
2054 raise "\nERROR: Invalid Tree Item. "
2056 return item
.IsEnabled()
2059 def SetDisabledColour(self
, colour
):
2060 """Sets the items disabled colour."""
2062 self
._disabledColour
= colour
2066 def GetDisabledColour(self
):
2067 """Returns the items disabled colour."""
2069 return self
._disabledColour
2072 def IsItemChecked(self
, item
):
2073 """Returns whether an item is checked or not."""
2076 raise "\nERROR: Invalid Tree Item. "
2078 return item
.IsChecked()
2081 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2082 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2084 if item
.GetType() == 0:
2090 dc
= wx
.ClientDC(self
)
2091 self
.CalculateSize(item
, dc
)
2092 self
.RefreshLine(item
)
2095 def UnCheckRadioParent(self
, item
, checked
=False):
2096 """Used internally to handle radio node parent correctly."""
2098 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2100 e
.SetEventObject(self
)
2102 if self
.GetEventHandler().ProcessEvent(e
):
2106 self
.RefreshLine(item
)
2107 self
.EnableChildren(item
, checked
)
2108 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2110 e
.SetEventObject(self
)
2111 self
.GetEventHandler().ProcessEvent(e
)
2116 def CheckItem(self
, item
, checked
=True):
2118 Actually checks/uncheks an item, sending (eventually) the two
2119 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2123 raise "\nERROR: Invalid Tree Item. "
2125 # Should we raise an error here?!?
2126 if item
.GetType() == 0:
2129 if item
.GetType() == 2: # it's a radio button
2130 if not checked
and item
.IsChecked(): # Try To Unckeck?
2131 if item
.HasChildren():
2132 self
.UnCheckRadioParent(item
, checked
)
2135 if not self
.UnCheckRadioParent(item
, checked
):
2138 self
.CheckSameLevel(item
, False)
2141 # Radiobuttons are done, let's handle checkbuttons...
2142 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2144 e
.SetEventObject(self
)
2146 if self
.GetEventHandler().ProcessEvent(e
):
2151 dc
= wx
.ClientDC(self
)
2152 self
.RefreshLine(item
)
2154 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2155 ischeck
= self
.IsItemChecked(item
)
2156 self
.AutoCheckChild(item
, ischeck
)
2157 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2158 self
.AutoToggleChild(item
)
2160 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2162 e
.SetEventObject(self
)
2163 self
.GetEventHandler().ProcessEvent(e
)
2166 def AutoToggleChild(self
, item
):
2167 """Transverses the tree and toggles the items. Meaningful only for check items."""
2170 raise "\nERROR: Invalid Tree Item. "
2172 child
, cookie
= self
.GetFirstChild(item
)
2175 if item
.IsExpanded():
2180 if child
.GetType() == 1 and child
.IsEnabled():
2181 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2182 self
.AutoToggleChild(child
)
2183 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2186 def AutoCheckChild(self
, item
, checked
):
2187 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2190 raise "\nERROR: Invalid Tree Item. "
2192 (child
, cookie
) = self
.GetFirstChild(item
)
2195 if item
.IsExpanded():
2199 if child
.GetType() == 1 and child
.IsEnabled():
2200 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2201 self
.AutoCheckChild(child
, checked
)
2202 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2205 def CheckChilds(self
, item
, checked
=True):
2206 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2209 raise "\nERROR: Invalid Tree Item. "
2212 self
.AutoToggleChild(item
)
2214 self
.AutoCheckChild(item
, checked
)
2217 def CheckSameLevel(self
, item
, checked
=False):
2219 Uncheck radio items which are on the same level of the checked one.
2223 parent
= item
.GetParent()
2229 if parent
.IsExpanded():
2232 (child
, cookie
) = self
.GetFirstChild(parent
)
2234 if child
.GetType() == 2 and child
!= item
:
2235 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2236 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2237 self
.EnableChildren(child
, checked
)
2238 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2241 def EditLabel(self
, item
):
2242 """Starts editing an item label."""
2245 raise "\nERROR: Invalid Tree Item. "
2250 def ShouldInheritColours(self
):
2251 """We don't inherit colours from anyone."""
2256 def SetIndent(self
, indent
):
2257 """Sets item indentation."""
2259 self
._indent
= indent
2263 def SetSpacing(self
, spacing
):
2264 """Sets item spacing."""
2266 self
._spacing
= spacing
2270 def HasFlag(self
, flag
):
2271 """Returns whether CustomTreeCtrl has a flag."""
2273 return self
._windowStyle
& flag
2276 def HasChildren(self
, item
):
2277 """Returns whether an item has children or not."""
2280 raise "\nERROR: Invalid Tree Item. "
2282 return len(item
.GetChildren()) > 0
2285 def GetChildrenCount(self
, item
, recursively
=True):
2286 """Gets the item children count."""
2289 raise "\nERROR: Invalid Tree Item. "
2291 return item
.GetChildrenCount(recursively
)
2294 def SetTreeStyle(self
, styles
):
2295 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2297 # Do not try to expand the root node if it hasn't been created yet
2298 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2300 # if we will hide the root, make sure children are visible
2301 self
._anchor
.SetHasPlus()
2302 self
._anchor
.Expand()
2303 self
.CalculatePositions()
2305 # right now, just sets the styles. Eventually, we may
2306 # want to update the inherited styles, but right now
2307 # none of the parents has updatable styles
2309 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2310 selections
= self
.GetSelections()
2311 for select
in selections
[0:-1]:
2312 self
.SelectItem(select
, False)
2314 self
._windowStyle
= styles
2318 def GetTreeStyle(self
):
2319 """Returns the CustomTreeCtrl style."""
2321 return self
._windowStyle
2324 def HasButtons(self
):
2325 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2327 return self
.HasFlag(TR_HAS_BUTTONS
)
2330 # -----------------------------------------------------------------------------
2331 # functions to work with tree items
2332 # -----------------------------------------------------------------------------
2334 def GetItemText(self
, item
):
2335 """Returns the item text."""
2338 raise "\nERROR: Invalid Tree Item. "
2340 return item
.GetText()
2343 def GetItemImage(self
, item
, which
):
2344 """Returns the item image."""
2347 raise "\nERROR: Invalid Tree Item. "
2349 return item
.GetImage(which
)
2352 def GetPyData(self
, item
):
2353 """Returns the data associated to an item."""
2356 raise "\nERROR: Invalid Tree Item. "
2358 return item
.GetData()
2360 GetItemPyData
= GetPyData
2363 def GetItemTextColour(self
, item
):
2364 """Returns the item text colour."""
2367 raise "\nERROR: Invalid Tree Item. "
2369 return item
.Attr().GetTextColour()
2372 def GetItemBackgroundColour(self
, item
):
2373 """Returns the item background colour."""
2376 raise "\nERROR: Invalid Tree Item. "
2378 return item
.Attr().GetBackgroundColour()
2381 def GetItemFont(self
, item
):
2382 """Returns the item font."""
2385 raise "\nERROR: Invalid Tree Item. "
2387 return item
.Attr().GetFont()
2390 def IsItemHyperText(self
, item
):
2391 """Returns whether an item is hypertext or not."""
2394 raise "\nERROR: Invalid Tree Item. "
2396 return item
.IsHyperText()
2399 def SetItemText(self
, item
, text
):
2400 """Sets the item text."""
2403 raise "\nERROR: Invalid Tree Item. "
2405 dc
= wx
.ClientDC(self
)
2407 self
.CalculateSize(item
, dc
)
2408 self
.RefreshLine(item
)
2411 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2412 """Sets the item image, depending on the item state."""
2415 raise "\nERROR: Invalid Tree Item. "
2417 item
.SetImage(image
, which
)
2419 dc
= wx
.ClientDC(self
)
2420 self
.CalculateSize(item
, dc
)
2421 self
.RefreshLine(item
)
2424 def SetPyData(self
, item
, data
):
2425 """Sets the data associated to an item."""
2428 raise "\nERROR: Invalid Tree Item. "
2432 SetItemPyData
= SetPyData
2435 def SetItemHasChildren(self
, item
, has
=True):
2436 """Forces the appearance of the button next to the item."""
2439 raise "\nERROR: Invalid Tree Item. "
2441 item
.SetHasPlus(has
)
2442 self
.RefreshLine(item
)
2445 def SetItemBold(self
, item
, bold
=True):
2446 """Sets the item font bold/unbold."""
2449 raise "\nERROR: Invalid Tree Item. "
2451 # avoid redrawing the tree if no real change
2452 if item
.IsBold() != bold
:
2457 def SetItemItalic(self
, item
, italic
=True):
2458 """Sets the item font italic/non-italic."""
2461 raise "\nERROR: Invalid Tree Item. "
2463 if item
.IsItalic() != italic
:
2464 itemFont
= self
.GetItemFont(item
)
2465 if itemFont
!= wx
.NullFont
:
2470 item
.SetItalic(italic
)
2471 itemFont
.SetStyle(style
)
2472 self
.SetItemFont(item
, itemFont
)
2476 def SetItemDropHighlight(self
, item
, highlight
=True):
2478 Gives the item the visual feedback for drag and drop operations.
2479 This is useful when something is dragged from outside the CustomTreeCtrl.
2483 raise "\nERROR: Invalid Tree Item. "
2486 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2487 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2489 item
.Attr().SetTextColour(fg
)
2490 item
.Attr
.SetBackgroundColour(bg
)
2491 self
.RefreshLine(item
)
2494 def SetItemTextColour(self
, item
, col
):
2495 """Sets the item text colour."""
2498 raise "\nERROR: Invalid Tree Item. "
2500 if self
.GetItemTextColour(item
) == col
:
2503 item
.Attr().SetTextColour(col
)
2504 self
.RefreshLine(item
)
2507 def SetItemBackgroundColour(self
, item
, col
):
2508 """Sets the item background colour."""
2511 raise "\nERROR: Invalid Tree Item. "
2513 item
.Attr().SetBackgroundColour(col
)
2514 self
.RefreshLine(item
)
2517 def SetItemHyperText(self
, item
, hyper
=True):
2518 """Sets whether the item is hypertext or not."""
2521 raise "\nERROR: Invalid Tree Item. "
2523 item
.SetHyperText(hyper
)
2524 self
.RefreshLine(item
)
2527 def SetItemFont(self
, item
, font
):
2528 """Sets the item font."""
2531 raise "\nERROR: Invalid Tree Item. "
2533 if self
.GetItemFont(item
) == font
:
2536 item
.Attr().SetFont(font
)
2540 def SetFont(self
, font
):
2541 """Sets the CustomTreeCtrl font."""
2543 wx
.ScrolledWindow
.SetFont(self
, font
)
2545 self
._normalFont
= font
2546 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2547 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2548 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2553 def GetHyperTextFont(self
):
2554 """Returns the font used to render an hypertext item."""
2556 return self
._hypertextfont
2559 def SetHyperTextFont(self
, font
):
2560 """Sets the font used to render an hypertext item."""
2562 self
._hypertextfont
= font
2566 def SetHyperTextNewColour(self
, colour
):
2567 """Sets the colour used to render a non-visited hypertext item."""
2569 self
._hypertextnewcolour
= colour
2573 def GetHyperTextNewColour(self
):
2574 """Returns the colour used to render a non-visited hypertext item."""
2576 return self
._hypertextnewcolour
2579 def SetHyperTextVisitedColour(self
, colour
):
2580 """Sets the colour used to render a visited hypertext item."""
2582 self
._hypertextvisitedcolour
= colour
2586 def GetHyperTextVisitedColour(self
):
2587 """Returns the colour used to render a visited hypertext item."""
2589 return self
._hypertextvisitedcolour
2592 def SetItemVisited(self
, item
, visited
=True):
2593 """Sets whether an hypertext item was visited."""
2596 raise "\nERROR: Invalid Tree Item. "
2598 item
.SetVisited(visited
)
2599 self
.RefreshLine(item
)
2602 def GetItemVisited(self
, item
):
2603 """Returns whether an hypertext item was visited."""
2606 raise "\nERROR: Invalid Tree Item. "
2608 return item
.GetVisited()
2611 def SetHilightFocusColour(self
, colour
):
2613 Sets the colour used to highlight focused selected items.
2614 This is applied only if gradient and Windows Vista styles are disabled.
2617 self
._hilightBrush
= wx
.Brush(colour
)
2618 self
.RefreshSelected()
2621 def SetHilightNonFocusColour(self
, colour
):
2623 Sets the colour used to highlight unfocused selected items.
2624 This is applied only if gradient and Windows Vista styles are disabled.
2627 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2628 self
.RefreshSelected()
2631 def GetHilightFocusColour(self
):
2633 Returns the colour used to highlight focused selected items.
2634 This is applied only if gradient and Windows Vista styles are disabled.
2637 return self
._hilightBrush
.GetColour()
2640 def GetHilightNonFocusColour(self
):
2642 Returns the colour used to highlight unfocused selected items.
2643 This is applied only if gradient and Windows Vista styles are disabled.
2646 return self
._hilightUnfocusedBrush
.GetColour()
2649 def SetFirstGradientColour(self
, colour
=None):
2650 """Sets the first gradient colour."""
2653 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2655 self
._firstcolour
= colour
2656 if self
._usegradients
:
2657 self
.RefreshSelected()
2660 def SetSecondGradientColour(self
, colour
=None):
2661 """Sets the second gradient colour."""
2664 # No colour given, generate a slightly darker from the
2665 # CustomTreeCtrl background colour
2666 color
= self
.GetBackgroundColour()
2667 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2668 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2669 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2671 self
._secondcolour
= colour
2673 if self
._usegradients
:
2674 self
.RefreshSelected()
2677 def GetFirstGradientColour(self
):
2678 """Returns the first gradient colour."""
2680 return self
._firstcolour
2683 def GetSecondGradientColour(self
):
2684 """Returns the second gradient colour."""
2686 return self
._secondcolour
2689 def EnableSelectionGradient(self
, enable
=True):
2690 """Globally enables/disables drawing of gradient selection."""
2692 self
._usegradients
= enable
2693 self
._vistaselection
= False
2694 self
.RefreshSelected()
2697 def SetGradientStyle(self
, vertical
=0):
2699 Sets the gradient style:
2700 0: horizontal gradient
2701 1: vertical gradient
2704 # 0 = Horizontal, 1 = Vertical
2705 self
._gradientstyle
= vertical
2707 if self
._usegradients
:
2708 self
.RefreshSelected()
2711 def GetGradientStyle(self
):
2713 Returns the gradient style:
2714 0: horizontal gradient
2715 1: vertical gradient
2718 return self
._gradientstyle
2721 def EnableSelectionVista(self
, enable
=True):
2722 """Globally enables/disables drawing of Windows Vista selection."""
2724 self
._usegradients
= False
2725 self
._vistaselection
= enable
2726 self
.RefreshSelected()
2729 def SetBorderPen(self
, pen
):
2731 Sets the pen used to draw the selected item border.
2732 The border pen is not used if the Windows Vista style is applied.
2735 self
._borderPen
= pen
2736 self
.RefreshSelected()
2739 def GetBorderPen(self
):
2741 Returns the pen used to draw the selected item border.
2742 The border pen is not used if the Windows Vista style is applied.
2745 return self
._borderPen
2748 def SetConnectionPen(self
, pen
):
2749 """Sets the pen used to draw the connecting lines between items."""
2751 self
._dottedPen
= pen
2755 def GetConnectionPen(self
):
2756 """Returns the pen used to draw the connecting lines between items."""
2758 return self
._dottedPen
2761 def SetBackgroundImage(self
, image
):
2762 """Sets the CustomTreeCtrl background image (can be none)."""
2764 self
._backgroundImage
= image
2768 def GetBackgroundImage(self
):
2769 """Returns the CustomTreeCtrl background image (can be none)."""
2771 return self
._backgroundImage
2774 def GetItemWindow(self
, item
):
2775 """Returns the window associated to the item (if any)."""
2778 raise "\nERROR: Invalid Item"
2780 return item
.GetWindow()
2783 def GetItemWindowEnabled(self
, item
):
2784 """Returns whether the window associated to the item is enabled."""
2787 raise "\nERROR: Invalid Item"
2789 return item
.GetWindowEnabled()
2792 def SetItemWindowEnabled(self
, item
, enable
=True):
2793 """Enables/disables the window associated to the item."""
2796 raise "\nERROR: Invalid Item"
2798 item
.SetWindowEnabled(enable
)
2801 def GetItemType(self
, item
):
2803 Returns the item type:
2810 raise "\nERROR: Invalid Item"
2812 return item
.GetType()
2814 # -----------------------------------------------------------------------------
2815 # item status inquiries
2816 # -----------------------------------------------------------------------------
2818 def IsVisible(self
, item
):
2819 """Returns whether the item is visible or not."""
2822 raise "\nERROR: Invalid Tree Item. "
2824 # An item is only visible if it's not a descendant of a collapsed item
2825 parent
= item
.GetParent()
2829 if not parent
.IsExpanded():
2832 parent
= parent
.GetParent()
2834 startX
, startY
= self
.GetViewStart()
2835 clientSize
= self
.GetClientSize()
2837 rect
= self
.GetBoundingRect(item
)
2841 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2843 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2845 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2851 def ItemHasChildren(self
, item
):
2852 """Returns whether the item has children or not."""
2855 raise "\nERROR: Invalid Tree Item. "
2857 # consider that the item does have children if it has the "+" button: it
2858 # might not have them (if it had never been expanded yet) but then it
2859 # could have them as well and it's better to err on this side rather than
2860 # disabling some operations which are restricted to the items with
2861 # children for an item which does have them
2862 return item
.HasPlus()
2865 def IsExpanded(self
, item
):
2866 """Returns whether the item is expanded or not."""
2869 raise "\nERROR: Invalid Tree Item. "
2871 return item
.IsExpanded()
2874 def IsSelected(self
, item
):
2875 """Returns whether the item is selected or not."""
2878 raise "\nERROR: Invalid Tree Item. "
2880 return item
.IsSelected()
2883 def IsBold(self
, item
):
2884 """Returns whether the item font is bold or not."""
2887 raise "\nERROR: Invalid Tree Item. "
2889 return item
.IsBold()
2892 def IsItalic(self
, item
):
2893 """Returns whether the item font is italic or not."""
2896 raise "\nERROR: Invalid Tree Item. "
2898 return item
.IsItalic()
2901 # -----------------------------------------------------------------------------
2903 # -----------------------------------------------------------------------------
2905 def GetItemParent(self
, item
):
2906 """Gets the item parent."""
2909 raise "\nERROR: Invalid Tree Item. "
2911 return item
.GetParent()
2914 def GetFirstChild(self
, item
):
2915 """Gets the item first child."""
2918 raise "\nERROR: Invalid Tree Item. "
2921 return self
.GetNextChild(item
, cookie
)
2924 def GetNextChild(self
, item
, cookie
):
2926 Gets the item next child based on the 'cookie' parameter.
2927 This method has no sense if you do not call GetFirstChild() before.
2931 raise "\nERROR: Invalid Tree Item. "
2933 children
= item
.GetChildren()
2935 # it's ok to cast cookie to size_t, we never have indices big enough to
2938 if cookie
< len(children
):
2940 return children
[cookie
], cookie
+1
2944 # there are no more of them
2948 def GetLastChild(self
, item
):
2949 """Gets the item last child."""
2952 raise "\nERROR: Invalid Tree Item. "
2954 children
= item
.GetChildren()
2955 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2958 def GetNextSibling(self
, item
):
2959 """Gets the next sibling of an item."""
2962 raise "\nERROR: Invalid Tree Item. "
2965 parent
= i
.GetParent()
2969 # root item doesn't have any siblings
2972 siblings
= parent
.GetChildren()
2973 index
= siblings
.index(i
)
2976 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
2979 def GetPrevSibling(self
, item
):
2980 """Gets the previous sibling of an item."""
2983 raise "\nERROR: Invalid Tree Item. "
2986 parent
= i
.GetParent()
2990 # root item doesn't have any siblings
2993 siblings
= parent
.GetChildren()
2994 index
= siblings
.index(i
)
2996 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
2999 def GetNext(self
, item
):
3000 """Gets the next item. Only for internal use right now."""
3003 raise "\nERROR: Invalid Tree Item. "
3007 # First see if there are any children.
3008 children
= i
.GetChildren()
3009 if len(children
) > 0:
3012 # Try a sibling of this or ancestor instead
3015 while p
and not toFind
:
3016 toFind
= self
.GetNextSibling(p
)
3017 p
= self
.GetItemParent(p
)
3022 def GetFirstVisibleItem(self
):
3023 """Returns the first visible item."""
3025 id = self
.GetRootItem()
3030 if self
.IsVisible(id):
3032 id = self
.GetNext(id)
3037 def GetNextVisible(self
, item
):
3038 """Returns the next visible item."""
3041 raise "\nERROR: Invalid Tree Item. "
3046 id = self
.GetNext(id)
3047 if id and self
.IsVisible(id):
3053 def GetPrevVisible(self
, item
):
3056 raise "\nERROR: Invalid Tree Item. "
3058 raise "\nERROR: Not Implemented"
3063 def ResetTextControl(self
):
3064 """Called by TreeTextCtrl when it marks itself for deletion."""
3066 self
._textCtrl
.Destroy()
3067 self
._textCtrl
= None
3070 def FindItem(self
, idParent
, prefixOrig
):
3071 """Finds the first item starting with the given prefix after the given item."""
3073 # match is case insensitive as this is more convenient to the user: having
3074 # to press Shift-letter to go to the item starting with a capital letter
3075 # would be too bothersome
3076 prefix
= prefixOrig
.lower()
3078 # determine the starting point: we shouldn't take the current item (this
3079 # allows to switch between two items starting with the same letter just by
3080 # pressing it) but we shouldn't jump to the next one if the user is
3081 # continuing to type as otherwise he might easily skip the item he wanted
3084 if len(prefix
) == 1:
3085 id = self
.GetNext(id)
3087 # look for the item starting with the given prefix after it
3088 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3090 id = self
.GetNext(id)
3092 # if we haven't found anything...
3095 # ... wrap to the beginning
3096 id = self
.GetRootItem()
3097 if self
.HasFlag(TR_HIDE_ROOT
):
3098 # can't select virtual root
3099 id = self
.GetNext(id)
3101 # and try all the items (stop when we get to the one we started from)
3102 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3103 id = self
.GetNext(id)
3108 # -----------------------------------------------------------------------------
3110 # -----------------------------------------------------------------------------
3112 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3113 """Actually inserts an item in the tree."""
3115 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3116 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3118 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3119 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3121 if ct_type
< 0 or ct_type
> 2:
3122 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3128 # should we give a warning here?
3129 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3131 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3133 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3136 self
._hasWindows
= True
3137 self
._itemWithWindow
.append(item
)
3139 parent
.Insert(item
, previous
)
3144 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3145 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3148 raise "\nERROR: Tree Can Have Only One Root"
3150 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3151 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3153 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3154 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3156 if ct_type
< 0 or ct_type
> 2:
3157 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3159 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3161 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3164 self
._hasWindows
= True
3165 self
._itemWithWindow
.append(self
._anchor
)
3167 if self
.HasFlag(TR_HIDE_ROOT
):
3169 # if root is hidden, make sure we can navigate
3171 self
._anchor
.SetHasPlus()
3172 self
._anchor
.Expand()
3173 self
.CalculatePositions()
3175 if not self
.HasFlag(TR_MULTIPLE
):
3177 self
._current
= self
._key
_current
= self
._anchor
3178 self
._current
.SetHilight(True)
3183 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3184 """Appends an item as a first child of parent."""
3186 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3187 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3189 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3190 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3192 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3195 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3196 """Auxiliary function to cope with the C++ hideous multifunction."""
3198 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3199 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3201 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3202 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3207 # should we give a warning here?
3208 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3214 index
= parent
.GetChildren().index(idPrevious
)
3216 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3218 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3221 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3222 """Auxiliary function to cope with the C++ hideous multifunction."""
3224 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3225 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3227 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3228 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3233 # should we give a warning here?
3234 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3236 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3239 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3240 """Inserts an item after the given previous."""
3242 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3243 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3245 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3246 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3248 if type(input) == type(1):
3249 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3251 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3254 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3255 """Appends an item as a last child of its parent."""
3257 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3258 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3260 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3261 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3266 # should we give a warning here?
3267 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3269 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3272 def SendDeleteEvent(self
, item
):
3273 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3275 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3277 event
.SetEventObject(self
)
3278 self
.ProcessEvent(event
)
3281 def IsDescendantOf(self
, parent
, item
):
3282 """Checks if the given item is under another one."""
3288 # item is a descendant of parent
3291 item
= item
.GetParent()
3296 # Don't leave edit or selection on a child which is about to disappear
3297 def ChildrenClosing(self
, item
):
3298 """We are about to destroy the item children."""
3300 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3301 self
._textCtrl
.StopEditing()
3303 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3304 self
._key
_current
= None
3306 if self
.IsDescendantOf(item
, self
._select
_me
):
3307 self
._select
_me
= item
3309 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3310 self
._current
.SetHilight(False)
3311 self
._current
= None
3312 self
._select
_me
= item
3315 def DeleteChildren(self
, item
):
3316 """Delete item children."""
3319 raise "\nERROR: Invalid Tree Item. "
3321 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3323 self
.ChildrenClosing(item
)
3324 item
.DeleteChildren(self
)
3327 def Delete(self
, item
):
3328 """Delete an item."""
3331 raise "\nERROR: Invalid Tree Item. "
3333 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3335 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3336 # can't delete the item being edited, cancel editing it first
3337 self
._textCtrl
.StopEditing()
3339 parent
= item
.GetParent()
3341 # don't keep stale pointers around!
3342 if self
.IsDescendantOf(item
, self
._key
_current
):
3344 # Don't silently change the selection:
3345 # do it properly in idle time, so event
3346 # handlers get called.
3348 # self._key_current = parent
3349 self
._key
_current
= None
3351 # self._select_me records whether we need to select
3352 # a different item, in idle time.
3353 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3354 self
._select
_me
= parent
3356 if self
.IsDescendantOf(item
, self
._current
):
3358 # Don't silently change the selection:
3359 # do it properly in idle time, so event
3360 # handlers get called.
3362 # self._current = parent
3363 self
._current
= None
3364 self
._select
_me
= parent
3366 # remove the item from the tree
3369 parent
.GetChildren().remove(item
) # remove by value
3371 else: # deleting the root
3373 # nothing will be left in the tree
3376 # and delete all of its children and the item itself now
3377 item
.DeleteChildren(self
)
3378 self
.SendDeleteEvent(item
)
3380 if item
== self
._select
_me
:
3381 self
._select
_me
= None
3383 # Remove the item with window
3384 if item
in self
._itemWithWindow
:
3385 wnd
= item
.GetWindow()
3389 self
._itemWithWindow
.remove(item
)
3394 def DeleteAllItems(self
):
3395 """Delete all items in the CustomTreeCtrl."""
3398 self
.Delete(self
._anchor
)
3401 def Expand(self
, item
):
3403 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3404 EVT_TREE_ITEM_EXPANDED events.
3408 raise "\nERROR: Invalid Tree Item. "
3410 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3411 raise "\nERROR: Can't Expand An Hidden Root. "
3413 if not item
.HasPlus():
3416 if item
.IsExpanded():
3419 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3421 event
.SetEventObject(self
)
3423 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3424 # cancelled by program
3428 self
.CalculatePositions()
3430 self
.RefreshSubtree(item
)
3432 if self
._hasWindows
:
3433 # We hide the associated window here, we may show it after
3436 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3437 self
.ProcessEvent(event
)
3440 def ExpandAll(self
, item
):
3441 """Expands all the items."""
3444 raise "\nERROR: Invalid Tree Item. "
3446 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3448 if not self
.IsExpanded(item
):
3451 child
, cookie
= self
.GetFirstChild(item
)
3454 self
.ExpandAll(child
)
3455 child
, cookie
= self
.GetNextChild(item
, cookie
)
3458 def Collapse(self
, item
):
3460 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3461 EVT_TREE_ITEM_COLLAPSED events.
3465 raise "\nERROR: Invalid Tree Item. "
3467 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3468 raise "\nERROR: Can't Collapse An Hidden Root. "
3470 if not item
.IsExpanded():
3473 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3475 event
.SetEventObject(self
)
3476 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3477 # cancelled by program
3480 self
.ChildrenClosing(item
)
3483 self
.CalculatePositions()
3484 self
.RefreshSubtree(item
)
3486 if self
._hasWindows
:
3489 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3490 self
.ProcessEvent(event
)
3493 def CollapseAndReset(self
, item
):
3494 """Collapse the given item and deletes its children."""
3497 self
.DeleteChildren(item
)
3500 def Toggle(self
, item
):
3501 """Toggles the item state (collapsed/expanded)."""
3503 if item
.IsExpanded():
3509 def HideWindows(self
):
3510 """Hides the windows associated to the items. Used internally."""
3512 for child
in self
._itemWithWindow
:
3513 if not self
.IsVisible(child
):
3514 wnd
= child
.GetWindow()
3519 """Unselects the current selection."""
3523 self
._current
.SetHilight(False)
3524 self
.RefreshLine(self
._current
)
3526 self
._current
= None
3527 self
._select
_me
= None
3530 def UnselectAllChildren(self
, item
):
3531 """Unselects all the children of the given item."""
3533 if item
.IsSelected():
3535 item
.SetHilight(False)
3536 self
.RefreshLine(item
)
3538 if item
.HasChildren():
3539 for child
in item
.GetChildren():
3540 self
.UnselectAllChildren(child
)
3543 def UnselectAll(self
):
3544 """Unselect all the items."""
3546 rootItem
= self
.GetRootItem()
3548 # the tree might not have the root item at all
3550 self
.UnselectAllChildren(rootItem
)
3553 # Recursive function !
3554 # To stop we must have crt_item<last_item
3556 # Tag all next children, when no more children,
3557 # Move to parent (not to tag)
3558 # Keep going... if we found last_item, we stop.
3560 def TagNextChildren(self
, crt_item
, last_item
, select
):
3561 """Used internally."""
3563 parent
= crt_item
.GetParent()
3565 if parent
== None: # This is root item
3566 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3568 children
= parent
.GetChildren()
3569 index
= children
.index(crt_item
)
3571 count
= len(children
)
3573 for n
in xrange(index
+1, count
):
3574 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3577 return self
.TagNextChildren(parent
, last_item
, select
)
3580 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3581 """Used internally."""
3583 crt_item
.SetHilight(select
)
3584 self
.RefreshLine(crt_item
)
3586 if crt_item
== last_item
:
3589 if crt_item
.HasChildren():
3590 for child
in crt_item
.GetChildren():
3591 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3597 def SelectItemRange(self
, item1
, item2
):
3598 """Selects all the items between item1 and item2."""
3600 self
._select
_me
= None
3602 # item2 is not necessary after item1
3603 # choice first' and 'last' between item1 and item2
3604 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3605 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3607 select
= self
._current
.IsSelected()
3609 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3612 self
.TagNextChildren(first
, last
, select
)
3615 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3616 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3619 raise "\nERROR: Invalid Tree Item. "
3621 self
._select
_me
= None
3623 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3625 # to keep going anyhow !!!
3627 if item
.IsSelected():
3628 return # nothing to do
3629 unselect_others
= True
3630 extended_select
= False
3632 elif unselect_others
and item
.IsSelected():
3634 # selection change if there is more than one item currently selected
3635 if len(self
.GetSelections()) == 1:
3638 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3640 event
._itemOld
= self
._current
3641 event
.SetEventObject(self
)
3642 # TODO : Here we don't send any selection mode yet !
3644 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3647 parent
= self
.GetItemParent(item
)
3649 if not self
.IsExpanded(parent
):
3652 parent
= self
.GetItemParent(parent
)
3657 self
.Unselect() # to speed up thing
3663 if not self
._current
:
3664 self
._current
= self
._key
_current
= self
.GetRootItem()
3666 # don't change the mark (self._current)
3667 self
.SelectItemRange(self
._current
, item
)
3671 select
= True # the default
3673 # Check if we need to toggle hilight (ctrl mode)
3674 if not unselect_others
:
3675 select
= not item
.IsSelected()
3677 self
._current
= self
._key
_current
= item
3678 self
._current
.SetHilight(select
)
3679 self
.RefreshLine(self
._current
)
3681 # This can cause idle processing to select the root
3682 # if no item is selected, so it must be after the
3684 self
.EnsureVisible(item
)
3686 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3687 self
.GetEventHandler().ProcessEvent(event
)
3689 # Handles hypertext items
3690 if self
.IsItemHyperText(item
):
3691 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3693 self
.GetEventHandler().ProcessEvent(event
)
3696 def SelectItem(self
, item
, select
=True):
3697 """Selects/deselects an item."""
3700 raise "\nERROR: Invalid Tree Item. "
3704 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3708 item
.SetHilight(False)
3709 self
.RefreshLine(item
)
3712 def FillArray(self
, item
, array
=[]):
3714 Internal function. Used to populate an array of selected items when
3715 the style TR_MULTIPLE is used.
3721 if item
.IsSelected():
3724 if item
.HasChildren():
3725 for child
in item
.GetChildren():
3726 array
= self
.FillArray(child
, array
)
3731 def GetSelections(self
):
3733 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3734 the TR_MULTIPLE style set.
3738 idRoot
= self
.GetRootItem()
3740 array
= self
.FillArray(idRoot
, array
)
3742 #else: the tree is empty, so no selections
3747 def EnsureVisible(self
, item
):
3748 """Ensure that an item is visible in CustomTreeCtrl."""
3751 raise "\nERROR: Invalid Tree Item. "
3753 # first expand all parent branches
3754 parent
= item
.GetParent()
3756 if self
.HasFlag(TR_HIDE_ROOT
):
3757 while parent
and parent
!= self
._anchor
:
3759 parent
= parent
.GetParent()
3763 parent
= parent
.GetParent()
3768 def ScrollTo(self
, item
):
3769 """Scrolls the specified item into view."""
3774 # We have to call this here because the label in
3775 # question might just have been added and no screen
3776 # update taken place.
3778 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3783 # now scroll to the item
3784 item_y
= item
.GetY()
3785 start_x
, start_y
= self
.GetViewStart()
3786 start_y
*= _PIXELS_PER_UNIT
3788 client_w
, client_h
= self
.GetClientSize()
3792 if item_y
< start_y
+3:
3795 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3796 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3797 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3798 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3799 # Item should appear at top
3800 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3802 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3805 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3806 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3807 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3808 item_y
+= _PIXELS_PER_UNIT
+2
3809 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3810 # Item should appear at bottom
3811 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
)
3814 def OnCompareItems(self
, item1
, item2
):
3816 Returns whether 2 items have the same text.
3817 Override this function in the derived class to change the sort order of the items
3818 in the CustomTreeCtrl. The function should return a negative, zero or positive
3819 value if the first item is less than, equal to or greater than the second one.
3821 The base class version compares items alphabetically.
3824 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3827 def SortChildren(self
, item
):
3829 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3830 You should override that method to change the sort order (the default is ascending
3831 case-sensitive alphabetical order).
3835 raise "\nERROR: Invalid Tree Item. "
3837 children
= item
.GetChildren()
3839 if len(children
) > 1:
3841 children
.sort(self
.OnCompareItems
)
3844 def GetImageList(self
):
3845 """Returns the normal image list."""
3847 return self
._imageListNormal
3850 def GetButtonsImageList(self
):
3851 """Returns the buttons image list (from which application-defined button images are taken)."""
3853 return self
._imageListButtons
3856 def GetStateImageList(self
):
3857 """Returns the state image list (from which application-defined state images are taken)."""
3859 return self
._imageListState
3862 def GetImageListCheck(self
):
3863 """Returns the image list used to build the check/radio buttons."""
3865 return self
._imageListCheck
3868 def CalculateLineHeight(self
):
3869 """Calculates the height of a line."""
3871 dc
= wx
.ClientDC(self
)
3872 self
._lineHeight
= dc
.GetCharHeight()
3874 if self
._imageListNormal
:
3876 # Calculate a self._lineHeight value from the normal Image sizes.
3877 # May be toggle off. Then CustomTreeCtrl will spread when
3878 # necessary (which might look ugly).
3879 n
= self
._imageListNormal
.GetImageCount()
3883 width
, height
= self
._imageListNormal
.GetSize(i
)
3885 if height
> self
._lineHeight
:
3886 self
._lineHeight
= height
3888 if self
._imageListButtons
:
3890 # Calculate a self._lineHeight value from the Button image sizes.
3891 # May be toggle off. Then CustomTreeCtrl will spread when
3892 # necessary (which might look ugly).
3893 n
= self
._imageListButtons
.GetImageCount()
3897 width
, height
= self
._imageListButtons
.GetSize(i
)
3899 if height
> self
._lineHeight
:
3900 self
._lineHeight
= height
3902 if self
._imageListCheck
:
3904 # Calculate a self._lineHeight value from the check/radio image sizes.
3905 # May be toggle off. Then CustomTreeCtrl will spread when
3906 # necessary (which might look ugly).
3907 n
= self
._imageListCheck
.GetImageCount()
3911 width
, height
= self
._imageListCheck
.GetSize(i
)
3913 if height
> self
._lineHeight
:
3914 self
._lineHeight
= height
3916 if self
._lineHeight
< 30:
3917 self
._lineHeight
+= 2 # at least 2 pixels
3919 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3922 def SetImageList(self
, imageList
):
3923 """Sets the normal image list."""
3925 if self
._ownsImageListNormal
:
3926 del self
._imageListNormal
3928 self
._imageListNormal
= imageList
3929 self
._ownsImageListNormal
= False
3931 # Don't do any drawing if we're setting the list to NULL,
3932 # since we may be in the process of deleting the tree control.
3934 self
.CalculateLineHeight()
3936 # We gray out the image list to use the grayed icons with disabled items
3937 self
._grayedImageList
= wx
.ImageList(16, 16, True, 0)
3939 for ii
in xrange(imageList
.GetImageCount()):
3941 bmp
= imageList
.GetBitmap(ii
)
3942 image
= wx
.ImageFromBitmap(bmp
)
3943 image
= GrayOut(image
)
3944 newbmp
= wx
.BitmapFromImage(image
)
3945 self
._grayedImageList
.Add(newbmp
)
3948 def SetStateImageList(self
, imageList
):
3949 """Sets the state image list (from which application-defined state images are taken)."""
3951 if self
._ownsImageListState
:
3952 del self
._imageListState
3954 self
._imageListState
= imageList
3955 self
._ownsImageListState
= False
3958 def SetButtonsImageList(self
, imageList
):
3959 """Sets the buttons image list (from which application-defined button images are taken)."""
3961 if self
._ownsImageListButtons
:
3962 del self
._imageListButtons
3964 self
._imageListButtons
= imageList
3965 self
._ownsImageListButtons
= False
3967 self
.CalculateLineHeight()
3970 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
3971 """Sets the check image list."""
3975 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
3976 self
._imageListCheck
.Add(GetCheckedBitmap())
3977 self
._imageListCheck
.Add(GetNotCheckedBitmap())
3978 self
._imageListCheck
.Add(GetFlaggedBitmap())
3979 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
3983 sizex
, sizey
= imglist
.GetSize(0)
3984 self
._imageListCheck
= imglist
3986 # We gray out the image list to use the grayed icons with disabled items
3987 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
3989 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
3991 bmp
= self
._imageListCheck
.GetBitmap(ii
)
3992 image
= wx
.ImageFromBitmap(bmp
)
3993 image
= GrayOut(image
)
3994 newbmp
= wx
.BitmapFromImage(image
)
3995 self
._grayedCheckList
.Add(newbmp
)
4000 self
.CalculateLineHeight()
4003 def AssignImageList(self
, imageList
):
4004 """Assigns the normal image list."""
4006 self
.SetImageList(imageList
)
4007 self
._ownsImageListNormal
= True
4010 def AssignStateImageList(self
, imageList
):
4011 """Assigns the state image list."""
4013 self
.SetStateImageList(imageList
)
4014 self
._ownsImageListState
= True
4017 def AssignButtonsImageList(self
, imageList
):
4018 """Assigns the button image list."""
4020 self
.SetButtonsImageList(imageList
)
4021 self
._ownsImageListButtons
= True
4024 # -----------------------------------------------------------------------------
4026 # -----------------------------------------------------------------------------
4028 def AdjustMyScrollbars(self
):
4029 """Adjust the wx.ScrolledWindow scrollbars."""
4033 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4034 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4035 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4036 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4037 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4038 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4042 self
.SetScrollbars(0, 0, 0, 0)
4045 def GetLineHeight(self
, item
):
4046 """Returns the line height for the given item."""
4048 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4049 return item
.GetHeight()
4051 return self
._lineHeight
4054 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4055 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4057 oldpen
= dc
.GetPen()
4058 oldbrush
= dc
.GetBrush()
4059 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4061 # calculate gradient coefficients
4063 col2
= self
._secondcolour
4064 col1
= self
._firstcolour
4066 col2
= self
._hilightUnfocusedBrush
.GetColour()
4067 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4069 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4070 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4072 flrect
= float(rect
.height
)
4074 rstep
= float((r2
- r1
)) / flrect
4075 gstep
= float((g2
- g1
)) / flrect
4076 bstep
= float((b2
- b1
)) / flrect
4078 rf
, gf
, bf
= 0, 0, 0
4080 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4081 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4082 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4083 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4089 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4090 dc
.DrawRectangleRect(rect
)
4091 dc
.SetBrush(oldbrush
)
4094 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4095 """Gradient fill from colour 1 to colour 2 from left to right."""
4097 oldpen
= dc
.GetPen()
4098 oldbrush
= dc
.GetBrush()
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
, rect
.x
+ rect
.width
):
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
, 1, rect
.height
)
4130 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4131 dc
.DrawRectangleRect(rect
)
4132 dc
.SetBrush(oldbrush
)
4135 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4136 """Draw the selected item(s) with the Windows Vista style."""
4140 outer
= _rgbSelectOuter
4141 inner
= _rgbSelectInner
4143 bottom
= _rgbSelectBottom
4147 outer
= _rgbNoFocusOuter
4148 inner
= _rgbNoFocusInner
4149 top
= _rgbNoFocusTop
4150 bottom
= _rgbNoFocusBottom
4152 oldpen
= dc
.GetPen()
4153 oldbrush
= dc
.GetBrush()
4155 bdrRect
= wx
.Rect(*rect
.Get())
4156 filRect
= wx
.Rect(*rect
.Get())
4157 filRect
.Deflate(1,1)
4159 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4160 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4162 flrect
= float(filRect
.height
)
4164 rstep
= float((r2
- r1
)) / flrect
4165 gstep
= float((g2
- g1
)) / flrect
4166 bstep
= float((b2
- b1
)) / flrect
4168 rf
, gf
, bf
= 0, 0, 0
4169 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4171 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4172 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4173 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4174 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4179 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4180 dc
.SetPen(wx
.Pen(outer
))
4181 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4182 bdrRect
.Deflate(1, 1)
4183 dc
.SetPen(wx
.Pen(inner
))
4184 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4187 dc
.SetBrush(oldbrush
)
4190 def PaintItem(self
, item
, dc
):
4191 """Actually paint an item."""
4193 attr
= item
.GetAttributes()
4195 if attr
and attr
.HasFont():
4196 dc
.SetFont(attr
.GetFont())
4198 dc
.SetFont(self
._boldFont
)
4199 if item
.IsHyperText():
4200 dc
.SetFont(self
.GetHyperTextFont())
4201 if item
.GetVisited():
4202 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4204 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4206 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4208 image
= item
.GetCurrentImage()
4209 checkimage
= item
.GetCurrentCheckedImage()
4210 image_w
, image_h
= 0, 0
4212 if image
!= _NO_IMAGE
:
4214 if self
._imageListNormal
:
4216 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4223 if item
.GetType() != 0:
4224 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4227 wcheck
, hcheck
= 0, 0
4229 total_h
= self
.GetLineHeight(item
)
4230 drawItemBackground
= False
4232 if item
.IsSelected():
4234 # under mac selections are only a rectangle in case they don't have the focus
4235 if wx
.Platform
== "__WXMAC__":
4236 if not self
._hasFocus
:
4237 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4238 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4240 dc
.SetBrush(self
._hilightBrush
)
4242 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4243 drawItemBackground
= True
4245 if attr
and attr
.HasBackgroundColour():
4246 drawItemBackground
= True
4247 colBg
= attr
.GetBackgroundColour()
4249 colBg
= self
._backgroundColour
4251 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4252 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4254 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4256 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4258 w
, h
= self
.GetClientSize()
4260 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4262 if item
.IsSelected():
4263 if self
._usegradients
:
4264 if self
._gradientstyle
== 0: # Horizontal
4265 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4267 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4268 elif self
._vistaselection
:
4269 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4271 dc
.DrawRectangleRect(itemrect
)
4275 if item
.IsSelected():
4277 # If it's selected, and there's an image, then we should
4278 # take care to leave the area under the image painted in the
4279 # background colour.
4281 wnd
= item
.GetWindow()
4284 wndx
, wndy
= item
.GetWindowSize()
4286 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4288 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4291 if self
._usegradients
:
4292 if self
._gradientstyle
== 0: # Horizontal
4293 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4295 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4296 elif self
._vistaselection
:
4297 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4299 dc
.DrawRectangleRect(itemrect
)
4301 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4302 # don't allow backgrounds to be customized. Not drawing the background,
4303 # except for custom item backgrounds, works for both kinds of theme.
4304 elif drawItemBackground
:
4306 minusicon
= wcheck
+ image_w
- 2
4307 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4309 item
.GetWidth()-minusicon
,
4312 if self
._usegradients
and self
._hasFocus
:
4313 if self
._gradientstyle
== 0: # Horizontal
4314 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4316 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4318 dc
.DrawRectangleRect(itemrect
)
4320 if image
!= _NO_IMAGE
:
4322 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4323 if item
.IsEnabled():
4324 imglist
= self
._imageListNormal
4326 imglist
= self
._grayedImageList
4328 imglist
.Draw(image
, dc
,
4329 item
.GetX() + wcheck
,
4330 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4331 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4333 dc
.DestroyClippingRegion()
4336 if item
.IsEnabled():
4337 imglist
= self
._imageListCheck
4339 imglist
= self
._grayedCheckList
4341 imglist
.Draw(checkimage
, dc
,
4343 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4344 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4346 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4347 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4349 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4351 if not item
.IsEnabled():
4352 foreground
= dc
.GetTextForeground()
4353 dc
.SetTextForeground(self
._disabledColour
)
4354 dc
.DrawLabel(item
.GetText(), textrect
)
4355 dc
.SetTextForeground(foreground
)
4357 dc
.DrawLabel(item
.GetText(), textrect
)
4359 wnd
= item
.GetWindow()
4361 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4362 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4363 if not wnd
.IsShown():
4365 if wnd
.GetPosition() != (wndx
, ya
):
4366 wnd
.SetPosition((wndx
, ya
))
4368 # restore normal font
4369 dc
.SetFont(self
._normalFont
)
4372 # Now y stands for the top of the item, whereas it used to stand for middle !
4373 def PaintLevel(self
, item
, dc
, level
, y
):
4374 """Paint a level of CustomTreeCtrl."""
4376 x
= level
*self
._indent
4378 if not self
.HasFlag(TR_HIDE_ROOT
):
4384 # always expand hidden root
4386 children
= item
.GetChildren()
4387 count
= len(children
)
4393 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4396 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4398 # draw line down to last child
4399 origY
+= self
.GetLineHeight(children
[0])>>1
4400 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4401 dc
.DrawLine(3, origY
, 3, oldY
)
4405 item
.SetX(x
+self
._spacing
)
4408 h
= self
.GetLineHeight(item
)
4410 y_mid
= y_top
+ (h
>>1)
4413 exposed_x
= dc
.LogicalToDeviceX(0)
4414 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4416 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4417 if wx
.Platform
== "__WXMAC__":
4418 # don't draw rect outline if we already have the
4419 # background color under Mac
4420 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4422 pen
= self
._borderPen
4424 if item
.IsSelected():
4425 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4426 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4428 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4430 attr
= item
.GetAttributes()
4431 if attr
and attr
.HasTextColour():
4432 colText
= attr
.GetTextColour()
4434 colText
= self
.GetForegroundColour()
4436 if self
._vistaselection
:
4440 dc
.SetTextForeground(colText
)
4445 self
.PaintItem(item
, dc
)
4447 if self
.HasFlag(TR_ROW_LINES
):
4449 # if the background colour is white, choose a
4450 # contrasting color for the lines
4451 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4452 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4453 dc
.DrawLine(0, y_top
, 10000, y_top
)
4454 dc
.DrawLine(0, y
, 10000, y
)
4456 # restore DC objects
4457 dc
.SetBrush(wx
.WHITE_BRUSH
)
4458 dc
.SetTextForeground(wx
.BLACK
)
4460 if not self
.HasFlag(TR_NO_LINES
):
4462 # draw the horizontal line here
4463 dc
.SetPen(self
._dottedPen
)
4465 if x
> self
._indent
:
4466 x_start
-= self
._indent
4467 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4469 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4472 # should the item show a button?
4473 if item
.HasPlus() and self
.HasButtons():
4475 if self
._imageListButtons
:
4477 # draw the image button here
4480 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4481 if item
.IsSelected():
4482 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4484 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4486 yy
= y_mid
- image_h
/2
4488 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4489 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4490 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4491 dc
.DestroyClippingRegion()
4493 else: # no custom buttons
4495 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4496 # We draw something like the Mac twist buttons
4498 dc
.SetPen(wx
.BLACK_PEN
)
4499 dc
.SetBrush(self
._hilightBrush
)
4500 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4502 if item
.IsExpanded():
4504 button
[0].y
= y_mid
- 3
4506 button
[1].y
= button
[0].y
4508 button
[2].y
= button
[0].y
+ 6
4511 button
[0].y
= y_mid
- 5
4512 button
[1].x
= button
[0].x
4513 button
[1].y
= y_mid
+ 5
4514 button
[2].x
= button
[0].x
+ 5
4517 dc
.DrawPolygon(button
)
4520 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4527 if item
.IsExpanded():
4528 flag |
= _CONTROL_EXPANDED
4529 if item
== self
._underMouse
:
4530 flag |
= _CONTROL_CURRENT
4532 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4534 if item
.IsExpanded():
4536 children
= item
.GetChildren()
4537 count
= len(children
)
4546 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4549 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4551 # draw line down to last child
4552 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4553 if self
.HasButtons():
4556 # Only draw the portion of the line that is visible, in case it is huge
4557 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4558 yOrigin
= abs(yOrigin
)
4559 width
, height
= self
.GetClientSize()
4561 # Move end points to the begining/end of the view?
4564 if oldY
> yOrigin
+ height
:
4565 oldY
= yOrigin
+ height
4567 # after the adjustments if y_mid is larger than oldY then the line
4568 # isn't visible at all so don't draw anything
4570 dc
.SetPen(self
._dottedPen
)
4571 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4576 # -----------------------------------------------------------------------------
4577 # wxWidgets callbacks
4578 # -----------------------------------------------------------------------------
4580 def OnPaint(self
, event
):
4581 """Handles the wx.EVT_PAINT event."""
4583 dc
= wx
.PaintDC(self
)
4586 if not self
._anchor
:
4589 dc
.SetFont(self
._normalFont
)
4590 dc
.SetPen(self
._dottedPen
)
4593 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4596 def OnEraseBackground(self
, event
):
4597 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4599 # Can we actually do something here (or in OnPaint()) To Handle
4600 # background images that are stretchable or always centered?
4601 # I tried but I get enormous flickering...
4603 if not self
._backgroundImage
:
4607 if self
._imageStretchStyle
== _StyleTile
:
4611 dc
= wx
.ClientDC(self
)
4612 rect
= self
.GetUpdateRegion().GetBox()
4613 dc
.SetClippingRect(rect
)
4615 self
.TileBackground(dc
)
4618 def TileBackground(self
, dc
):
4619 """Tiles the background image to fill all the available area."""
4621 sz
= self
.GetClientSize()
4622 w
= self
._backgroundImage
.GetWidth()
4623 h
= self
._backgroundImage
.GetHeight()
4630 while y
< sz
.height
:
4631 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4637 def OnSetFocus(self
, event
):
4638 """Handles the wx.EVT_SET_FOCUS event."""
4640 self
._hasFocus
= True
4641 self
.RefreshSelected()
4645 def OnKillFocus(self
, event
):
4646 """Handles the wx.EVT_KILL_FOCUS event."""
4648 self
._hasFocus
= False
4649 self
.RefreshSelected()
4653 def OnKeyDown(self
, event
):
4654 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4656 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4658 te
.SetEventObject(self
)
4660 if self
.GetEventHandler().ProcessEvent(te
):
4661 # intercepted by the user code
4664 if self
._current
is None or self
._key
_current
is None:
4669 # how should the selection work for this event?
4670 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4674 # * : Expand all/Collapse all
4675 # ' ' | return : activate
4676 # up : go up (not last children!)
4678 # left : go to parent
4679 # right : open if parent and go next
4681 # end : go to last item without opening parents
4682 # alnum : start or continue searching for the item with this prefix
4684 keyCode
= event
.GetKeyCode()
4686 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4687 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4688 self
.Expand(self
._current
)
4690 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4691 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4693 self
.ExpandAll(self
._current
)
4695 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4696 if self
.IsExpanded(self
._current
):
4697 self
.Collapse(self
._current
)
4699 elif keyCode
== wx
.WXK_MENU
:
4700 # Use the item's bounding rectangle to determine position for the event
4701 itemRect
= self
.GetBoundingRect(self
._current
, True)
4702 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4703 event
._item
= self
._current
4704 # Use the left edge, vertical middle
4705 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4706 event
.SetEventObject(self
)
4707 self
.GetEventHandler().ProcessEvent(event
)
4709 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4711 if not self
.IsEnabled(self
._current
):
4715 if not event
.HasModifiers():
4716 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4717 event
._item
= self
._current
4718 event
.SetEventObject(self
)
4719 self
.GetEventHandler().ProcessEvent(event
)
4721 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4722 checked
= not self
.IsItemChecked(self
._current
)
4723 self
.CheckItem(self
._current
, checked
)
4725 # in any case, also generate the normal key event for this key,
4726 # even if we generated the ACTIVATED event above: this is what
4727 # wxMSW does and it makes sense because you might not want to
4728 # process ACTIVATED event at all and handle Space and Return
4729 # directly (and differently) which would be impossible otherwise
4732 # up goes to the previous sibling or to the last
4733 # of its children if it's expanded
4734 elif keyCode
== wx
.WXK_UP
:
4735 prev
= self
.GetPrevSibling(self
._key
_current
)
4737 prev
= self
.GetItemParent(self
._key
_current
)
4738 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4742 current
= self
._key
_current
4743 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4744 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4745 # otherwise we return to where we came from
4746 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4747 self
._key
_current
= prev
4750 current
= self
._key
_current
4752 # We are going to another parent node
4753 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4754 child
= self
.GetLastChild(prev
)
4759 # Try to get the previous siblings and see if they are active
4760 while prev
and not self
.IsEnabled(prev
):
4761 prev
= self
.GetPrevSibling(prev
)
4764 # No previous siblings active: go to the parent and up
4765 prev
= self
.GetItemParent(current
)
4766 while prev
and not self
.IsEnabled(prev
):
4767 prev
= self
.GetItemParent(prev
)
4770 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4771 self
._key
_current
= prev
4773 # left arrow goes to the parent
4774 elif keyCode
== wx
.WXK_LEFT
:
4776 prev
= self
.GetItemParent(self
._current
)
4777 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4778 # don't go to root if it is hidden
4779 prev
= self
.GetPrevSibling(self
._current
)
4781 if self
.IsExpanded(self
._current
):
4782 self
.Collapse(self
._current
)
4784 if prev
and self
.IsEnabled(prev
):
4785 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4787 elif keyCode
== wx
.WXK_RIGHT
:
4788 # this works the same as the down arrow except that we
4789 # also expand the item if it wasn't expanded yet
4790 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4791 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4792 if self
.IsEnabled(child
):
4793 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4794 self
._key
_current
= child
4796 self
.Expand(self
._current
)
4799 elif keyCode
== wx
.WXK_DOWN
:
4800 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4802 child
= self
.GetNextActiveItem(self
._key
_current
)
4805 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4806 self
._key
_current
= child
4810 next
= self
.GetNextSibling(self
._key
_current
)
4813 current
= self
._key
_current
4814 while current
and not next
:
4815 current
= self
.GetItemParent(current
)
4817 next
= self
.GetNextSibling(current
)
4818 if not self
.IsEnabled(next
):
4822 while next
and not self
.IsEnabled(next
):
4823 next
= self
.GetNext(next
)
4826 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4827 self
._key
_current
= next
4830 # <End> selects the last visible tree item
4831 elif keyCode
== wx
.WXK_END
:
4833 last
= self
.GetRootItem()
4835 while last
and self
.IsExpanded(last
):
4837 lastChild
= self
.GetLastChild(last
)
4839 # it may happen if the item was expanded but then all of
4840 # its children have been deleted - so IsExpanded() returned
4841 # true, but GetLastChild() returned invalid item
4847 if last
and self
.IsEnabled(last
):
4849 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4851 # <Home> selects the root item
4852 elif keyCode
== wx
.WXK_HOME
:
4854 prev
= self
.GetRootItem()
4859 if self
.HasFlag(TR_HIDE_ROOT
):
4860 prev
, cookie
= self
.GetFirstChild(prev
)
4864 if self
.IsEnabled(prev
):
4865 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4869 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4870 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4871 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4873 # find the next item starting with the given prefix
4875 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4881 if self
.IsEnabled(id):
4883 self
._findPrefix
+= ch
4885 # also start the timer to reset the current prefix if the user
4886 # doesn't press any more alnum keys soon -- we wouldn't want
4887 # to use this prefix for a new item search
4888 if not self
._findTimer
:
4889 self
._findTimer
= TreeFindTimer(self
)
4891 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4898 def GetNextActiveItem(self
, item
, down
=True):
4899 """Returns the next active item. Used Internally at present. """
4902 sibling
= self
.GetNextSibling
4904 sibling
= self
.GetPrevSibling
4906 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4907 # Is an unchecked radiobutton... all its children are inactive
4908 # try to get the next/previous sibling
4912 child
= sibling(item
)
4913 if (child
and self
.IsEnabled(child
)) or not child
:
4918 # Tha's not a radiobutton... but some of its children can be
4920 child
, cookie
= self
.GetFirstChild(item
)
4921 while child
and not self
.IsEnabled(child
):
4922 child
, cookie
= self
.GetNextChild(item
, cookie
)
4924 if child
and self
.IsEnabled(child
):
4930 def HitTest(self
, point
, flags
=0):
4932 Calculates which (if any) item is under the given point, returning the tree item
4933 at this point plus extra information flags. Flags is a bitlist of the following:
4935 TREE_HITTEST_ABOVE above the client area
4936 TREE_HITTEST_BELOW below the client area
4937 TREE_HITTEST_NOWHERE no item has been hit
4938 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4939 TREE_HITTEST_ONITEMICON on the icon associated to an item
4940 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4941 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4942 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4943 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4944 TREE_HITTEST_TOLEFT on the left of the client area
4945 TREE_HITTEST_TORIGHT on the right of the client area
4946 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4947 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4948 TREE_HITTEST_ONITEM anywhere on the item
4950 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4953 w
, h
= self
.GetSize()
4957 flags |
= TREE_HITTEST_TOLEFT
4959 flags |
= TREE_HITTEST_TORIGHT
4961 flags |
= TREE_HITTEST_ABOVE
4963 flags |
= TREE_HITTEST_BELOW
4968 if self
._anchor
== None:
4969 flags
= TREE_HITTEST_NOWHERE
4972 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
4975 flags
= TREE_HITTEST_NOWHERE
4978 if not self
.IsEnabled(hit
):
4984 def GetBoundingRect(self
, item
, textOnly
=False):
4985 """Gets the bounding rectangle of the item."""
4988 raise "\nERROR: Invalid Tree Item. "
4992 startX
, startY
= self
.GetViewStart()
4995 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
4996 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
4997 rect
.width
= i
.GetWidth()
4998 rect
.height
= self
.GetLineHeight(i
)
5003 def Edit(self
, item
):
5005 Internal function. Starts the editing of an item label, sending a
5006 EVT_TREE_BEGIN_LABEL_EDIT event.
5009 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5011 te
.SetEventObject(self
)
5012 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5016 # We have to call this here because the label in
5017 # question might just have been added and no screen
5018 # update taken place.
5020 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5025 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5026 self
._textCtrl
.StopEditing()
5028 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5029 self
._textCtrl
.SetFocus()
5032 def GetEditControl(self
):
5034 Returns a pointer to the edit TextCtrl if the item is being edited or
5035 None otherwise (it is assumed that no more than one item may be edited
5039 return self
._textCtrl
5042 def OnRenameAccept(self
, item
, value
):
5044 Called by TreeTextCtrl, to accept the changes and to send the
5045 EVT_TREE_END_LABEL_EDIT event.
5048 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5050 le
.SetEventObject(self
)
5052 le
._editCancelled
= False
5054 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5057 def OnRenameCancelled(self
, item
):
5059 Called by TreeTextCtrl, to cancel the changes and to send the
5060 EVT_TREE_END_LABEL_EDIT event.
5063 # let owner know that the edit was cancelled
5064 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5066 le
.SetEventObject(self
)
5068 le
._editCancelled
= True
5070 self
.GetEventHandler().ProcessEvent(le
)
5073 def OnRenameTimer(self
):
5074 """The timer for renaming has expired. Start editing."""
5076 self
.Edit(self
._current
)
5079 def OnMouse(self
, event
):
5080 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5082 if not self
._anchor
:
5085 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5087 # Is the mouse over a tree item button?
5089 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5090 underMouse
= thisItem
5091 underMouseChanged
= underMouse
!= self
._underMouse
5093 if underMouse
and (flags
& TREE_HITTEST_ONITEMBUTTON
) and not event
.LeftIsDown() and \
5094 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5095 underMouse
= underMouse
5099 if underMouse
!= self
._underMouse
:
5100 if self
._underMouse
:
5101 # unhighlight old item
5102 self
._underMouse
= None
5104 self
._underMouse
= underMouse
5106 # Determines what item we are hovering over and need a tooltip for
5107 hoverItem
= thisItem
5109 # We do not want a tooltip if we are dragging, or if the rename timer is running
5110 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5112 if hoverItem
is not None:
5113 # Ask the tree control what tooltip (if any) should be shown
5114 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5115 hevent
._item
= hoverItem
5116 hevent
.SetEventObject(self
)
5118 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5119 self
.SetToolTip(hevent
._label
)
5121 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5122 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5123 self
._isonhyperlink
= True
5125 if self
._isonhyperlink
:
5126 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5127 self
._isonhyperlink
= False
5129 # we process left mouse up event (enables in-place edit), right down
5130 # (pass to the user code), left dbl click (activate item) and
5131 # dragging/moving events for items drag-and-drop
5133 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5134 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5140 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5142 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5144 if self
._dragCount
== 0:
5145 self
._dragStart
= pt
5148 self
._dragCount
= self
._dragCount
+ 1
5150 if self
._dragCount
!= 3:
5151 # wait until user drags a bit further...
5154 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5156 nevent
= TreeEvent(command
, self
.GetId())
5157 nevent
._item
= self
._current
5158 nevent
.SetEventObject(self
)
5159 newpt
= self
.CalcScrolledPosition(pt
)
5160 nevent
.SetPoint(newpt
)
5162 # by default the dragging is not supported, the user code must
5163 # explicitly allow the event for it to take place
5166 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5168 # we're going to drag this item
5169 self
._isDragging
= True
5171 # remember the old cursor because we will change it while
5173 self
._oldCursor
= self
._cursor
5175 # in a single selection control, hide the selection temporarily
5176 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5177 self
._oldSelection
= self
.GetSelection()
5179 if self
._oldSelection
:
5181 self
._oldSelection
.SetHilight(False)
5182 self
.RefreshLine(self
._oldSelection
)
5184 selections
= self
.GetSelections()
5185 if len(selections
) == 1:
5186 self
._oldSelection
= selections
[0]
5187 self
._oldSelection
.SetHilight(False)
5188 self
.RefreshLine(self
._oldSelection
)
5193 # Create the custom draw image from the icons and the text of the item
5194 self
._dragImage
= DragImage(self
, self
._current
)
5195 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5196 self
._dragImage
.Show()
5197 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5199 elif event
.Dragging() and self
._isDragging
:
5201 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5203 if self
._countDrag
== 0 and item
:
5204 self
._oldItem
= item
5206 if item
!= self
._dropTarget
:
5208 # unhighlight the previous drop target
5209 if self
._dropTarget
:
5210 self
._dropTarget
.SetHilight(False)
5211 self
.RefreshLine(self
._dropTarget
)
5213 item
.SetHilight(True)
5214 self
.RefreshLine(item
)
5215 self
._countDrag
= self
._countDrag
+ 1
5216 self
._dropTarget
= item
5220 if self
._countDrag
>= 3:
5221 # Here I am trying to avoid ugly repainting problems... hope it works
5222 self
.RefreshLine(self
._oldItem
)
5225 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5228 self
._dragImage
.EndDrag()
5230 if self
._dropTarget
:
5231 self
._dropTarget
.SetHilight(False)
5233 if self
._oldSelection
:
5235 self
._oldSelection
.SetHilight(True)
5236 self
.RefreshLine(self
._oldSelection
)
5237 self
._oldSelection
= None
5239 # generate the drag end event
5240 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5242 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5243 event
.SetEventObject(self
)
5245 self
.GetEventHandler().ProcessEvent(event
)
5247 self
._isDragging
= False
5248 self
._dropTarget
= None
5250 self
.SetCursor(self
._oldCursor
)
5252 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5255 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5260 # If we got to this point, we are not dragging or moving the mouse.
5261 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5262 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5263 # We skip even if we didn't hit an item because we still should
5264 # restore focus to the tree control even if we didn't exactly hit an item.
5265 if event
.LeftDown():
5266 self
._hasFocus
= True
5267 self
.SetFocusIgnoringChildren()
5270 # here we process only the messages which happen on tree items
5275 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5276 self
._textCtrl
.StopEditing()
5277 return # we hit the blank area
5279 if event
.RightDown():
5281 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5282 self
._textCtrl
.StopEditing()
5284 self
._hasFocus
= True
5285 self
.SetFocusIgnoringChildren()
5287 # If the item is already selected, do not update the selection.
5288 # Multi-selections should not be cleared if a selected item is clicked.
5289 if not self
.IsSelected(item
):
5291 self
.DoSelectItem(item
, True, False)
5293 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5295 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5296 nevent
.SetEventObject(self
)
5297 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5299 # Consistent with MSW (for now), send the ITEM_MENU *after*
5300 # the RIGHT_CLICK event. TODO: This behaviour may change.
5301 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5302 nevent2
._item
= item
5303 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5304 nevent2
.SetEventObject(self
)
5305 self
.GetEventHandler().ProcessEvent(nevent2
)
5307 elif event
.LeftUp():
5309 # this facilitates multiple-item drag-and-drop
5311 if self
.HasFlag(TR_MULTIPLE
):
5313 selections
= self
.GetSelections()
5315 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5317 self
.DoSelectItem(item
, True, False)
5319 if self
._lastOnSame
:
5321 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5323 if self
._renameTimer
:
5325 if self
._renameTimer
.IsRunning():
5327 self
._renameTimer
.Stop()
5331 self
._renameTimer
= TreeRenameTimer(self
)
5333 self
._renameTimer
.Start(_DELAY
, True)
5335 self
._lastOnSame
= False
5338 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5340 if not item
or not item
.IsEnabled():
5341 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5342 self
._textCtrl
.StopEditing()
5345 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5346 self
._textCtrl
.StopEditing()
5348 self
._hasFocus
= True
5349 self
.SetFocusIgnoringChildren()
5351 if event
.LeftDown():
5353 self
._lastOnSame
= item
== self
._current
5355 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5357 # only toggle the item for a single click, double click on
5358 # the button doesn't do anything (it toggles the item twice)
5359 if event
.LeftDown():
5363 # don't select the item if the button was clicked
5366 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5368 if event
.LeftDown():
5370 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5374 # clear the previously selected items, if the
5375 # user clicked outside of the present selection.
5376 # otherwise, perform the deselection on mouse-up.
5377 # this allows multiple drag and drop to work.
5378 # but if Cmd is down, toggle selection of the clicked item
5379 if not self
.IsSelected(item
) or event
.CmdDown():
5381 if flags
& TREE_HITTEST_ONITEM
:
5382 # how should the selection work for this event?
5383 if item
.IsHyperText():
5384 self
.SetItemVisited(item
, True)
5386 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5390 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5392 # For some reason, Windows isn't recognizing a left double-click,
5393 # so we need to simulate it here. Allow 200 milliseconds for now.
5394 if event
.LeftDClick():
5396 # double clicking should not start editing the item label
5397 if self
._renameTimer
:
5398 self
._renameTimer
.Stop()
5400 self
._lastOnSame
= False
5402 # send activate event first
5403 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5405 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5406 nevent
.SetEventObject(self
)
5407 if not self
.GetEventHandler().ProcessEvent(nevent
):
5409 # if the user code didn't process the activate event,
5410 # handle it ourselves by toggling the item when it is
5412 ## if item.HasPlus():
5416 def OnInternalIdle(self
):
5417 """Performs operations in idle time (essentially drawing)."""
5419 # Check if we need to select the root item
5420 # because nothing else has been selected.
5421 # Delaying it means that we can invoke event handlers
5422 # as required, when a first item is selected.
5423 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5426 self
.SelectItem(self
._select
_me
)
5427 elif self
.GetRootItem():
5428 self
.SelectItem(self
.GetRootItem())
5430 # after all changes have been done to the tree control,
5431 # we actually redraw the tree when everything is over
5435 if self
._freezeCount
:
5440 self
.CalculatePositions()
5442 self
.AdjustMyScrollbars()
5447 def CalculateSize(self
, item
, dc
):
5448 """Calculates overall position and size of an item."""
5450 attr
= item
.GetAttributes()
5452 if attr
and attr
.HasFont():
5453 dc
.SetFont(attr
.GetFont())
5455 dc
.SetFont(self
._boldFont
)
5457 dc
.SetFont(self
._normalFont
)
5459 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5462 # restore normal font
5463 dc
.SetFont(self
._normalFont
)
5465 image_w
, image_h
= 0, 0
5466 image
= item
.GetCurrentImage()
5468 if image
!= _NO_IMAGE
:
5470 if self
._imageListNormal
:
5472 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5475 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5477 checkimage
= item
.GetCurrentCheckedImage()
5478 if checkimage
is not None:
5479 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5485 total_h
+= 2 # at least 2 pixels
5487 total_h
+= total_h
/10 # otherwise 10% extra spacing
5489 if total_h
> self
._lineHeight
:
5490 self
._lineHeight
= total_h
5492 if not item
.GetWindow():
5493 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5494 item
.SetHeight(total_h
)
5496 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5499 def CalculateLevel(self
, item
, dc
, level
, y
):
5500 """Calculates the level of an item."""
5502 x
= level
*self
._indent
5504 if not self
.HasFlag(TR_HIDE_ROOT
):
5510 # a hidden root is not evaluated, but its
5511 # children are always calculated
5512 children
= item
.GetChildren()
5513 count
= len(children
)
5515 for n
in xrange(count
):
5516 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5520 self
.CalculateSize(item
, dc
)
5523 item
.SetX(x
+self
._spacing
)
5525 y
+= self
.GetLineHeight(item
)
5527 if not item
.IsExpanded():
5528 # we don't need to calculate collapsed branches
5531 children
= item
.GetChildren()
5532 count
= len(children
)
5534 for n
in xrange(count
):
5535 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5540 def CalculatePositions(self
):
5541 """Calculates all the positions of the visible items."""
5543 if not self
._anchor
:
5546 dc
= wx
.ClientDC(self
)
5549 dc
.SetFont(self
._normalFont
)
5550 dc
.SetPen(self
._dottedPen
)
5552 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5555 def RefreshSubtree(self
, item
):
5556 """Refreshes a damaged subtree of an item."""
5560 if self
._freezeCount
:
5563 client
= self
.GetClientSize()
5566 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5567 rect
.width
= client
.x
5568 rect
.height
= client
.y
5570 self
.Refresh(True, rect
)
5571 self
.AdjustMyScrollbars()
5574 def RefreshLine(self
, item
):
5575 """Refreshes a damaged item line."""
5579 if self
._freezeCount
:
5583 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5584 rect
.width
= self
.GetClientSize().x
5585 rect
.height
= self
.GetLineHeight(item
)
5587 self
.Refresh(True, rect
)
5590 def RefreshSelected(self
):
5591 """Refreshes a damaged selected item line."""
5593 if self
._freezeCount
:
5596 # TODO: this is awfully inefficient, we should keep the list of all
5597 # selected items internally, should be much faster
5599 self
.RefreshSelectedUnder(self
._anchor
)
5602 def RefreshSelectedUnder(self
, item
):
5603 """Refreshes the selected items under the given item."""
5605 if self
._freezeCount
:
5608 if item
.IsSelected():
5609 self
.RefreshLine(item
)
5611 children
= item
.GetChildren()
5612 for child
in children
:
5613 self
.RefreshSelectedUnder(child
)
5617 """Freeze CustomTreeCtrl."""
5619 self
._freezeCount
= self
._freezeCount
+ 1
5623 """Thaw CustomTreeCtrl."""
5625 if self
._freezeCount
== 0:
5626 raise "\nERROR: Thawing Unfrozen Tree Control?"
5628 self
._freezeCount
= self
._freezeCount
- 1
5630 if not self
._freezeCount
:
5634 # ----------------------------------------------------------------------------
5635 # changing colours: we need to refresh the tree control
5636 # ----------------------------------------------------------------------------
5638 def SetBackgroundColour(self
, colour
):
5639 """Changes the background colour of CustomTreeCtrl."""
5641 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5644 if self
._freezeCount
:
5652 def SetForegroundColour(self
, colour
):
5653 """Changes the foreground colour of CustomTreeCtrl."""
5655 if not wx
.Window
.SetForegroundColour(self
, colour
):
5658 if self
._freezeCount
:
5666 def OnGetToolTip(self
, event
):
5668 Process the tooltip event, to speed up event processing. Does not actually
5675 def DoGetBestSize(self
):
5676 """Something is better than nothing..."""
5678 # something is better than nothing...
5679 # 100x80 is what the MSW version will get from the default
5680 # wxControl::DoGetBestSize
5682 return wx
.Size(100, 80)
5685 def GetClassDefaultAttributes(self
):
5686 """Gets the class default attributes."""
5688 attr
= wx
.VisualAttributes()
5689 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5690 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5691 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)