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 3 more styles to handle checkbox-type items:
99 - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children;
100 - TR_AUTO_CHECK_PARENT : automatically checks/unchecks the item parent;
101 - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children.
103 All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl.
109 All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with
112 - EVT_TREE_GET_INFO (don't know what this means);
113 - EVT_TREE_SET_INFO (don't know what this means);
114 - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add);
115 - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below.
117 Plus, CustomTreeCtrl supports the events related to the checkbutton-type items:
119 - EVT_TREE_ITEM_CHECKING: an item is being checked;
120 - EVT_TREE_ITEM_CHECKED: an item has been checked.
122 And to hyperlink-type items:
124 - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent
125 after the EVT_TREE_SEL_CHANGED event).
131 CustomTreeCtrl has been tested on the following platforms:
132 * Windows (Windows XP);
133 * GTK (Thanks to Michele Petrazzo);
134 * Mac OS (Thanks to John Jackson).
137 Latest Revision: Andrea Gavana @ 26 May 2006, 22.30 CET
147 # ----------------------------------------------------------------------------
149 # ----------------------------------------------------------------------------
152 _PIXELS_PER_UNIT
= 10
154 # Start editing the current item after half a second (if the mouse hasn't
155 # been clicked/moved)
158 # ----------------------------------------------------------------------------
160 # ----------------------------------------------------------------------------
162 # Enum for different images associated with a treectrl item
163 TreeItemIcon_Normal
= 0 # not selected, not expanded
164 TreeItemIcon_Selected
= 1 # selected, not expanded
165 TreeItemIcon_Expanded
= 2 # not selected, expanded
166 TreeItemIcon_SelectedExpanded
= 3 # selected, expanded
168 TreeItemIcon_Checked
= 0 # check button, checked
169 TreeItemIcon_NotChecked
= 1 # check button, not checked
170 TreeItemIcon_Flagged
= 2 # radio button, selected
171 TreeItemIcon_NotFlagged
= 3 # radio button, not selected
173 # ----------------------------------------------------------------------------
174 # CustomTreeCtrl flags
175 # ----------------------------------------------------------------------------
177 TR_NO_BUTTONS
= wx
.TR_NO_BUTTONS
# for convenience
178 TR_HAS_BUTTONS
= wx
.TR_HAS_BUTTONS
# draw collapsed/expanded btns
179 TR_NO_LINES
= wx
.TR_NO_LINES
# don't draw lines at all
180 TR_LINES_AT_ROOT
= wx
.TR_LINES_AT_ROOT
# connect top-level nodes
181 TR_TWIST_BUTTONS
= wx
.TR_TWIST_BUTTONS
# still used by wxTreeListCtrl
183 TR_SINGLE
= wx
.TR_SINGLE
# for convenience
184 TR_MULTIPLE
= wx
.TR_MULTIPLE
# can select multiple items
185 TR_EXTENDED
= wx
.TR_EXTENDED
# TODO: allow extended selection
186 TR_HAS_VARIABLE_ROW_HEIGHT
= wx
.TR_HAS_VARIABLE_ROW_HEIGHT
# what it says
188 TR_EDIT_LABELS
= wx
.TR_EDIT_LABELS
# can edit item labels
189 TR_ROW_LINES
= wx
.TR_ROW_LINES
# put border around items
190 TR_HIDE_ROOT
= wx
.TR_HIDE_ROOT
# don't display root node
192 TR_FULL_ROW_HIGHLIGHT
= wx
.TR_FULL_ROW_HIGHLIGHT
# highlight full horz space
194 TR_AUTO_CHECK_CHILD
= 0x04000 # only meaningful for checkboxes
195 TR_AUTO_TOGGLE_CHILD
= 0x08000 # only meaningful for checkboxes
196 TR_AUTO_CHECK_PARENT
= 0x10000 # only meaningful for checkboxes
198 TR_DEFAULT_STYLE
= wx
.TR_DEFAULT_STYLE
# default style for the tree control
200 # Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine
201 # where exactly the specified point is situated:
203 TREE_HITTEST_ABOVE
= wx
.TREE_HITTEST_ABOVE
204 TREE_HITTEST_BELOW
= wx
.TREE_HITTEST_BELOW
205 TREE_HITTEST_NOWHERE
= wx
.TREE_HITTEST_NOWHERE
206 # on the button associated with an item.
207 TREE_HITTEST_ONITEMBUTTON
= wx
.TREE_HITTEST_ONITEMBUTTON
208 # on the bitmap associated with an item.
209 TREE_HITTEST_ONITEMICON
= wx
.TREE_HITTEST_ONITEMICON
210 # on the indent associated with an item.
211 TREE_HITTEST_ONITEMINDENT
= wx
.TREE_HITTEST_ONITEMINDENT
212 # on the label (string) associated with an item.
213 TREE_HITTEST_ONITEMLABEL
= wx
.TREE_HITTEST_ONITEMLABEL
214 # on the right of the label associated with an item.
215 TREE_HITTEST_ONITEMRIGHT
= wx
.TREE_HITTEST_ONITEMRIGHT
216 # on the label (string) associated with an item.
217 TREE_HITTEST_ONITEMSTATEICON
= wx
.TREE_HITTEST_ONITEMSTATEICON
218 # on the left of the CustomTreeCtrl.
219 TREE_HITTEST_TOLEFT
= wx
.TREE_HITTEST_TOLEFT
220 # on the right of the CustomTreeCtrl.
221 TREE_HITTEST_TORIGHT
= wx
.TREE_HITTEST_TORIGHT
222 # on the upper part (first half) of the item.
223 TREE_HITTEST_ONITEMUPPERPART
= wx
.TREE_HITTEST_ONITEMUPPERPART
224 # on the lower part (second half) of the item.
225 TREE_HITTEST_ONITEMLOWERPART
= wx
.TREE_HITTEST_ONITEMLOWERPART
226 # on the check icon, if present
227 TREE_HITTEST_ONITEMCHECKICON
= 0x4000
228 # anywhere on the item
229 TREE_HITTEST_ONITEM
= TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON
232 # Background Image Style
236 # Windows Vista Colours
237 _rgbSelectOuter
= wx
.Colour(170, 200, 245)
238 _rgbSelectInner
= wx
.Colour(230, 250, 250)
239 _rgbSelectTop
= wx
.Colour(210, 240, 250)
240 _rgbSelectBottom
= wx
.Colour(185, 215, 250)
241 _rgbNoFocusTop
= wx
.Colour(250, 250, 250)
242 _rgbNoFocusBottom
= wx
.Colour(235, 235, 235)
243 _rgbNoFocusOuter
= wx
.Colour(220, 220, 220)
244 _rgbNoFocusInner
= wx
.Colour(245, 245, 245)
246 # Flags for wx.RendererNative
247 _CONTROL_EXPANDED
= 8
248 _CONTROL_CURRENT
= 16
254 # ----------------------------------------------------------------------------
255 # CustomTreeCtrl events and binding for handling them
256 # ----------------------------------------------------------------------------
258 wxEVT_TREE_BEGIN_DRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_DRAG
259 wxEVT_TREE_BEGIN_RDRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_RDRAG
260 wxEVT_TREE_BEGIN_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
261 wxEVT_TREE_END_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_END_LABEL_EDIT
262 wxEVT_TREE_DELETE_ITEM
= wx
.wxEVT_COMMAND_TREE_DELETE_ITEM
263 wxEVT_TREE_GET_INFO
= wx
.wxEVT_COMMAND_TREE_GET_INFO
264 wxEVT_TREE_SET_INFO
= wx
.wxEVT_COMMAND_TREE_SET_INFO
265 wxEVT_TREE_ITEM_EXPANDED
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDED
266 wxEVT_TREE_ITEM_EXPANDING
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDING
267 wxEVT_TREE_ITEM_COLLAPSED
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
268 wxEVT_TREE_ITEM_COLLAPSING
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
269 wxEVT_TREE_SEL_CHANGED
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGED
270 wxEVT_TREE_SEL_CHANGING
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGING
271 wxEVT_TREE_KEY_DOWN
= wx
.wxEVT_COMMAND_TREE_KEY_DOWN
272 wxEVT_TREE_ITEM_ACTIVATED
= wx
.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
273 wxEVT_TREE_ITEM_RIGHT_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
274 wxEVT_TREE_ITEM_MIDDLE_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
275 wxEVT_TREE_END_DRAG
= wx
.wxEVT_COMMAND_TREE_END_DRAG
276 wxEVT_TREE_STATE_IMAGE_CLICK
= wx
.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
277 wxEVT_TREE_ITEM_GETTOOLTIP
= wx
.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
278 wxEVT_TREE_ITEM_MENU
= wx
.wxEVT_COMMAND_TREE_ITEM_MENU
279 wxEVT_TREE_ITEM_CHECKING
= wx
.NewEventType()
280 wxEVT_TREE_ITEM_CHECKED
= wx
.NewEventType()
281 wxEVT_TREE_ITEM_HYPERLINK
= wx
.NewEventType()
283 EVT_TREE_BEGIN_DRAG
= wx
.EVT_TREE_BEGIN_DRAG
284 EVT_TREE_BEGIN_RDRAG
= wx
.EVT_TREE_BEGIN_RDRAG
285 EVT_TREE_BEGIN_LABEL_EDIT
= wx
.EVT_TREE_BEGIN_LABEL_EDIT
286 EVT_TREE_END_LABEL_EDIT
= wx
.EVT_TREE_END_LABEL_EDIT
287 EVT_TREE_DELETE_ITEM
= wx
.EVT_TREE_DELETE_ITEM
288 EVT_TREE_GET_INFO
= wx
.EVT_TREE_GET_INFO
289 EVT_TREE_SET_INFO
= wx
.EVT_TREE_SET_INFO
290 EVT_TREE_ITEM_EXPANDED
= wx
.EVT_TREE_ITEM_EXPANDED
291 EVT_TREE_ITEM_EXPANDING
= wx
.EVT_TREE_ITEM_EXPANDING
292 EVT_TREE_ITEM_COLLAPSED
= wx
.EVT_TREE_ITEM_COLLAPSED
293 EVT_TREE_ITEM_COLLAPSING
= wx
.EVT_TREE_ITEM_COLLAPSING
294 EVT_TREE_SEL_CHANGED
= wx
.EVT_TREE_SEL_CHANGED
295 EVT_TREE_SEL_CHANGING
= wx
.EVT_TREE_SEL_CHANGING
296 EVT_TREE_KEY_DOWN
= wx
.EVT_TREE_KEY_DOWN
297 EVT_TREE_ITEM_ACTIVATED
= wx
.EVT_TREE_ITEM_ACTIVATED
298 EVT_TREE_ITEM_RIGHT_CLICK
= wx
.EVT_TREE_ITEM_RIGHT_CLICK
299 EVT_TREE_ITEM_MIDDLE_CLICK
= wx
.EVT_TREE_ITEM_MIDDLE_CLICK
300 EVT_TREE_END_DRAG
= wx
.EVT_TREE_END_DRAG
301 EVT_TREE_STATE_IMAGE_CLICK
= wx
.EVT_TREE_STATE_IMAGE_CLICK
302 EVT_TREE_ITEM_GETTOOLTIP
= wx
.EVT_TREE_ITEM_GETTOOLTIP
303 EVT_TREE_ITEM_MENU
= wx
.EVT_TREE_ITEM_MENU
304 EVT_TREE_ITEM_CHECKING
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKING
, 1)
305 EVT_TREE_ITEM_CHECKED
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKED
, 1)
306 EVT_TREE_ITEM_HYPERLINK
= wx
.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK
, 1)
309 def GetFlaggedData():
310 return zlib
.decompress(
311 'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
312 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
313 |\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\
314 \x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\
315 \xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\
316 \xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\
317 \xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\
318 \xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\
319 \r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\
320 \xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\
321 \xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\
322 \xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\
323 \x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\
324 \xb3\xb7H,a\xe1\xa9)\xdf<e$2Ble\x85\x94e\xb1\x96\xcep\xfb\xdd-D\x04\xa5\x14\
325 \xdeZ\'\xb1\x84\x85\xd8\x8bm\x84\xe6\x977\x7f8kog)\xba\xc4\xb7\xe5\xef$\xe2?\
326 \xe9\xa9\xbf\x86R\n\x11a&\x1c\xc1^lC|\r.\x02\xb3\x8b\x9b\xa6&G\x13W\xaa\xbb\
327 \x91_\x05\x0c\x1d\xbfI\xc7\xa1\x8e\xbf&a|:\x8c\xaf\xc1\x05J4\x8e\xd6>36\x192\
328 \xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\
329 \xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\
330 nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\
331 \xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\
332 \xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' )
334 def GetFlaggedBitmap():
335 return wx
.BitmapFromImage(GetFlaggedImage())
337 def GetFlaggedImage():
338 stream
= cStringIO
.StringIO(GetFlaggedData())
339 return wx
.ImageFromStream(stream
)
341 #----------------------------------------------------------------------
342 def GetNotFlaggedData():
343 return zlib
.decompress(
344 'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
345 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
346 |\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\
347 \x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\
348 \x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\
349 \x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\
350 \x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\
351 \xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\
352 \x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\
353 \xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\
354 \x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\
355 \xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\
356 \x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\
357 \x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\
358 \xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\
359 \xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\
360 \xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' )
362 def GetNotFlaggedBitmap():
363 return wx
.BitmapFromImage(GetNotFlaggedImage())
365 def GetNotFlaggedImage():
366 stream
= cStringIO
.StringIO(GetNotFlaggedData())
367 return wx
.ImageFromStream(stream
)
369 #----------------------------------------------------------------------
370 def GetCheckedData():
371 return zlib
.decompress(
372 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
373 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\
374 \x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\
375 \xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\
376 \x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\
377 \xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\
378 \xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\
379 \xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\
380 \x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\
381 \xeb\x9c\x12\x9a\x00\x92\xccS\x9f" )
383 def GetCheckedBitmap():
384 return wx
.BitmapFromImage(GetCheckedImage())
386 def GetCheckedImage():
387 stream
= cStringIO
.StringIO(GetCheckedData())
388 return wx
.ImageFromStream(stream
)
390 #----------------------------------------------------------------------
391 def GetNotCheckedData():
392 return zlib
.decompress(
393 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
394 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\
395 \xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\
396 \xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\
397 \x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\
398 \xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\
399 \xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\
402 def GetNotCheckedBitmap():
403 return wx
.BitmapFromImage(GetNotCheckedImage())
405 def GetNotCheckedImage():
406 stream
= cStringIO
.StringIO(GetNotCheckedData())
407 return wx
.ImageFromStream(stream
)
410 def GrayOut(anImage
):
412 Convert the given image (in place) to a grayed-out version,
413 appropriate for a 'disabled' appearance.
416 factor
= 0.7 # 0 < f < 1. Higher Is Grayer
418 if anImage
.HasMask():
419 maskColor
= (anImage
.GetMaskRed(), anImage
.GetMaskGreen(), anImage
.GetMaskBlue())
423 data
= map(ord, list(anImage
.GetData()))
425 for i
in range(0, len(data
), 3):
427 pixel
= (data
[i
], data
[i
+1], data
[i
+2])
428 pixel
= MakeGray(pixel
, factor
, maskColor
)
433 anImage
.SetData(''.join(map(chr, data
)))
438 def MakeGray((r
,g
,b
), factor
, maskColor
):
440 Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be
444 if (r
,g
,b
) != maskColor
:
445 return map(lambda x
: int((230 - x
) * factor
) + x
, (r
,g
,b
))
450 def DrawTreeItemButton(win
, dc
, rect
, flags
):
451 """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """
454 dc
.SetPen(wx
.GREY_PEN
)
455 dc
.SetBrush(wx
.WHITE_BRUSH
)
456 dc
.DrawRectangleRect(rect
)
459 xMiddle
= rect
.x
+ rect
.width
/2
460 yMiddle
= rect
.y
+ rect
.height
/2
462 # half of the length of the horz lines in "-" and "+"
463 halfWidth
= rect
.width
/2 - 2
464 dc
.SetPen(wx
.BLACK_PEN
)
465 dc
.DrawLine(xMiddle
- halfWidth
, yMiddle
,
466 xMiddle
+ halfWidth
+ 1, yMiddle
)
468 if not flags
& _CONTROL_EXPANDED
:
471 halfHeight
= rect
.height
/2 - 2
472 dc
.DrawLine(xMiddle
, yMiddle
- halfHeight
,
473 xMiddle
, yMiddle
+ halfHeight
+ 1)
476 #---------------------------------------------------------------------------
477 # DragImage Implementation
478 # This Class Handles The Creation Of A Custom Image In Case Of Item Drag
480 #---------------------------------------------------------------------------
482 class DragImage(wx
.DragImage
):
484 This class handles the creation of a custom image in case of item drag
488 def __init__(self
, treeCtrl
, item
):
490 Default class constructor.
491 For internal use: do not call it in your code!
494 text
= item
.GetText()
495 font
= item
.Attr().GetFont()
496 colour
= item
.Attr().GetTextColour()
500 font
= treeCtrl
._normalFont
502 backcolour
= treeCtrl
.GetBackgroundColour()
503 r
, g
, b
= int(backcolour
.Red()), int(backcolour
.Green()), int(backcolour
.Blue())
504 backcolour
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
505 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
506 self
._backgroundColour
= backcolour
508 tempdc
= wx
.ClientDC(treeCtrl
)
510 width
, height
, dummy
= tempdc
.GetMultiLineTextExtent(text
+ "M")
512 image
= item
.GetCurrentImage()
514 image_w
, image_h
= 0, 0
515 wcheck
, hcheck
= 0, 0
523 if image
!= _NO_IMAGE
:
524 if treeCtrl
._imageListNormal
:
525 image_w
, image_h
= treeCtrl
._imageListNormal
.GetSize(image
)
527 itemimage
= treeCtrl
._imageListNormal
.GetBitmap(image
)
529 checkimage
= item
.GetCurrentCheckedImage()
531 if checkimage
is not None:
532 if treeCtrl
._imageListCheck
:
533 wcheck
, hcheck
= treeCtrl
._imageListCheck
.GetSize(checkimage
)
535 itemcheck
= treeCtrl
._imageListCheck
.GetBitmap(checkimage
)
537 total_h
= max(hcheck
, height
)
538 total_h
= max(image_h
, total_h
)
542 yimagepos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0]
544 if checkimage
is not None:
546 ycheckpos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0] + 2
548 extraH
= ((total_h
> height
) and [(total_h
- height
)/2] or [0])[0]
550 xtextpos
= wcheck
+ image_w
553 total_h
= max(image_h
, hcheck
)
554 total_h
= max(total_h
, height
)
557 total_h
+= 2 # at least 2 pixels
559 total_h
+= total_h
/10 # otherwise 10% extra spacing
561 total_w
= image_w
+ wcheck
+ width
563 self
._total
_w
= total_w
564 self
._total
_h
= total_h
565 self
._itemimage
= itemimage
566 self
._itemcheck
= itemcheck
568 self
._colour
= colour
570 self
._xtextpos
= xtextpos
571 self
._ytextpos
= ytextpos
572 self
._ximagepos
= ximagepos
573 self
._yimagepos
= yimagepos
574 self
._xcheckpos
= xcheckpos
575 self
._ycheckpos
= ycheckpos
576 self
._textwidth
= width
577 self
._textheight
= height
578 self
._extraH
= extraH
580 self
._bitmap
= self
.CreateBitmap()
582 wx
.DragImage
.__init
__(self
, self
._bitmap
)
585 def CreateBitmap(self
):
586 """Actually creates the dnd bitmap."""
588 memory
= wx
.MemoryDC()
590 bitmap
= wx
.EmptyBitmap(self
._total
_w
, self
._total
_h
)
591 memory
.SelectObject(bitmap
)
593 memory
.SetTextBackground(self
._backgroundColour
)
594 memory
.SetBackground(wx
.Brush(self
._backgroundColour
))
595 memory
.SetFont(self
._font
)
596 memory
.SetTextForeground(self
._colour
)
600 memory
.DrawBitmap(self
._itemimage
, self
._ximagepos
, self
._yimagepos
, True)
603 memory
.DrawBitmap(self
._itemcheck
, self
._xcheckpos
, self
._ycheckpos
, True)
605 textrect
= wx
.Rect(self
._xtextpos
, self
._ytextpos
+self
._extraH
, self
._textwidth
, self
._textheight
)
606 memory
.DrawLabel(self
._text
, textrect
)
608 memory
.SelectObject(wx
.NullBitmap
)
613 # ----------------------------------------------------------------------------
614 # TreeItemAttr: a structure containing the visual attributes of an item
615 # ----------------------------------------------------------------------------
618 """Creates the item attributes (text colour, background colour and font)."""
620 def __init__(self
, colText
=wx
.NullColour
, colBack
=wx
.NullColour
, font
=wx
.NullFont
):
622 Default class constructor.
623 For internal use: do not call it in your code!
626 self
._colText
= colText
627 self
._colBack
= colBack
631 def SetTextColour(self
, colText
):
632 """Sets the attribute text colour."""
634 self
._colText
= colText
637 def SetBackgroundColour(self
, colBack
):
638 """Sets the attribute background colour."""
640 self
._colBack
= colBack
643 def SetFont(self
, font
):
644 """Sets the attribute font."""
650 def HasTextColour(self
):
651 """Returns whether the attribute has text colour."""
653 return self
._colText
!= wx
.NullColour
656 def HasBackgroundColour(self
):
657 """Returns whether the attribute has background colour."""
659 return self
._colBack
!= wx
.NullColour
663 """Returns whether the attribute has font."""
665 return self
._font
!= wx
.NullFont
669 def GetTextColour(self
):
670 """Returns the attribute text colour."""
675 def GetBackgroundColour(self
):
676 """Returns the attribute background colour."""
682 """Returns the attribute font."""
687 # ----------------------------------------------------------------------------
688 # CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent
690 # NB: Note That Not All The Accessors Make Sense For All The Events, See The
691 # Event Description Below.
692 # ----------------------------------------------------------------------------
694 class CommandTreeEvent(wx
.PyCommandEvent
):
696 CommandTreeEvent is a special subclassing of wx.PyCommandEvent.
697 NB: note that not all the accessors make sense for all the events, see the
698 event description for every method in this class.
701 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
702 label
=None, **kwargs
):
704 Default class constructor.
705 For internal use: do not call it in your code!
708 wx
.PyCommandEvent
.__init
__(self
, type, id, **kwargs
)
710 self
._evtKey
= evtKey
711 self
._pointDrag
= point
717 Gets the item on which the operation was performed or the newly selected
718 item for EVT_TREE_SEL_CHANGED/ING events.
724 def SetItem(self
, item
):
726 Sets the item on which the operation was performed or the newly selected
727 item for EVT_TREE_SEL_CHANGED/ING events.
733 def GetOldItem(self
):
734 """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item."""
739 def SetOldItem(self
, item
):
740 """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item."""
747 Returns the point where the mouse was when the drag operation started
748 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
751 return self
._pointDrag
754 def SetPoint(self
, pt
):
756 Sets the point where the mouse was when the drag operation started
757 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
763 def GetKeyEvent(self
):
764 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
769 def GetKeyCode(self
):
770 """Returns the integer key code (for EVT_TREE_KEY_DOWN only)."""
772 return self
._evtKey
.GetKeyCode()
775 def SetKeyEvent(self
, evt
):
776 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
782 """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
787 def SetLabel(self
, label
):
788 """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
793 def IsEditCancelled(self
):
794 """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
796 return self
._editCancelled
799 def SetEditCanceled(self
, editCancelled
):
800 """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
802 self
._editCancelled
= editCancelled
805 def SetToolTip(self
, toolTip
):
806 """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
808 self
._label
= toolTip
811 def GetToolTip(self
):
812 """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
817 # ----------------------------------------------------------------------------
818 # TreeEvent is a special class for all events associated with tree controls
820 # NB: note that not all accessors make sense for all events, see the event
822 # ----------------------------------------------------------------------------
824 class TreeEvent(CommandTreeEvent
):
826 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
827 label
=None, **kwargs
):
829 Default class constructor.
830 For internal use: do not call it in your code!
833 CommandTreeEvent
.__init
__(self
, type, id, item
, evtKey
, point
, label
, **kwargs
)
834 self
.notify
= wx
.NotifyEvent(type, id)
837 def GetNotifyEvent(self
):
838 """Returns the actual wx.NotifyEvent."""
844 """Returns whether the event is allowed or not."""
846 return self
.notify
.IsAllowed()
850 """Vetos the event."""
856 """The event is allowed."""
861 # -----------------------------------------------------------------------------
862 # Auxiliary Classes: TreeRenameTimer
863 # -----------------------------------------------------------------------------
865 class TreeRenameTimer(wx
.Timer
):
866 """Timer used for enabling in-place edit."""
868 def __init__(self
, owner
):
870 Default class constructor.
871 For internal use: do not call it in your code!
874 wx
.Timer
.__init
__(self
)
879 """The timer has expired."""
881 self
._owner
.OnRenameTimer()
884 # -----------------------------------------------------------------------------
885 # Auxiliary Classes: TreeTextCtrl
886 # This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item
887 # -----------------------------------------------------------------------------
889 class TreeTextCtrl(wx
.TextCtrl
):
890 """Control used for in-place edit."""
892 def __init__(self
, owner
, item
=None):
894 Default class constructor.
895 For internal use: do not call it in your code!
899 self
._itemEdited
= item
900 self
._startValue
= item
.GetText()
901 self
._finished
= False
902 self
._aboutToFinish
= False
904 w
= self
._itemEdited
.GetWidth()
905 h
= self
._itemEdited
.GetHeight()
907 wnd
= self
._itemEdited
.GetWindow()
909 w
= w
- self
._itemEdited
.GetWindowSize()[0]
912 x
, y
= self
._owner
.CalcScrolledPosition(item
.GetX(), item
.GetY())
917 image
= item
.GetCurrentImage()
919 if image
!= _NO_IMAGE
:
921 if self
._owner
._imageListNormal
:
922 image_w
, image_h
= self
._owner
._imageListNormal
.GetSize(image
)
927 raise Exception("\n ERROR: You Must Create An Image List To Use Images!")
929 checkimage
= item
.GetCurrentCheckedImage()
931 if checkimage
is not None:
932 wcheck
, hcheck
= self
._owner
._imageListCheck
.GetSize(checkimage
)
938 h
= max(hcheck
, image_h
)
939 dc
= wx
.ClientDC(self
._owner
)
940 h
= max(h
, dc
.GetTextExtent("Aq")[1])
943 # FIXME: what are all these hardcoded 4, 8 and 11s really?
944 x
+= image_w
+ wcheck
945 w
-= image_w
+ 4 + wcheck
947 wx
.TextCtrl
.__init
__(self
, self
._owner
, wx
.ID_ANY
, self
._startValue
,
948 wx
.Point(x
- 4, y
), wx
.Size(w
+ 15, h
))
949 if wx
.Platform
== "__WXMAC__":
950 self
.SetFont(owner
.GetFont())
951 bs
= self
.GetBestSize()
952 self
.SetSize((-1, bs
.height
))
954 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
955 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
956 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
959 def AcceptChanges(self
):
960 """Accepts/refuses the changes made by the user."""
962 value
= self
.GetValue()
964 if value
== self
._startValue
:
965 # nothing changed, always accept
966 # when an item remains unchanged, the owner
967 # needs to be notified that the user decided
968 # not to change the tree item label, and that
969 # the edit has been cancelled
970 self
._owner
.OnRenameCancelled(self
._itemEdited
)
973 if not self
._owner
.OnRenameAccept(self
._itemEdited
, value
):
977 # accepted, do rename the item
978 self
._owner
.SetItemText(self
._itemEdited
, value
)
984 """Finish editing."""
986 if not self
._finished
:
988 ## wxPendingDelete.Append(this)
989 self
._finished
= True
990 self
._owner
.SetFocusIgnoringChildren()
991 self
._owner
.ResetTextControl()
994 def OnChar(self
, event
):
995 """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
997 keycode
= event
.GetKeyCode()
999 if keycode
== wx
.WXK_RETURN
:
1000 self
._aboutToFinish
= True
1001 # Notify the owner about the changes
1002 self
.AcceptChanges()
1003 # Even if vetoed, close the control (consistent with MSW)
1004 wx
.CallAfter(self
.Finish
)
1006 elif keycode
== wx
.WXK_ESCAPE
:
1013 def OnKeyUp(self
, event
):
1014 """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
1016 if not self
._finished
:
1018 # auto-grow the textctrl:
1019 parentSize
= self
._owner
.GetSize()
1020 myPos
= self
.GetPosition()
1021 mySize
= self
.GetSize()
1023 sx
, sy
= self
.GetTextExtent(self
.GetValue() + "M")
1024 if myPos
.x
+ sx
> parentSize
.x
:
1025 sx
= parentSize
.x
- myPos
.x
1029 self
.SetSize((sx
, -1))
1034 def OnKillFocus(self
, event
):
1035 """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
1037 # I commented out those lines, and everything seems to work fine.
1038 # But why in the world are these lines of code here? Maybe GTK
1039 # or MAC give troubles?
1041 ## if not self._finished and not self._aboutToFinish:
1043 ## # We must finish regardless of success, otherwise we'll get
1044 ## # focus problems:
1046 ## if not self.AcceptChanges():
1047 ## self._owner.OnRenameCancelled(self._itemEdited)
1049 # We must let the native text control handle focus, too, otherwise
1050 # it could have problems with the cursor (e.g., in wxGTK).
1054 def StopEditing(self
):
1055 """Suddenly stops the editing."""
1057 self
._owner
.OnRenameCancelled(self
._itemEdited
)
1062 """Returns the item currently edited."""
1064 return self
._itemEdited
1067 # -----------------------------------------------------------------------------
1068 # Auxiliary Classes: TreeFindTimer
1069 # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
1070 # Sufficiently Long Time.
1071 # -----------------------------------------------------------------------------
1073 class TreeFindTimer(wx
.Timer
):
1075 Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
1076 for a sufficiently long time.
1079 def __init__(self
, owner
):
1081 Default class constructor.
1082 For internal use: do not call it in your code!
1085 wx
.Timer
.__init
__(self
)
1090 """The timer has expired."""
1092 self
._owner
._findPrefix
= ""
1095 # -----------------------------------------------------------------------------
1096 # GenericTreeItem Implementation.
1097 # This Class Holds All The Information And Methods For Every Single Item In
1099 # -----------------------------------------------------------------------------
1101 class GenericTreeItem
:
1103 This class holds all the information and methods for every single item in
1104 CustomTreeCtrl. No wx based.
1107 def __init__(self
, parent
, text
="", ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
1109 Default class constructor.
1110 For internal use: do not call it in your code!
1113 # since there can be very many of these, we save size by chosing
1114 # the smallest representation for the elements and by ordering
1115 # the members to avoid padding.
1116 self
._text
= text
# label to be rendered for item
1117 self
._data
= data
# user-provided data
1119 self
._children
= [] # list of children
1120 self
._parent
= parent
# parent of this item
1122 self
._attr
= None # attributes???
1124 # tree ctrl images for the normal, selected, expanded and
1125 # expanded+selected states
1126 self
._images
= [-1, -1, -1, -1]
1127 self
._images
[TreeItemIcon_Normal
] = image
1128 self
._images
[TreeItemIcon_Selected
] = selImage
1129 self
._images
[TreeItemIcon_Expanded
] = _NO_IMAGE
1130 self
._images
[TreeItemIcon_SelectedExpanded
] = _NO_IMAGE
1132 self
._checkedimages
= [None, None, None, None]
1134 self
._x
= 0 # (virtual) offset from top
1135 self
._y
= 0 # (virtual) offset from left
1136 self
._width
= 0 # width of this item
1137 self
._height
= 0 # height of this item
1139 self
._isCollapsed
= True
1140 self
._hasHilight
= False # same as focused
1141 self
._hasPlus
= False # used for item which doesn't have
1142 # children but has a [+] button
1143 self
._isBold
= False # render the label in bold font
1144 self
._isItalic
= False # render the label in italic font
1145 self
._ownsAttr
= False # delete attribute when done
1146 self
._type
= ct_type
# item type: 0=normal, 1=check, 2=radio
1147 self
._checked
= False # only meaningful for check and radio
1148 self
._enabled
= True # flag to enable/disable an item
1149 self
._hypertext
= False # indicates if the item is hypertext
1150 self
._visited
= False # visited state for an hypertext item
1153 # do not construct the array for normal items
1154 self
._checkedimages
[TreeItemIcon_Checked
] = 0
1155 self
._checkedimages
[TreeItemIcon_NotChecked
] = 1
1156 self
._checkedimages
[TreeItemIcon_Flagged
] = 2
1157 self
._checkedimages
[TreeItemIcon_NotFlagged
] = 3
1160 if parent
.GetType() == 2 and not parent
.IsChecked():
1161 # if the node parent is a radio not enabled, we are disabled
1162 self
._enabled
= False
1164 self
._wnd
= wnd
# are we holding a window?
1167 if wnd
.GetSizer(): # the window is a complex one hold by a sizer
1168 size
= wnd
.GetBestSize()
1169 else: # simple window, without sizers
1170 size
= wnd
.GetSize()
1172 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1173 # No other solution to handle the focus changing from an item in
1174 # CustomTreeCtrl and the window associated to an item
1175 # Do better strategies exist?
1176 self
._wnd
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1178 self
._height
= size
.GetHeight() + 2
1179 self
._width
= size
.GetWidth()
1180 self
._windowsize
= size
1182 # We don't show the window if the item is collapsed
1183 if self
._isCollapsed
:
1184 self
._wnd
.Show(False)
1186 # The window is enabled only if the item is enabled
1187 self
._wnd
.Enable(self
._enabled
)
1188 self
._windowenabled
= self
._enabled
1193 Returns whether the item is ok or not. Useless on Python, but added for
1194 backward compatibility with the C++ implementation.
1200 def GetChildren(self
):
1201 """Returns the item's children."""
1203 return self
._children
1207 """Returns the item text."""
1212 def GetImage(self
, which
=TreeItemIcon_Normal
):
1213 """Returns the item image for a particular state."""
1215 return self
._images
[which
]
1218 def GetCheckedImage(self
, which
=TreeItemIcon_Checked
):
1219 """Returns the item check image. Meaningful only for radio & check items."""
1221 return self
._checkedimages
[which
]
1225 """Returns the data associated to this item."""
1230 def SetImage(self
, image
, which
):
1231 """Sets the item image."""
1233 self
._images
[which
] = image
1236 def SetData(self
, data
):
1237 """Sets the data associated to this item."""
1242 def SetHasPlus(self
, has
=True):
1243 """Sets whether an item has the 'plus' button."""
1248 def SetBold(self
, bold
):
1249 """Sets the item font bold."""
1254 def SetItalic(self
, italic
):
1255 """Sets the item font italic."""
1257 self
._isItalic
= italic
1261 """Returns the x position on an item in the ScrolledWindow."""
1267 """Returns the y position on an item in the ScrolledWindow."""
1273 """Sets the x position on an item in the ScrolledWindow."""
1279 """Sets the y position on an item in the ScrolledWindow."""
1284 def GetHeight(self
):
1285 """Returns the height of the item."""
1291 """Returns the width of the item."""
1296 def SetHeight(self
, h
):
1297 """Sets the height of the item."""
1302 def SetWidth(self
, w
):
1303 """Sets the width of the item."""
1308 def SetWindow(self
, wnd
):
1309 """Sets the window associated to the item."""
1314 def GetWindow(self
):
1315 """Returns the window associated to the item."""
1320 def GetWindowEnabled(self
):
1321 """Returns whether the associated window is enabled or not."""
1324 raise Exception("\nERROR: This Item Has No Window Associated")
1326 return self
._windowenabled
1329 def SetWindowEnabled(self
, enable
=True):
1330 """Sets whether the associated window is enabled or not."""
1333 raise Exception("\nERROR: This Item Has No Window Associated")
1335 self
._windowenabled
= enable
1336 self
._wnd
.Enable(enable
)
1339 def GetWindowSize(self
):
1340 """Returns the associated window size."""
1342 return self
._windowsize
1345 def OnSetFocus(self
, event
):
1346 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1348 treectrl
= self
._wnd
.GetParent()
1349 select
= treectrl
.GetSelection()
1351 # If the window is associated to an item that currently is selected
1352 # (has focus) we don't kill the focus. Otherwise we do it.
1354 treectrl
._hasFocus
= False
1356 treectrl
._hasFocus
= True
1363 Returns the item type. It should be one of:
1372 def SetHyperText(self
, hyper
=True):
1373 """Sets whether the item is hypertext or not."""
1375 self
._hypertext
= hyper
1378 def SetVisited(self
, visited
=True):
1379 """Sets whether an hypertext item was visited or not."""
1381 self
._visited
= visited
1384 def GetVisited(self
):
1385 """Returns whether an hypertext item was visited or not."""
1387 return self
._visited
1390 def IsHyperText(self
):
1391 """Returns whether the item is hypetext or not."""
1393 return self
._hypertext
1396 def GetParent(self
):
1397 """Gets the item parent."""
1402 def Insert(self
, child
, index
):
1403 """Inserts an item in the item children."""
1405 self
._children
.insert(index
, child
)
1409 """Expand the item."""
1411 self
._isCollapsed
= False
1415 """Collapse the item."""
1417 self
._isCollapsed
= True
1420 def SetHilight(self
, set=True):
1421 """Sets the item focus/unfocus."""
1423 self
._hasHilight
= set
1426 def HasChildren(self
):
1427 """Returns whether the item has children or not."""
1429 return len(self
._children
) > 0
1432 def IsSelected(self
):
1433 """Returns whether the item is selected or not."""
1435 return self
._hasHilight
!= 0
1438 def IsExpanded(self
):
1439 """Returns whether the item is expanded or not."""
1441 return not self
._isCollapsed
1444 def IsChecked(self
):
1445 """Returns whether the item is checked or not."""
1447 return self
._checked
1450 def Check(self
, checked
=True):
1451 """Check an item. Meaningful only for check and radio items."""
1453 self
._checked
= checked
1457 """Returns whether the item has the plus button or not."""
1459 return self
._hasPlus
or self
.HasChildren()
1463 """Returns whether the item font is bold or not."""
1465 return self
._isBold
!= 0
1469 """Returns whether the item font is italic or not."""
1471 return self
._isItalic
!= 0
1474 def Enable(self
, enable
=True):
1475 """Enables/disables the item."""
1477 self
._enabled
= enable
1480 def IsEnabled(self
):
1481 """Returns whether the item is enabled or not."""
1483 return self
._enabled
1486 def GetAttributes(self
):
1487 """Returns the item attributes (font, colours)."""
1493 """Creates a new attribute (font, colours)."""
1497 self
._attr
= TreeItemAttr()
1498 self
._ownsAttr
= True
1503 def SetAttributes(self
, attr
):
1504 """Sets the item attributes (font, colours)."""
1510 self
._ownsAttr
= False
1513 def AssignAttributes(self
, attr
):
1514 """Assigns the item attributes (font, colours)."""
1516 self
.SetAttributes(attr
)
1517 self
._ownsAttr
= True
1520 def DeleteChildren(self
, tree
):
1521 """Deletes the item children."""
1523 for child
in self
._children
:
1525 tree
.SendDeleteEvent(child
)
1527 child
.DeleteChildren(tree
)
1529 if child
== tree
._select
_me
:
1530 tree
._select
_me
= None
1532 # We have to destroy the associated window
1533 wnd
= child
.GetWindow()
1538 if child
in tree
._itemWithWindow
:
1539 tree
._itemWithWindow
.remove(child
)
1546 def SetText(self
, text
):
1547 """Sets the item text."""
1552 def GetChildrenCount(self
, recursively
=True):
1553 """Gets the number of children."""
1555 count
= len(self
._children
)
1562 for n
in xrange(count
):
1563 total
+= self
._children
[n
].GetChildrenCount()
1568 def GetSize(self
, x
, y
, theButton
):
1569 """Returns the item size."""
1571 bottomY
= self
._y
+ theButton
.GetLineHeight(self
)
1576 width
= self
._x
+ self
._width
1581 if self
.IsExpanded():
1582 for child
in self
._children
:
1583 x
, y
= child
.GetSize(x
, y
, theButton
)
1588 def HitTest(self
, point
, theCtrl
, flags
=0, level
=0):
1590 HitTest method for an item. Called from the main window HitTest.
1591 see the CustomTreeCtrl HitTest method for the flags explanation.
1594 # for a hidden root node, don't evaluate it, but do evaluate children
1595 if not (level
== 0 and theCtrl
.HasFlag(TR_HIDE_ROOT
)):
1598 h
= theCtrl
.GetLineHeight(self
)
1600 if point
.y
> self
._y
and point
.y
< self
._y
+ h
:
1602 y_mid
= self
._y
+ h
/2
1605 flags |
= TREE_HITTEST_ONITEMUPPERPART
1607 flags |
= TREE_HITTEST_ONITEMLOWERPART
1609 xCross
= self
._x
- theCtrl
.GetSpacing()
1611 if wx
.Platform
== "__WXMAC__":
1612 # according to the drawing code the triangels are drawn
1613 # at -4 , -4 from the position up to +10/+10 max
1614 if point
.x
> xCross
-4 and point
.x
< xCross
+10 and point
.y
> y_mid
-4 and \
1615 point
.y
< y_mid
+10 and self
.HasPlus() and theCtrl
.HasButtons():
1617 flags |
= TREE_HITTEST_ONITEMBUTTON
1620 # 5 is the size of the plus sign
1621 if point
.x
> xCross
-6 and point
.x
< xCross
+6 and point
.y
> y_mid
-6 and \
1622 point
.y
< y_mid
+6 and self
.HasPlus() and theCtrl
.HasButtons():
1624 flags |
= TREE_HITTEST_ONITEMBUTTON
1627 if point
.x
>= self
._x
and point
.x
<= self
._x
+ self
._width
:
1632 # assuming every image (normal and selected) has the same size!
1633 if self
.GetImage() != _NO_IMAGE
and theCtrl
._imageListNormal
:
1634 image_w
, image_h
= theCtrl
._imageListNormal
.GetSize(self
.GetImage())
1636 if self
.GetCheckedImage() is not None:
1637 wcheck
, hcheck
= theCtrl
._imageListCheck
.GetSize(self
.GetCheckedImage())
1639 if wcheck
and point
.x
<= self
._x
+ wcheck
+ 1:
1640 flags |
= TREE_HITTEST_ONITEMCHECKICON
1643 if image_w
!= -1 and point
.x
<= self
._x
+ wcheck
+ image_w
+ 1:
1644 flags |
= TREE_HITTEST_ONITEMICON
1646 flags |
= TREE_HITTEST_ONITEMLABEL
1650 if point
.x
< self
._x
:
1651 flags |
= TREE_HITTEST_ONITEMINDENT
1652 if point
.x
> self
._x
+ self
._width
:
1653 flags |
= TREE_HITTEST_ONITEMRIGHT
1657 # if children are expanded, fall through to evaluate them
1658 if self
._isCollapsed
:
1662 for child
in self
._children
:
1663 res
, flags
= child
.HitTest(point
, theCtrl
, flags
, level
+ 1)
1670 def GetCurrentImage(self
):
1671 """Returns the current item image."""
1675 if self
.IsExpanded():
1677 if self
.IsSelected():
1679 image
= self
.GetImage(TreeItemIcon_SelectedExpanded
)
1681 if image
== _NO_IMAGE
:
1683 # we usually fall back to the normal item, but try just the
1684 # expanded one (and not selected) first in this case
1685 image
= self
.GetImage(TreeItemIcon_Expanded
)
1687 else: # not expanded
1689 if self
.IsSelected():
1690 image
= self
.GetImage(TreeItemIcon_Selected
)
1692 # maybe it doesn't have the specific image we want,
1693 # try the default one instead
1694 if image
== _NO_IMAGE
:
1695 image
= self
.GetImage()
1700 def GetCurrentCheckedImage(self
):
1701 """Returns the current item check image."""
1706 if self
.IsChecked():
1707 if self
._type
== 1: # Checkbox
1708 return self
._checkedimages
[TreeItemIcon_Checked
]
1710 return self
._checkedimages
[TreeItemIcon_Flagged
]
1712 if self
._type
== 1: # Checkbox
1713 return self
._checkedimages
[TreeItemIcon_NotChecked
]
1715 return self
._checkedimages
[TreeItemIcon_NotFlagged
]
1718 def EventFlagsToSelType(style
, shiftDown
=False, ctrlDown
=False):
1720 Translate the key or mouse event flag to the type of selection we
1724 is_multiple
= (style
& TR_MULTIPLE
) != 0
1725 extended_select
= shiftDown
and is_multiple
1726 unselect_others
= not (extended_select
or (ctrlDown
and is_multiple
))
1728 return is_multiple
, extended_select
, unselect_others
1731 # -----------------------------------------------------------------------------
1732 # CustomTreeCtrl Main Implementation.
1733 # This Is The Main Class.
1734 # -----------------------------------------------------------------------------
1736 class CustomTreeCtrl(wx
.PyScrolledWindow
):
1738 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1739 style
=0, ctstyle
=TR_DEFAULT_STYLE
, validator
=wx
.DefaultValidator
,
1740 name
="CustomTreeCtrl"):
1742 Default class constructor.
1744 parent: parent window. Must not be none.
1746 id: window identifier. A value of -1 indicates a default value.
1748 pos: window position.
1750 size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
1752 style: the underlying wx.ScrolledWindow style
1754 ctstyle: CustomTreeCtrl window style. This can be one of:
1756 TR_HAS_BUTTONS # draw collapsed/expanded btns
1757 TR_NO_LINES # don't draw lines at all
1758 TR_LINES_AT_ROOT # connect top-level nodes
1759 TR_TWIST_BUTTONS # draw mac-like twist buttons
1760 TR_SINGLE # single selection mode
1761 TR_MULTIPLE # can select multiple items
1762 TR_EXTENDED # todo: allow extended selection
1763 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1764 TR_EDIT_LABELS # can edit item labels
1765 TR_ROW_LINES # put border around items
1766 TR_HIDE_ROOT # don't display root node
1767 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1768 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1769 TR_AUTO_CHECK_PARENT # only meaningful for checkboxes
1770 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1772 validator: window validator.
1777 self
._current
= self
._key
_current
= self
._anchor
= self
._select
_me
= None
1778 self
._hasFocus
= False
1781 # Default line height: it will soon be changed
1782 self
._lineHeight
= 10
1783 # Item indent wrt parent
1785 # item horizontal spacing between the start and the text
1788 # Brushes for focused/unfocused items (also gradient type)
1789 self
._hilightBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
))
1790 btnshadow
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1791 self
._hilightUnfocusedBrush
= wx
.Brush(btnshadow
)
1792 r
, g
, b
= btnshadow
.Red(), btnshadow
.Green(), btnshadow
.Blue()
1793 backcolour
= (max((r
>> 1) - 20, 0),
1794 max((g
>> 1) - 20, 0),
1795 max((b
>> 1) - 20, 0))
1796 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1797 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1799 # image list for icons
1800 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1801 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1803 # Drag and drop initial settings
1806 self
._isDragging
= False
1807 self
._dropTarget
= self
._oldSelection
= None
1808 self
._dragImage
= None
1809 self
._underMouse
= None
1811 # TextCtrl initial settings for editable items
1812 self
._textCtrl
= None
1813 self
._renameTimer
= None
1815 # This one allows us to handle Freeze() and Thaw() calls
1816 self
._freezeCount
= 0
1818 self
._findPrefix
= ""
1819 self
._findTimer
= None
1821 self
._dropEffectAboveItem
= False
1822 self
._lastOnSame
= False
1824 # Default normal and bold fonts for an item
1825 self
._hasFont
= True
1826 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1827 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1828 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1829 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1833 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1834 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1835 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1836 self
._hypertextnewcolour
= wx
.BLUE
1837 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1838 self
._isonhyperlink
= False
1840 # Default CustomTreeCtrl background colour.
1841 self
._backgroundColour
= wx
.WHITE
1843 # Background image settings
1844 self
._backgroundImage
= None
1845 self
._imageStretchStyle
= _StyleTile
1847 # Disabled items colour
1848 self
._disabledColour
= wx
.Colour(180, 180, 180)
1850 # Gradient selection colours
1851 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1852 self
._secondcolour
= wx
.WHITE
1853 self
._usegradients
= False
1854 self
._gradientstyle
= 0 # Horizontal Gradient
1856 # Vista Selection Styles
1857 self
._vistaselection
= False
1859 # Connection lines style
1860 if wx
.Platform
!= "__WXMAC__":
1861 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1862 self
._dottedPen
.SetDashes([1,1])
1863 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1865 self
._dottedPen
= wx
.Pen("grey", 1)
1867 # Pen Used To Draw The Border Around Selected Items
1868 self
._borderPen
= wx
.BLACK_PEN
1869 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1871 # For Appended Windows
1872 self
._hasWindows
= False
1873 self
._itemWithWindow
= []
1875 if wx
.Platform
== "__WXMAC__":
1876 ctstyle
&= ~TR_LINES_AT_ROOT
1877 ctstyle |
= TR_NO_LINES
1879 platform
, major
, minor
= wx
.GetOsVersion()
1881 ctstyle |
= TR_ROW_LINES
1883 self
._windowStyle
= ctstyle
1885 # Create the default check image list
1886 self
.SetImageListCheck(13, 13)
1888 # A constant to use my translation of RendererNative.DrawTreeItemButton
1889 # if the wxPython version is less than 2.6.2.1.
1890 if wx
.VERSION_STRING
< "2.6.2.1":
1891 self
._drawingfunction
= DrawTreeItemButton
1893 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1895 # Create our container... at last!
1896 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1898 # If the tree display has no buttons, but does have
1899 # connecting lines, we can use a narrower layout.
1900 # It may not be a good idea to force this...
1901 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1905 self
.SetValidator(validator
)
1907 attr
= self
.GetDefaultAttributes()
1908 self
.SetOwnForegroundColour(attr
.colFg
)
1909 self
.SetOwnBackgroundColour(wx
.WHITE
)
1911 if not self
._hasFont
:
1912 self
.SetOwnFont(attr
.font
)
1917 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1918 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1919 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1920 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1921 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1922 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1923 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1924 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1926 # Sets the focus to ourselves: this is useful if you have items
1927 # with associated widgets.
1931 def AcceptsFocus(self
):
1932 # overridden base class method, allows this ctrl to
1933 # participate in the tab-order, etc. It's overridable because
1934 # of deriving this class from wx.PyScrolledWindow...
1938 def OnDestroy(self
, event
):
1939 """Handles the wx.EVT_WINDOW_DESTROY event."""
1941 # Here there may be something I miss... do I have to destroy
1943 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1944 self
._renameTimer
.Stop()
1945 del self
._renameTimer
1947 if self
._findTimer
and self
._findTimer
.IsRunning():
1948 self
._findTimer
.Stop()
1955 """Returns the global number of items in the tree."""
1957 if not self
._anchor
:
1961 count
= self
._anchor
.GetChildrenCount()
1963 if not self
.HasFlag(TR_HIDE_ROOT
):
1964 # take the root itself into account
1970 def GetIndent(self
):
1971 """Returns the item indentation."""
1976 def GetSpacing(self
):
1977 """Returns the spacing between the start and the text."""
1979 return self
._spacing
1982 def GetRootItem(self
):
1983 """Returns the root item."""
1988 def GetSelection(self
):
1989 """Returns the current selection: TR_SINGLE only."""
1991 return self
._current
1994 def ToggleItemSelection(self
, item
):
1995 """Toggles the item selection."""
1998 raise Exception("\nERROR: Invalid Tree Item. ")
2000 self
.SelectItem(item
, not self
.IsSelected(item
))
2003 def EnableChildren(self
, item
, enable
=True):
2004 """Enables/disables item children. Used internally."""
2007 if item
.IsExpanded():
2010 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2011 # We hit a radiobutton item not checked, we don't want to
2012 # enable the children
2015 child
, cookie
= self
.GetFirstChild(item
)
2017 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2019 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2020 self
.EnableChildren(child
, enable
)
2021 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2024 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2025 """Enables/disables an item."""
2028 raise Exception("\nERROR: Invalid Tree Item. ")
2030 if item
.IsEnabled() == enable
:
2033 if not enable
and item
.IsSelected():
2034 self
.SelectItem(item
, False)
2037 wnd
= item
.GetWindow()
2039 # Handles the eventual window associated to the item
2041 wndenable
= item
.GetWindowEnabled()
2049 # We have to refresh the item line
2050 dc
= wx
.ClientDC(self
)
2051 self
.CalculateSize(item
, dc
)
2052 self
.RefreshLine(item
)
2055 def IsEnabled(self
, item
):
2056 """Returns whether an item is enabled or disabled."""
2059 raise Exception("\nERROR: Invalid Tree Item. ")
2061 return item
.IsEnabled()
2064 def SetDisabledColour(self
, colour
):
2065 """Sets the items disabled colour."""
2067 self
._disabledColour
= colour
2071 def GetDisabledColour(self
):
2072 """Returns the items disabled colour."""
2074 return self
._disabledColour
2077 def IsItemChecked(self
, item
):
2078 """Returns whether an item is checked or not."""
2081 raise Exception("\nERROR: Invalid Tree Item. ")
2083 return item
.IsChecked()
2086 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2087 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2089 if item
.GetType() == 0:
2095 dc
= wx
.ClientDC(self
)
2096 self
.CalculateSize(item
, dc
)
2097 self
.RefreshLine(item
)
2100 def UnCheckRadioParent(self
, item
, checked
=False):
2101 """Used internally to handle radio node parent correctly."""
2103 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2105 e
.SetEventObject(self
)
2107 if self
.GetEventHandler().ProcessEvent(e
):
2111 self
.RefreshLine(item
)
2112 self
.EnableChildren(item
, checked
)
2113 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2115 e
.SetEventObject(self
)
2116 self
.GetEventHandler().ProcessEvent(e
)
2121 def CheckItem(self
, item
, checked
=True):
2123 Actually checks/uncheks an item, sending (eventually) the two
2124 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2128 raise Exception("\nERROR: Invalid Tree Item. ")
2130 # Should we raise an error here?!?
2131 if item
.GetType() == 0:
2134 if item
.GetType() == 2: # it's a radio button
2135 if not checked
and item
.IsChecked(): # Try To Unckeck?
2136 if item
.HasChildren():
2137 self
.UnCheckRadioParent(item
, checked
)
2140 if not self
.UnCheckRadioParent(item
, checked
):
2143 self
.CheckSameLevel(item
, False)
2146 # Radiobuttons are done, let's handle checkbuttons...
2147 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2149 e
.SetEventObject(self
)
2151 if self
.GetEventHandler().ProcessEvent(e
):
2156 dc
= wx
.ClientDC(self
)
2157 self
.RefreshLine(item
)
2159 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2160 ischeck
= self
.IsItemChecked(item
)
2161 self
.AutoCheckChild(item
, ischeck
)
2162 if self
._windowStyle
& TR_AUTO_CHECK_PARENT
:
2163 ischeck
= self
.IsItemChecked(item
)
2164 self
.AutoCheckParent(item
, ischeck
)
2165 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2166 self
.AutoToggleChild(item
)
2168 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2170 e
.SetEventObject(self
)
2171 self
.GetEventHandler().ProcessEvent(e
)
2174 def AutoToggleChild(self
, item
):
2175 """Transverses the tree and toggles the items. Meaningful only for check items."""
2178 raise Exception("\nERROR: Invalid Tree Item. ")
2180 child
, cookie
= self
.GetFirstChild(item
)
2183 if item
.IsExpanded():
2188 if child
.GetType() == 1 and child
.IsEnabled():
2189 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2190 self
.AutoToggleChild(child
)
2191 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2194 def AutoCheckChild(self
, item
, checked
):
2195 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2198 raise Exception("\nERROR: Invalid Tree Item. ")
2200 (child
, cookie
) = self
.GetFirstChild(item
)
2203 if item
.IsExpanded():
2207 if child
.GetType() == 1 and child
.IsEnabled():
2208 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2209 self
.AutoCheckChild(child
, checked
)
2210 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2213 def AutoCheckParent(self
, item
, checked
):
2214 """Traverses up the tree and checks/unchecks parent items.
2215 Meaningful only for check items."""
2218 raise Exception("\nERROR: Invalid Tree Item. ")
2220 parent
= item
.GetParent()
2221 if not parent
or parent
.GetType() != 1:
2224 (child
, cookie
) = self
.GetFirstChild(parent
)
2226 if child
.GetType() == 1 and child
.IsEnabled():
2227 if checked
!= child
.IsChecked():
2229 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2231 self
.CheckItem2(parent
, checked
, torefresh
=True)
2232 self
.AutoCheckParent(parent
, checked
)
2235 def CheckChilds(self
, item
, checked
=True):
2236 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2239 raise Exception("\nERROR: Invalid Tree Item. ")
2242 self
.AutoToggleChild(item
)
2244 self
.AutoCheckChild(item
, checked
)
2247 def CheckSameLevel(self
, item
, checked
=False):
2249 Uncheck radio items which are on the same level of the checked one.
2253 parent
= item
.GetParent()
2259 if parent
.IsExpanded():
2262 (child
, cookie
) = self
.GetFirstChild(parent
)
2264 if child
.GetType() == 2 and child
!= item
:
2265 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2266 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2267 self
.EnableChildren(child
, checked
)
2268 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2271 def EditLabel(self
, item
):
2272 """Starts editing an item label."""
2275 raise Exception("\nERROR: Invalid Tree Item. ")
2280 def ShouldInheritColours(self
):
2281 """We don't inherit colours from anyone."""
2286 def SetIndent(self
, indent
):
2287 """Sets item indentation."""
2289 self
._indent
= indent
2293 def SetSpacing(self
, spacing
):
2294 """Sets item spacing."""
2296 self
._spacing
= spacing
2300 def HasFlag(self
, flag
):
2301 """Returns whether CustomTreeCtrl has a flag."""
2303 return self
._windowStyle
& flag
2306 def HasChildren(self
, item
):
2307 """Returns whether an item has children or not."""
2310 raise Exception("\nERROR: Invalid Tree Item. ")
2312 return len(item
.GetChildren()) > 0
2315 def GetChildrenCount(self
, item
, recursively
=True):
2316 """Gets the item children count."""
2319 raise Exception("\nERROR: Invalid Tree Item. ")
2321 return item
.GetChildrenCount(recursively
)
2324 def SetTreeStyle(self
, styles
):
2325 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2327 # Do not try to expand the root node if it hasn't been created yet
2328 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2330 # if we will hide the root, make sure children are visible
2331 self
._anchor
.SetHasPlus()
2332 self
._anchor
.Expand()
2333 self
.CalculatePositions()
2335 # right now, just sets the styles. Eventually, we may
2336 # want to update the inherited styles, but right now
2337 # none of the parents has updatable styles
2339 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2340 selections
= self
.GetSelections()
2341 for select
in selections
[0:-1]:
2342 self
.SelectItem(select
, False)
2344 self
._windowStyle
= styles
2348 def GetTreeStyle(self
):
2349 """Returns the CustomTreeCtrl style."""
2351 return self
._windowStyle
2354 def HasButtons(self
):
2355 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2357 return self
.HasFlag(TR_HAS_BUTTONS
)
2360 # -----------------------------------------------------------------------------
2361 # functions to work with tree items
2362 # -----------------------------------------------------------------------------
2364 def GetItemText(self
, item
):
2365 """Returns the item text."""
2368 raise Exception("\nERROR: Invalid Tree Item. ")
2370 return item
.GetText()
2373 def GetItemImage(self
, item
, which
):
2374 """Returns the item image."""
2377 raise Exception("\nERROR: Invalid Tree Item. ")
2379 return item
.GetImage(which
)
2382 def GetPyData(self
, item
):
2383 """Returns the data associated to an item."""
2386 raise Exception("\nERROR: Invalid Tree Item. ")
2388 return item
.GetData()
2390 GetItemPyData
= GetPyData
2393 def GetItemTextColour(self
, item
):
2394 """Returns the item text colour."""
2397 raise Exception("\nERROR: Invalid Tree Item. ")
2399 return item
.Attr().GetTextColour()
2402 def GetItemBackgroundColour(self
, item
):
2403 """Returns the item background colour."""
2406 raise Exception("\nERROR: Invalid Tree Item. ")
2408 return item
.Attr().GetBackgroundColour()
2411 def GetItemFont(self
, item
):
2412 """Returns the item font."""
2415 raise Exception("\nERROR: Invalid Tree Item. ")
2417 return item
.Attr().GetFont()
2420 def IsItemHyperText(self
, item
):
2421 """Returns whether an item is hypertext or not."""
2424 raise Exception("\nERROR: Invalid Tree Item. ")
2426 return item
.IsHyperText()
2429 def SetItemText(self
, item
, text
):
2430 """Sets the item text."""
2433 raise Exception("\nERROR: Invalid Tree Item. ")
2435 dc
= wx
.ClientDC(self
)
2437 self
.CalculateSize(item
, dc
)
2438 self
.RefreshLine(item
)
2441 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2442 """Sets the item image, depending on the item state."""
2445 raise Exception("\nERROR: Invalid Tree Item. ")
2447 item
.SetImage(image
, which
)
2449 dc
= wx
.ClientDC(self
)
2450 self
.CalculateSize(item
, dc
)
2451 self
.RefreshLine(item
)
2454 def SetPyData(self
, item
, data
):
2455 """Sets the data associated to an item."""
2458 raise Exception("\nERROR: Invalid Tree Item. ")
2462 SetItemPyData
= SetPyData
2465 def SetItemHasChildren(self
, item
, has
=True):
2466 """Forces the appearance of the button next to the item."""
2469 raise Exception("\nERROR: Invalid Tree Item. ")
2471 item
.SetHasPlus(has
)
2472 self
.RefreshLine(item
)
2475 def SetItemBold(self
, item
, bold
=True):
2476 """Sets the item font bold/unbold."""
2479 raise Exception("\nERROR: Invalid Tree Item. ")
2481 # avoid redrawing the tree if no real change
2482 if item
.IsBold() != bold
:
2487 def SetItemItalic(self
, item
, italic
=True):
2488 """Sets the item font italic/non-italic."""
2491 raise Exception("\nERROR: Invalid Tree Item. ")
2493 if item
.IsItalic() != italic
:
2494 itemFont
= self
.GetItemFont(item
)
2495 if itemFont
!= wx
.NullFont
:
2500 item
.SetItalic(italic
)
2501 itemFont
.SetStyle(style
)
2502 self
.SetItemFont(item
, itemFont
)
2506 def SetItemDropHighlight(self
, item
, highlight
=True):
2508 Gives the item the visual feedback for drag and drop operations.
2509 This is useful when something is dragged from outside the CustomTreeCtrl.
2513 raise Exception("\nERROR: Invalid Tree Item. ")
2516 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2517 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2519 item
.Attr().SetTextColour(fg
)
2520 item
.Attr
.SetBackgroundColour(bg
)
2521 self
.RefreshLine(item
)
2524 def SetItemTextColour(self
, item
, col
):
2525 """Sets the item text colour."""
2528 raise Exception("\nERROR: Invalid Tree Item. ")
2530 if self
.GetItemTextColour(item
) == col
:
2533 item
.Attr().SetTextColour(col
)
2534 self
.RefreshLine(item
)
2537 def SetItemBackgroundColour(self
, item
, col
):
2538 """Sets the item background colour."""
2541 raise Exception("\nERROR: Invalid Tree Item. ")
2543 item
.Attr().SetBackgroundColour(col
)
2544 self
.RefreshLine(item
)
2547 def SetItemHyperText(self
, item
, hyper
=True):
2548 """Sets whether the item is hypertext or not."""
2551 raise Exception("\nERROR: Invalid Tree Item. ")
2553 item
.SetHyperText(hyper
)
2554 self
.RefreshLine(item
)
2557 def SetItemFont(self
, item
, font
):
2558 """Sets the item font."""
2561 raise Exception("\nERROR: Invalid Tree Item. ")
2563 if self
.GetItemFont(item
) == font
:
2566 item
.Attr().SetFont(font
)
2570 def SetFont(self
, font
):
2571 """Sets the CustomTreeCtrl font."""
2573 wx
.ScrolledWindow
.SetFont(self
, font
)
2575 self
._normalFont
= font
2576 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2577 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2578 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2583 def GetHyperTextFont(self
):
2584 """Returns the font used to render an hypertext item."""
2586 return self
._hypertextfont
2589 def SetHyperTextFont(self
, font
):
2590 """Sets the font used to render an hypertext item."""
2592 self
._hypertextfont
= font
2596 def SetHyperTextNewColour(self
, colour
):
2597 """Sets the colour used to render a non-visited hypertext item."""
2599 self
._hypertextnewcolour
= colour
2603 def GetHyperTextNewColour(self
):
2604 """Returns the colour used to render a non-visited hypertext item."""
2606 return self
._hypertextnewcolour
2609 def SetHyperTextVisitedColour(self
, colour
):
2610 """Sets the colour used to render a visited hypertext item."""
2612 self
._hypertextvisitedcolour
= colour
2616 def GetHyperTextVisitedColour(self
):
2617 """Returns the colour used to render a visited hypertext item."""
2619 return self
._hypertextvisitedcolour
2622 def SetItemVisited(self
, item
, visited
=True):
2623 """Sets whether an hypertext item was visited."""
2626 raise Exception("\nERROR: Invalid Tree Item. ")
2628 item
.SetVisited(visited
)
2629 self
.RefreshLine(item
)
2632 def GetItemVisited(self
, item
):
2633 """Returns whether an hypertext item was visited."""
2636 raise Exception("\nERROR: Invalid Tree Item. ")
2638 return item
.GetVisited()
2641 def SetHilightFocusColour(self
, colour
):
2643 Sets the colour used to highlight focused selected items.
2644 This is applied only if gradient and Windows Vista styles are disabled.
2647 self
._hilightBrush
= wx
.Brush(colour
)
2648 self
.RefreshSelected()
2651 def SetHilightNonFocusColour(self
, colour
):
2653 Sets the colour used to highlight unfocused selected items.
2654 This is applied only if gradient and Windows Vista styles are disabled.
2657 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2658 self
.RefreshSelected()
2661 def GetHilightFocusColour(self
):
2663 Returns the colour used to highlight focused selected items.
2664 This is applied only if gradient and Windows Vista styles are disabled.
2667 return self
._hilightBrush
.GetColour()
2670 def GetHilightNonFocusColour(self
):
2672 Returns the colour used to highlight unfocused selected items.
2673 This is applied only if gradient and Windows Vista styles are disabled.
2676 return self
._hilightUnfocusedBrush
.GetColour()
2679 def SetFirstGradientColour(self
, colour
=None):
2680 """Sets the first gradient colour."""
2683 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2685 self
._firstcolour
= colour
2686 if self
._usegradients
:
2687 self
.RefreshSelected()
2690 def SetSecondGradientColour(self
, colour
=None):
2691 """Sets the second gradient colour."""
2694 # No colour given, generate a slightly darker from the
2695 # CustomTreeCtrl background colour
2696 color
= self
.GetBackgroundColour()
2697 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2698 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2699 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2701 self
._secondcolour
= colour
2703 if self
._usegradients
:
2704 self
.RefreshSelected()
2707 def GetFirstGradientColour(self
):
2708 """Returns the first gradient colour."""
2710 return self
._firstcolour
2713 def GetSecondGradientColour(self
):
2714 """Returns the second gradient colour."""
2716 return self
._secondcolour
2719 def EnableSelectionGradient(self
, enable
=True):
2720 """Globally enables/disables drawing of gradient selection."""
2722 self
._usegradients
= enable
2723 self
._vistaselection
= False
2724 self
.RefreshSelected()
2727 def SetGradientStyle(self
, vertical
=0):
2729 Sets the gradient style:
2730 0: horizontal gradient
2731 1: vertical gradient
2734 # 0 = Horizontal, 1 = Vertical
2735 self
._gradientstyle
= vertical
2737 if self
._usegradients
:
2738 self
.RefreshSelected()
2741 def GetGradientStyle(self
):
2743 Returns the gradient style:
2744 0: horizontal gradient
2745 1: vertical gradient
2748 return self
._gradientstyle
2751 def EnableSelectionVista(self
, enable
=True):
2752 """Globally enables/disables drawing of Windows Vista selection."""
2754 self
._usegradients
= False
2755 self
._vistaselection
= enable
2756 self
.RefreshSelected()
2759 def SetBorderPen(self
, pen
):
2761 Sets the pen used to draw the selected item border.
2762 The border pen is not used if the Windows Vista style is applied.
2765 self
._borderPen
= pen
2766 self
.RefreshSelected()
2769 def GetBorderPen(self
):
2771 Returns the pen used to draw the selected item border.
2772 The border pen is not used if the Windows Vista style is applied.
2775 return self
._borderPen
2778 def SetConnectionPen(self
, pen
):
2779 """Sets the pen used to draw the connecting lines between items."""
2781 self
._dottedPen
= pen
2785 def GetConnectionPen(self
):
2786 """Returns the pen used to draw the connecting lines between items."""
2788 return self
._dottedPen
2791 def SetBackgroundImage(self
, image
):
2792 """Sets the CustomTreeCtrl background image (can be none)."""
2794 self
._backgroundImage
= image
2798 def GetBackgroundImage(self
):
2799 """Returns the CustomTreeCtrl background image (can be none)."""
2801 return self
._backgroundImage
2804 def GetItemWindow(self
, item
):
2805 """Returns the window associated to the item (if any)."""
2808 raise Exception("\nERROR: Invalid Item")
2810 return item
.GetWindow()
2813 def GetItemWindowEnabled(self
, item
):
2814 """Returns whether the window associated to the item is enabled."""
2817 raise Exception("\nERROR: Invalid Item")
2819 return item
.GetWindowEnabled()
2822 def SetItemWindowEnabled(self
, item
, enable
=True):
2823 """Enables/disables the window associated to the item."""
2826 raise Exception("\nERROR: Invalid Item")
2828 item
.SetWindowEnabled(enable
)
2831 def GetItemType(self
, item
):
2833 Returns the item type:
2840 raise Exception("\nERROR: Invalid Item")
2842 return item
.GetType()
2844 # -----------------------------------------------------------------------------
2845 # item status inquiries
2846 # -----------------------------------------------------------------------------
2848 def IsVisible(self
, item
):
2849 """Returns whether the item is visible or not."""
2852 raise Exception("\nERROR: Invalid Tree Item. ")
2854 # An item is only visible if it's not a descendant of a collapsed item
2855 parent
= item
.GetParent()
2859 if not parent
.IsExpanded():
2862 parent
= parent
.GetParent()
2864 startX
, startY
= self
.GetViewStart()
2865 clientSize
= self
.GetClientSize()
2867 rect
= self
.GetBoundingRect(item
)
2871 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2873 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2875 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2881 def ItemHasChildren(self
, item
):
2882 """Returns whether the item has children or not."""
2885 raise Exception("\nERROR: Invalid Tree Item. ")
2887 # consider that the item does have children if it has the "+" button: it
2888 # might not have them (if it had never been expanded yet) but then it
2889 # could have them as well and it's better to err on this side rather than
2890 # disabling some operations which are restricted to the items with
2891 # children for an item which does have them
2892 return item
.HasPlus()
2895 def IsExpanded(self
, item
):
2896 """Returns whether the item is expanded or not."""
2899 raise Exception("\nERROR: Invalid Tree Item. ")
2901 return item
.IsExpanded()
2904 def IsSelected(self
, item
):
2905 """Returns whether the item is selected or not."""
2908 raise Exception("\nERROR: Invalid Tree Item. ")
2910 return item
.IsSelected()
2913 def IsBold(self
, item
):
2914 """Returns whether the item font is bold or not."""
2917 raise Exception("\nERROR: Invalid Tree Item. ")
2919 return item
.IsBold()
2922 def IsItalic(self
, item
):
2923 """Returns whether the item font is italic or not."""
2926 raise Exception("\nERROR: Invalid Tree Item. ")
2928 return item
.IsItalic()
2931 # -----------------------------------------------------------------------------
2933 # -----------------------------------------------------------------------------
2935 def GetItemParent(self
, item
):
2936 """Gets the item parent."""
2939 raise Exception("\nERROR: Invalid Tree Item. ")
2941 return item
.GetParent()
2944 def GetFirstChild(self
, item
):
2945 """Gets the item first child."""
2948 raise Exception("\nERROR: Invalid Tree Item. ")
2951 return self
.GetNextChild(item
, cookie
)
2954 def GetNextChild(self
, item
, cookie
):
2956 Gets the item next child based on the 'cookie' parameter.
2957 This method has no sense if you do not call GetFirstChild() before.
2961 raise Exception("\nERROR: Invalid Tree Item. ")
2963 children
= item
.GetChildren()
2965 # it's ok to cast cookie to size_t, we never have indices big enough to
2968 if cookie
< len(children
):
2970 return children
[cookie
], cookie
+1
2974 # there are no more of them
2978 def GetLastChild(self
, item
):
2979 """Gets the item last child."""
2982 raise Exception("\nERROR: Invalid Tree Item. ")
2984 children
= item
.GetChildren()
2985 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2988 def GetNextSibling(self
, item
):
2989 """Gets the next sibling of an item."""
2992 raise Exception("\nERROR: Invalid Tree Item. ")
2995 parent
= i
.GetParent()
2999 # root item doesn't have any siblings
3002 siblings
= parent
.GetChildren()
3003 index
= siblings
.index(i
)
3006 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
3009 def GetPrevSibling(self
, item
):
3010 """Gets the previous sibling of an item."""
3013 raise Exception("\nERROR: Invalid Tree Item. ")
3016 parent
= i
.GetParent()
3020 # root item doesn't have any siblings
3023 siblings
= parent
.GetChildren()
3024 index
= siblings
.index(i
)
3026 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3029 def GetNext(self
, item
):
3030 """Gets the next item. Only for internal use right now."""
3033 raise Exception("\nERROR: Invalid Tree Item. ")
3037 # First see if there are any children.
3038 children
= i
.GetChildren()
3039 if len(children
) > 0:
3042 # Try a sibling of this or ancestor instead
3045 while p
and not toFind
:
3046 toFind
= self
.GetNextSibling(p
)
3047 p
= self
.GetItemParent(p
)
3052 def GetFirstVisibleItem(self
):
3053 """Returns the first visible item."""
3055 id = self
.GetRootItem()
3060 if self
.IsVisible(id):
3062 id = self
.GetNext(id)
3067 def GetNextVisible(self
, item
):
3068 """Returns the next visible item."""
3071 raise Exception("\nERROR: Invalid Tree Item. ")
3076 id = self
.GetNext(id)
3077 if id and self
.IsVisible(id):
3083 def GetPrevVisible(self
, item
):
3086 raise Exception("\nERROR: Invalid Tree Item. ")
3088 raise Exception("\nERROR: Not Implemented")
3093 def ResetTextControl(self
):
3094 """Called by TreeTextCtrl when it marks itself for deletion."""
3096 self
._textCtrl
.Destroy()
3097 self
._textCtrl
= None
3100 def FindItem(self
, idParent
, prefixOrig
):
3101 """Finds the first item starting with the given prefix after the given item."""
3103 # match is case insensitive as this is more convenient to the user: having
3104 # to press Shift-letter to go to the item starting with a capital letter
3105 # would be too bothersome
3106 prefix
= prefixOrig
.lower()
3108 # determine the starting point: we shouldn't take the current item (this
3109 # allows to switch between two items starting with the same letter just by
3110 # pressing it) but we shouldn't jump to the next one if the user is
3111 # continuing to type as otherwise he might easily skip the item he wanted
3114 if len(prefix
) == 1:
3115 id = self
.GetNext(id)
3117 # look for the item starting with the given prefix after it
3118 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3120 id = self
.GetNext(id)
3122 # if we haven't found anything...
3125 # ... wrap to the beginning
3126 id = self
.GetRootItem()
3127 if self
.HasFlag(TR_HIDE_ROOT
):
3128 # can't select virtual root
3129 id = self
.GetNext(id)
3131 # and try all the items (stop when we get to the one we started from)
3132 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3133 id = self
.GetNext(id)
3138 # -----------------------------------------------------------------------------
3140 # -----------------------------------------------------------------------------
3142 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3143 """Actually inserts an item in the tree."""
3145 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3146 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3148 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3149 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3151 if ct_type
< 0 or ct_type
> 2:
3152 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3158 # should we give a warning here?
3159 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3161 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3163 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3166 self
._hasWindows
= True
3167 self
._itemWithWindow
.append(item
)
3169 parent
.Insert(item
, previous
)
3174 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3175 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3178 raise Exception("\nERROR: Tree Can Have Only One Root")
3180 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3181 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3183 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3184 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3186 if ct_type
< 0 or ct_type
> 2:
3187 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3189 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3191 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3194 self
._hasWindows
= True
3195 self
._itemWithWindow
.append(self
._anchor
)
3197 if self
.HasFlag(TR_HIDE_ROOT
):
3199 # if root is hidden, make sure we can navigate
3201 self
._anchor
.SetHasPlus()
3202 self
._anchor
.Expand()
3203 self
.CalculatePositions()
3205 if not self
.HasFlag(TR_MULTIPLE
):
3207 self
._current
= self
._key
_current
= self
._anchor
3208 self
._current
.SetHilight(True)
3213 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3214 """Appends an item as a first child of parent."""
3216 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3217 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3219 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3220 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3222 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3225 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3226 """Auxiliary function to cope with the C++ hideous multifunction."""
3228 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3229 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3231 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3232 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3237 # should we give a warning here?
3238 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3244 index
= parent
.GetChildren().index(idPrevious
)
3246 raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
3248 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3251 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3252 """Auxiliary function to cope with the C++ hideous multifunction."""
3254 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3255 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3257 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3258 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3263 # should we give a warning here?
3264 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3266 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3269 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3270 """Inserts an item after the given previous."""
3272 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3273 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3275 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3276 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3278 if type(input) == type(1):
3279 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3281 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3284 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3285 """Appends an item as a last child of its parent."""
3287 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3288 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3290 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3291 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3296 # should we give a warning here?
3297 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3299 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3302 def SendDeleteEvent(self
, item
):
3303 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3305 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3307 event
.SetEventObject(self
)
3308 self
.ProcessEvent(event
)
3311 def IsDescendantOf(self
, parent
, item
):
3312 """Checks if the given item is under another one."""
3318 # item is a descendant of parent
3321 item
= item
.GetParent()
3326 # Don't leave edit or selection on a child which is about to disappear
3327 def ChildrenClosing(self
, item
):
3328 """We are about to destroy the item children."""
3330 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3331 self
._textCtrl
.StopEditing()
3333 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3334 self
._key
_current
= None
3336 if self
.IsDescendantOf(item
, self
._select
_me
):
3337 self
._select
_me
= item
3339 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3340 self
._current
.SetHilight(False)
3341 self
._current
= None
3342 self
._select
_me
= item
3345 def DeleteChildren(self
, item
):
3346 """Delete item children."""
3349 raise Exception("\nERROR: Invalid Tree Item. ")
3351 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3353 self
.ChildrenClosing(item
)
3354 item
.DeleteChildren(self
)
3357 def Delete(self
, item
):
3358 """Delete an item."""
3361 raise Exception("\nERROR: Invalid Tree Item. ")
3363 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3365 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3366 # can't delete the item being edited, cancel editing it first
3367 self
._textCtrl
.StopEditing()
3369 parent
= item
.GetParent()
3371 # don't keep stale pointers around!
3372 if self
.IsDescendantOf(item
, self
._key
_current
):
3374 # Don't silently change the selection:
3375 # do it properly in idle time, so event
3376 # handlers get called.
3378 # self._key_current = parent
3379 self
._key
_current
= None
3381 # self._select_me records whether we need to select
3382 # a different item, in idle time.
3383 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3384 self
._select
_me
= parent
3386 if self
.IsDescendantOf(item
, self
._current
):
3388 # Don't silently change the selection:
3389 # do it properly in idle time, so event
3390 # handlers get called.
3392 # self._current = parent
3393 self
._current
= None
3394 self
._select
_me
= parent
3396 # remove the item from the tree
3399 parent
.GetChildren().remove(item
) # remove by value
3401 else: # deleting the root
3403 # nothing will be left in the tree
3406 # and delete all of its children and the item itself now
3407 item
.DeleteChildren(self
)
3408 self
.SendDeleteEvent(item
)
3410 if item
== self
._select
_me
:
3411 self
._select
_me
= None
3413 # Remove the item with window
3414 if item
in self
._itemWithWindow
:
3415 wnd
= item
.GetWindow()
3419 self
._itemWithWindow
.remove(item
)
3424 def DeleteAllItems(self
):
3425 """Delete all items in the CustomTreeCtrl."""
3428 self
.Delete(self
._anchor
)
3431 def Expand(self
, item
):
3433 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3434 EVT_TREE_ITEM_EXPANDED events.
3438 raise Exception("\nERROR: Invalid Tree Item. ")
3440 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3441 raise Exception("\nERROR: Can't Expand An Hidden Root. ")
3443 if not item
.HasPlus():
3446 if item
.IsExpanded():
3449 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3451 event
.SetEventObject(self
)
3453 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3454 # cancelled by program
3458 self
.CalculatePositions()
3460 self
.RefreshSubtree(item
)
3462 if self
._hasWindows
:
3463 # We hide the associated window here, we may show it after
3466 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3467 self
.ProcessEvent(event
)
3470 def ExpandAll(self
, item
):
3471 """Expands all the items."""
3474 raise Exception("\nERROR: Invalid Tree Item. ")
3476 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3478 if not self
.IsExpanded(item
):
3481 child
, cookie
= self
.GetFirstChild(item
)
3484 self
.ExpandAll(child
)
3485 child
, cookie
= self
.GetNextChild(item
, cookie
)
3488 def Collapse(self
, item
):
3490 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3491 EVT_TREE_ITEM_COLLAPSED events.
3495 raise Exception("\nERROR: Invalid Tree Item. ")
3497 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3498 raise Exception("\nERROR: Can't Collapse An Hidden Root. ")
3500 if not item
.IsExpanded():
3503 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3505 event
.SetEventObject(self
)
3506 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3507 # cancelled by program
3510 self
.ChildrenClosing(item
)
3513 self
.CalculatePositions()
3514 self
.RefreshSubtree(item
)
3516 if self
._hasWindows
:
3519 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3520 self
.ProcessEvent(event
)
3523 def CollapseAndReset(self
, item
):
3524 """Collapse the given item and deletes its children."""
3527 self
.DeleteChildren(item
)
3530 def Toggle(self
, item
):
3531 """Toggles the item state (collapsed/expanded)."""
3533 if item
.IsExpanded():
3539 def HideWindows(self
):
3540 """Hides the windows associated to the items. Used internally."""
3542 for child
in self
._itemWithWindow
:
3543 if not self
.IsVisible(child
):
3544 wnd
= child
.GetWindow()
3549 """Unselects the current selection."""
3553 self
._current
.SetHilight(False)
3554 self
.RefreshLine(self
._current
)
3556 self
._current
= None
3557 self
._select
_me
= None
3560 def UnselectAllChildren(self
, item
):
3561 """Unselects all the children of the given item."""
3563 if item
.IsSelected():
3565 item
.SetHilight(False)
3566 self
.RefreshLine(item
)
3568 if item
.HasChildren():
3569 for child
in item
.GetChildren():
3570 self
.UnselectAllChildren(child
)
3573 def UnselectAll(self
):
3574 """Unselect all the items."""
3576 rootItem
= self
.GetRootItem()
3578 # the tree might not have the root item at all
3580 self
.UnselectAllChildren(rootItem
)
3583 # Recursive function !
3584 # To stop we must have crt_item<last_item
3586 # Tag all next children, when no more children,
3587 # Move to parent (not to tag)
3588 # Keep going... if we found last_item, we stop.
3590 def TagNextChildren(self
, crt_item
, last_item
, select
):
3591 """Used internally."""
3593 parent
= crt_item
.GetParent()
3595 if parent
== None: # This is root item
3596 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3598 children
= parent
.GetChildren()
3599 index
= children
.index(crt_item
)
3601 count
= len(children
)
3603 for n
in xrange(index
+1, count
):
3604 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3607 return self
.TagNextChildren(parent
, last_item
, select
)
3610 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3611 """Used internally."""
3613 crt_item
.SetHilight(select
)
3614 self
.RefreshLine(crt_item
)
3616 if crt_item
== last_item
:
3619 if crt_item
.HasChildren():
3620 for child
in crt_item
.GetChildren():
3621 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3627 def SelectItemRange(self
, item1
, item2
):
3628 """Selects all the items between item1 and item2."""
3630 self
._select
_me
= None
3632 # item2 is not necessary after item1
3633 # choice first' and 'last' between item1 and item2
3634 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3635 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3637 select
= self
._current
.IsSelected()
3639 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3642 self
.TagNextChildren(first
, last
, select
)
3645 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3646 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3649 raise Exception("\nERROR: Invalid Tree Item. ")
3651 self
._select
_me
= None
3653 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3655 # to keep going anyhow !!!
3657 if item
.IsSelected():
3658 return # nothing to do
3659 unselect_others
= True
3660 extended_select
= False
3662 elif unselect_others
and item
.IsSelected():
3664 # selection change if there is more than one item currently selected
3665 if len(self
.GetSelections()) == 1:
3668 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3670 event
._itemOld
= self
._current
3671 event
.SetEventObject(self
)
3672 # TODO : Here we don't send any selection mode yet !
3674 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3677 parent
= self
.GetItemParent(item
)
3679 if not self
.IsExpanded(parent
):
3682 parent
= self
.GetItemParent(parent
)
3687 self
.Unselect() # to speed up thing
3693 if not self
._current
:
3694 self
._current
= self
._key
_current
= self
.GetRootItem()
3696 # don't change the mark (self._current)
3697 self
.SelectItemRange(self
._current
, item
)
3701 select
= True # the default
3703 # Check if we need to toggle hilight (ctrl mode)
3704 if not unselect_others
:
3705 select
= not item
.IsSelected()
3707 self
._current
= self
._key
_current
= item
3708 self
._current
.SetHilight(select
)
3709 self
.RefreshLine(self
._current
)
3711 # This can cause idle processing to select the root
3712 # if no item is selected, so it must be after the
3714 self
.EnsureVisible(item
)
3716 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3717 self
.GetEventHandler().ProcessEvent(event
)
3719 # Handles hypertext items
3720 if self
.IsItemHyperText(item
):
3721 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3723 self
.GetEventHandler().ProcessEvent(event
)
3726 def SelectItem(self
, item
, select
=True):
3727 """Selects/deselects an item."""
3730 raise Exception("\nERROR: Invalid Tree Item. ")
3734 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3738 item
.SetHilight(False)
3739 self
.RefreshLine(item
)
3742 def FillArray(self
, item
, array
=[]):
3744 Internal function. Used to populate an array of selected items when
3745 the style TR_MULTIPLE is used.
3751 if item
.IsSelected():
3754 if item
.HasChildren() and item
.IsExpanded():
3755 for child
in item
.GetChildren():
3756 array
= self
.FillArray(child
, array
)
3761 def GetSelections(self
):
3763 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3764 the TR_MULTIPLE style set.
3768 idRoot
= self
.GetRootItem()
3770 array
= self
.FillArray(idRoot
, array
)
3772 #else: the tree is empty, so no selections
3777 def EnsureVisible(self
, item
):
3778 """Ensure that an item is visible in CustomTreeCtrl."""
3781 raise Exception("\nERROR: Invalid Tree Item. ")
3783 # first expand all parent branches
3784 parent
= item
.GetParent()
3786 if self
.HasFlag(TR_HIDE_ROOT
):
3787 while parent
and parent
!= self
._anchor
:
3789 parent
= parent
.GetParent()
3793 parent
= parent
.GetParent()
3798 def ScrollTo(self
, item
):
3799 """Scrolls the specified item into view."""
3804 # We have to call this here because the label in
3805 # question might just have been added and no screen
3806 # update taken place.
3808 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3813 # now scroll to the item
3814 item_y
= item
.GetY()
3815 start_x
, start_y
= self
.GetViewStart()
3816 start_y
*= _PIXELS_PER_UNIT
3818 client_w
, client_h
= self
.GetClientSize()
3822 if item_y
< start_y
+3:
3825 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3826 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3827 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3828 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3829 # Item should appear at top
3830 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3832 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3835 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3836 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3837 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3838 item_y
+= _PIXELS_PER_UNIT
+2
3839 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3840 # Item should appear at bottom
3841 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
)
3844 def OnCompareItems(self
, item1
, item2
):
3846 Returns whether 2 items have the same text.
3847 Override this function in the derived class to change the sort order of the items
3848 in the CustomTreeCtrl. The function should return a negative, zero or positive
3849 value if the first item is less than, equal to or greater than the second one.
3851 The base class version compares items alphabetically.
3854 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3857 def SortChildren(self
, item
):
3859 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3860 You should override that method to change the sort order (the default is ascending
3861 case-sensitive alphabetical order).
3865 raise Exception("\nERROR: Invalid Tree Item. ")
3867 children
= item
.GetChildren()
3869 if len(children
) > 1:
3871 children
.sort(self
.OnCompareItems
)
3874 def GetImageList(self
):
3875 """Returns the normal image list."""
3877 return self
._imageListNormal
3880 def GetButtonsImageList(self
):
3881 """Returns the buttons image list (from which application-defined button images are taken)."""
3883 return self
._imageListButtons
3886 def GetStateImageList(self
):
3887 """Returns the state image list (from which application-defined state images are taken)."""
3889 return self
._imageListState
3892 def GetImageListCheck(self
):
3893 """Returns the image list used to build the check/radio buttons."""
3895 return self
._imageListCheck
3898 def CalculateLineHeight(self
):
3899 """Calculates the height of a line."""
3901 dc
= wx
.ClientDC(self
)
3902 self
._lineHeight
= dc
.GetCharHeight()
3904 if self
._imageListNormal
:
3906 # Calculate a self._lineHeight value from the normal Image sizes.
3907 # May be toggle off. Then CustomTreeCtrl will spread when
3908 # necessary (which might look ugly).
3909 n
= self
._imageListNormal
.GetImageCount()
3913 width
, height
= self
._imageListNormal
.GetSize(i
)
3915 if height
> self
._lineHeight
:
3916 self
._lineHeight
= height
3918 if self
._imageListButtons
:
3920 # Calculate a self._lineHeight value from the Button image sizes.
3921 # May be toggle off. Then CustomTreeCtrl will spread when
3922 # necessary (which might look ugly).
3923 n
= self
._imageListButtons
.GetImageCount()
3927 width
, height
= self
._imageListButtons
.GetSize(i
)
3929 if height
> self
._lineHeight
:
3930 self
._lineHeight
= height
3932 if self
._imageListCheck
:
3934 # Calculate a self._lineHeight value from the check/radio image sizes.
3935 # May be toggle off. Then CustomTreeCtrl will spread when
3936 # necessary (which might look ugly).
3937 n
= self
._imageListCheck
.GetImageCount()
3941 width
, height
= self
._imageListCheck
.GetSize(i
)
3943 if height
> self
._lineHeight
:
3944 self
._lineHeight
= height
3946 if self
._lineHeight
< 30:
3947 self
._lineHeight
+= 2 # at least 2 pixels
3949 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3952 def SetImageList(self
, imageList
):
3953 """Sets the normal image list."""
3955 if self
._ownsImageListNormal
:
3956 del self
._imageListNormal
3958 self
._imageListNormal
= imageList
3959 self
._ownsImageListNormal
= False
3961 # Don't do any drawing if we're setting the list to NULL,
3962 # since we may be in the process of deleting the tree control.
3964 self
.CalculateLineHeight()
3966 # We gray out the image list to use the grayed icons with disabled items
3967 self
._grayedImageList
= wx
.ImageList(16, 16, True, 0)
3969 for ii
in xrange(imageList
.GetImageCount()):
3971 bmp
= imageList
.GetBitmap(ii
)
3972 image
= wx
.ImageFromBitmap(bmp
)
3973 image
= GrayOut(image
)
3974 newbmp
= wx
.BitmapFromImage(image
)
3975 self
._grayedImageList
.Add(newbmp
)
3978 def SetStateImageList(self
, imageList
):
3979 """Sets the state image list (from which application-defined state images are taken)."""
3981 if self
._ownsImageListState
:
3982 del self
._imageListState
3984 self
._imageListState
= imageList
3985 self
._ownsImageListState
= False
3988 def SetButtonsImageList(self
, imageList
):
3989 """Sets the buttons image list (from which application-defined button images are taken)."""
3991 if self
._ownsImageListButtons
:
3992 del self
._imageListButtons
3994 self
._imageListButtons
= imageList
3995 self
._ownsImageListButtons
= False
3997 self
.CalculateLineHeight()
4000 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
4001 """Sets the check image list."""
4005 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
4006 self
._imageListCheck
.Add(GetCheckedBitmap())
4007 self
._imageListCheck
.Add(GetNotCheckedBitmap())
4008 self
._imageListCheck
.Add(GetFlaggedBitmap())
4009 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
4013 sizex
, sizey
= imglist
.GetSize(0)
4014 self
._imageListCheck
= imglist
4016 # We gray out the image list to use the grayed icons with disabled items
4017 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
4019 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
4021 bmp
= self
._imageListCheck
.GetBitmap(ii
)
4022 image
= wx
.ImageFromBitmap(bmp
)
4023 image
= GrayOut(image
)
4024 newbmp
= wx
.BitmapFromImage(image
)
4025 self
._grayedCheckList
.Add(newbmp
)
4030 self
.CalculateLineHeight()
4033 def AssignImageList(self
, imageList
):
4034 """Assigns the normal image list."""
4036 self
.SetImageList(imageList
)
4037 self
._ownsImageListNormal
= True
4040 def AssignStateImageList(self
, imageList
):
4041 """Assigns the state image list."""
4043 self
.SetStateImageList(imageList
)
4044 self
._ownsImageListState
= True
4047 def AssignButtonsImageList(self
, imageList
):
4048 """Assigns the button image list."""
4050 self
.SetButtonsImageList(imageList
)
4051 self
._ownsImageListButtons
= True
4054 # -----------------------------------------------------------------------------
4056 # -----------------------------------------------------------------------------
4058 def AdjustMyScrollbars(self
):
4059 """Adjust the wx.ScrolledWindow scrollbars."""
4063 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4064 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4065 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4066 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4067 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4068 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4072 self
.SetScrollbars(0, 0, 0, 0)
4075 def GetLineHeight(self
, item
):
4076 """Returns the line height for the given item."""
4078 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4079 return item
.GetHeight()
4081 return self
._lineHeight
4084 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4085 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4087 oldpen
= dc
.GetPen()
4088 oldbrush
= dc
.GetBrush()
4089 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4091 # calculate gradient coefficients
4093 col2
= self
._secondcolour
4094 col1
= self
._firstcolour
4096 col2
= self
._hilightUnfocusedBrush
.GetColour()
4097 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4099 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4100 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4102 flrect
= float(rect
.height
)
4104 rstep
= float((r2
- r1
)) / flrect
4105 gstep
= float((g2
- g1
)) / flrect
4106 bstep
= float((b2
- b1
)) / flrect
4108 rf
, gf
, bf
= 0, 0, 0
4110 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4111 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4112 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4113 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4119 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4120 dc
.DrawRectangleRect(rect
)
4121 dc
.SetBrush(oldbrush
)
4124 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4125 """Gradient fill from colour 1 to colour 2 from left to right."""
4127 oldpen
= dc
.GetPen()
4128 oldbrush
= dc
.GetBrush()
4129 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4131 # calculate gradient coefficients
4134 col2
= self
._secondcolour
4135 col1
= self
._firstcolour
4137 col2
= self
._hilightUnfocusedBrush
.GetColour()
4138 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4140 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4141 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4143 flrect
= float(rect
.width
)
4145 rstep
= float((r2
- r1
)) / flrect
4146 gstep
= float((g2
- g1
)) / flrect
4147 bstep
= float((b2
- b1
)) / flrect
4149 rf
, gf
, bf
= 0, 0, 0
4151 for x
in xrange(rect
.x
, rect
.x
+ rect
.width
):
4152 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4153 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4154 dc
.DrawRectangle(x
, rect
.y
, 1, rect
.height
)
4160 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4161 dc
.DrawRectangleRect(rect
)
4162 dc
.SetBrush(oldbrush
)
4165 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4166 """Draw the selected item(s) with the Windows Vista style."""
4170 outer
= _rgbSelectOuter
4171 inner
= _rgbSelectInner
4173 bottom
= _rgbSelectBottom
4177 outer
= _rgbNoFocusOuter
4178 inner
= _rgbNoFocusInner
4179 top
= _rgbNoFocusTop
4180 bottom
= _rgbNoFocusBottom
4182 oldpen
= dc
.GetPen()
4183 oldbrush
= dc
.GetBrush()
4185 bdrRect
= wx
.Rect(*rect
.Get())
4186 filRect
= wx
.Rect(*rect
.Get())
4187 filRect
.Deflate(1,1)
4189 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4190 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4192 flrect
= float(filRect
.height
)
4194 rstep
= float((r2
- r1
)) / flrect
4195 gstep
= float((g2
- g1
)) / flrect
4196 bstep
= float((b2
- b1
)) / flrect
4198 rf
, gf
, bf
= 0, 0, 0
4199 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4201 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4202 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4203 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4204 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4209 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4210 dc
.SetPen(wx
.Pen(outer
))
4211 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4212 bdrRect
.Deflate(1, 1)
4213 dc
.SetPen(wx
.Pen(inner
))
4214 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4217 dc
.SetBrush(oldbrush
)
4220 def PaintItem(self
, item
, dc
):
4221 """Actually paint an item."""
4223 attr
= item
.GetAttributes()
4225 if attr
and attr
.HasFont():
4226 dc
.SetFont(attr
.GetFont())
4228 dc
.SetFont(self
._boldFont
)
4229 if item
.IsHyperText():
4230 dc
.SetFont(self
.GetHyperTextFont())
4231 if item
.GetVisited():
4232 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4234 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4236 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4238 image
= item
.GetCurrentImage()
4239 checkimage
= item
.GetCurrentCheckedImage()
4240 image_w
, image_h
= 0, 0
4242 if image
!= _NO_IMAGE
:
4244 if self
._imageListNormal
:
4246 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4253 if item
.GetType() != 0:
4254 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4257 wcheck
, hcheck
= 0, 0
4259 total_h
= self
.GetLineHeight(item
)
4260 drawItemBackground
= False
4262 if item
.IsSelected():
4264 # under mac selections are only a rectangle in case they don't have the focus
4265 if wx
.Platform
== "__WXMAC__":
4266 if not self
._hasFocus
:
4267 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4268 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4270 dc
.SetBrush(self
._hilightBrush
)
4272 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4273 drawItemBackground
= True
4275 if attr
and attr
.HasBackgroundColour():
4276 drawItemBackground
= True
4277 colBg
= attr
.GetBackgroundColour()
4279 colBg
= self
._backgroundColour
4281 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4282 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4284 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4286 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4288 w
, h
= self
.GetClientSize()
4290 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4292 if item
.IsSelected():
4293 if self
._usegradients
:
4294 if self
._gradientstyle
== 0: # Horizontal
4295 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4297 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4298 elif self
._vistaselection
:
4299 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4301 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4302 flags
= wx
.CONTROL_SELECTED
4303 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4304 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4306 dc
.DrawRectangleRect(itemrect
)
4310 if item
.IsSelected():
4312 # If it's selected, and there's an image, then we should
4313 # take care to leave the area under the image painted in the
4314 # background colour.
4316 wnd
= item
.GetWindow()
4319 wndx
, wndy
= item
.GetWindowSize()
4321 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4323 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4326 if self
._usegradients
:
4327 if self
._gradientstyle
== 0: # Horizontal
4328 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4330 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4331 elif self
._vistaselection
:
4332 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4334 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4335 flags
= wx
.CONTROL_SELECTED
4336 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4337 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4339 dc
.DrawRectangleRect(itemrect
)
4341 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4342 # don't allow backgrounds to be customized. Not drawing the background,
4343 # except for custom item backgrounds, works for both kinds of theme.
4344 elif drawItemBackground
:
4346 minusicon
= wcheck
+ image_w
- 2
4347 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4349 item
.GetWidth()-minusicon
,
4352 if self
._usegradients
and self
._hasFocus
:
4353 if self
._gradientstyle
== 0: # Horizontal
4354 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4356 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4358 dc
.DrawRectangleRect(itemrect
)
4360 if image
!= _NO_IMAGE
:
4362 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4363 if item
.IsEnabled():
4364 imglist
= self
._imageListNormal
4366 imglist
= self
._grayedImageList
4368 imglist
.Draw(image
, dc
,
4369 item
.GetX() + wcheck
,
4370 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4371 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4373 dc
.DestroyClippingRegion()
4376 if item
.IsEnabled():
4377 imglist
= self
._imageListCheck
4379 imglist
= self
._grayedCheckList
4381 imglist
.Draw(checkimage
, dc
,
4383 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4384 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4386 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4387 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4389 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4391 if not item
.IsEnabled():
4392 foreground
= dc
.GetTextForeground()
4393 dc
.SetTextForeground(self
._disabledColour
)
4394 dc
.DrawLabel(item
.GetText(), textrect
)
4395 dc
.SetTextForeground(foreground
)
4397 if wx
.Platform
== "__WXMAC__" and item
.IsSelected() and self
._hasFocus
:
4398 dc
.SetTextForeground(wx
.WHITE
)
4399 dc
.DrawLabel(item
.GetText(), textrect
)
4401 wnd
= item
.GetWindow()
4403 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4404 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4405 if not wnd
.IsShown():
4407 if wnd
.GetPosition() != (wndx
, ya
):
4408 wnd
.SetPosition((wndx
, ya
))
4410 # restore normal font
4411 dc
.SetFont(self
._normalFont
)
4414 # Now y stands for the top of the item, whereas it used to stand for middle !
4415 def PaintLevel(self
, item
, dc
, level
, y
):
4416 """Paint a level of CustomTreeCtrl."""
4418 x
= level
*self
._indent
4420 if not self
.HasFlag(TR_HIDE_ROOT
):
4426 # always expand hidden root
4428 children
= item
.GetChildren()
4429 count
= len(children
)
4435 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4438 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4440 # draw line down to last child
4441 origY
+= self
.GetLineHeight(children
[0])>>1
4442 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4443 dc
.DrawLine(3, origY
, 3, oldY
)
4447 item
.SetX(x
+self
._spacing
)
4450 h
= self
.GetLineHeight(item
)
4452 y_mid
= y_top
+ (h
>>1)
4455 exposed_x
= dc
.LogicalToDeviceX(0)
4456 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4458 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4459 if wx
.Platform
== "__WXMAC__":
4460 # don't draw rect outline if we already have the
4461 # background color under Mac
4462 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4464 pen
= self
._borderPen
4466 if item
.IsSelected():
4467 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4468 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4470 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4472 attr
= item
.GetAttributes()
4473 if attr
and attr
.HasTextColour():
4474 colText
= attr
.GetTextColour()
4476 colText
= self
.GetForegroundColour()
4478 if self
._vistaselection
:
4482 dc
.SetTextForeground(colText
)
4487 self
.PaintItem(item
, dc
)
4489 if self
.HasFlag(TR_ROW_LINES
):
4491 # if the background colour is white, choose a
4492 # contrasting color for the lines
4493 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4494 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4495 dc
.DrawLine(0, y_top
, 10000, y_top
)
4496 dc
.DrawLine(0, y
, 10000, y
)
4498 # restore DC objects
4499 dc
.SetBrush(wx
.WHITE_BRUSH
)
4500 dc
.SetTextForeground(wx
.BLACK
)
4502 if not self
.HasFlag(TR_NO_LINES
):
4504 # draw the horizontal line here
4505 dc
.SetPen(self
._dottedPen
)
4507 if x
> self
._indent
:
4508 x_start
-= self
._indent
4509 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4511 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4514 # should the item show a button?
4515 if item
.HasPlus() and self
.HasButtons():
4517 if self
._imageListButtons
:
4519 # draw the image button here
4522 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4523 if item
.IsSelected():
4524 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4526 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4528 yy
= y_mid
- image_h
/2
4530 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4531 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4532 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4533 dc
.DestroyClippingRegion()
4535 else: # no custom buttons
4537 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4538 # We draw something like the Mac twist buttons
4540 dc
.SetPen(wx
.BLACK_PEN
)
4541 dc
.SetBrush(self
._hilightBrush
)
4542 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4544 if item
.IsExpanded():
4546 button
[0].y
= y_mid
- 3
4548 button
[1].y
= button
[0].y
4550 button
[2].y
= button
[0].y
+ 6
4553 button
[0].y
= y_mid
- 5
4554 button
[1].x
= button
[0].x
4555 button
[1].y
= y_mid
+ 5
4556 button
[2].x
= button
[0].x
+ 5
4559 dc
.DrawPolygon(button
)
4562 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4569 if item
.IsExpanded():
4570 flag |
= _CONTROL_EXPANDED
4571 if item
== self
._underMouse
:
4572 flag |
= _CONTROL_CURRENT
4574 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4576 if item
.IsExpanded():
4578 children
= item
.GetChildren()
4579 count
= len(children
)
4588 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4591 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4593 # draw line down to last child
4594 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4595 if self
.HasButtons():
4598 # Only draw the portion of the line that is visible, in case it is huge
4599 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4600 yOrigin
= abs(yOrigin
)
4601 width
, height
= self
.GetClientSize()
4603 # Move end points to the begining/end of the view?
4606 if oldY
> yOrigin
+ height
:
4607 oldY
= yOrigin
+ height
4609 # after the adjustments if y_mid is larger than oldY then the line
4610 # isn't visible at all so don't draw anything
4612 dc
.SetPen(self
._dottedPen
)
4613 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4618 # -----------------------------------------------------------------------------
4619 # wxWidgets callbacks
4620 # -----------------------------------------------------------------------------
4622 def OnPaint(self
, event
):
4623 """Handles the wx.EVT_PAINT event."""
4625 dc
= wx
.PaintDC(self
)
4628 if not self
._anchor
:
4631 dc
.SetFont(self
._normalFont
)
4632 dc
.SetPen(self
._dottedPen
)
4635 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4638 def OnEraseBackground(self
, event
):
4639 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4641 # Can we actually do something here (or in OnPaint()) To Handle
4642 # background images that are stretchable or always centered?
4643 # I tried but I get enormous flickering...
4645 if not self
._backgroundImage
:
4649 if self
._imageStretchStyle
== _StyleTile
:
4653 dc
= wx
.ClientDC(self
)
4654 rect
= self
.GetUpdateRegion().GetBox()
4655 dc
.SetClippingRect(rect
)
4657 self
.TileBackground(dc
)
4660 def TileBackground(self
, dc
):
4661 """Tiles the background image to fill all the available area."""
4663 sz
= self
.GetClientSize()
4664 w
= self
._backgroundImage
.GetWidth()
4665 h
= self
._backgroundImage
.GetHeight()
4672 while y
< sz
.height
:
4673 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4679 def OnSetFocus(self
, event
):
4680 """Handles the wx.EVT_SET_FOCUS event."""
4682 self
._hasFocus
= True
4683 self
.RefreshSelected()
4687 def OnKillFocus(self
, event
):
4688 """Handles the wx.EVT_KILL_FOCUS event."""
4690 self
._hasFocus
= False
4691 self
.RefreshSelected()
4695 def OnKeyDown(self
, event
):
4696 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4698 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4700 te
.SetEventObject(self
)
4702 if self
.GetEventHandler().ProcessEvent(te
):
4703 # intercepted by the user code
4706 if self
._current
is None or self
._key
_current
is None:
4711 # how should the selection work for this event?
4712 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4716 # * : Expand all/Collapse all
4717 # ' ' | return : activate
4718 # up : go up (not last children!)
4720 # left : go to parent
4721 # right : open if parent and go next
4723 # end : go to last item without opening parents
4724 # alnum : start or continue searching for the item with this prefix
4726 keyCode
= event
.GetKeyCode()
4728 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4729 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4730 self
.Expand(self
._current
)
4732 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4733 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4735 self
.ExpandAll(self
._current
)
4737 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4738 if self
.IsExpanded(self
._current
):
4739 self
.Collapse(self
._current
)
4741 elif keyCode
== wx
.WXK_MENU
:
4742 # Use the item's bounding rectangle to determine position for the event
4743 itemRect
= self
.GetBoundingRect(self
._current
, True)
4744 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4745 event
._item
= self
._current
4746 # Use the left edge, vertical middle
4747 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4748 event
.SetEventObject(self
)
4749 self
.GetEventHandler().ProcessEvent(event
)
4751 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4753 if not self
.IsEnabled(self
._current
):
4757 if not event
.HasModifiers():
4758 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4759 event
._item
= self
._current
4760 event
.SetEventObject(self
)
4761 self
.GetEventHandler().ProcessEvent(event
)
4763 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4764 checked
= not self
.IsItemChecked(self
._current
)
4765 self
.CheckItem(self
._current
, checked
)
4767 # in any case, also generate the normal key event for this key,
4768 # even if we generated the ACTIVATED event above: this is what
4769 # wxMSW does and it makes sense because you might not want to
4770 # process ACTIVATED event at all and handle Space and Return
4771 # directly (and differently) which would be impossible otherwise
4774 # up goes to the previous sibling or to the last
4775 # of its children if it's expanded
4776 elif keyCode
== wx
.WXK_UP
:
4777 prev
= self
.GetPrevSibling(self
._key
_current
)
4779 prev
= self
.GetItemParent(self
._key
_current
)
4780 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4784 current
= self
._key
_current
4785 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4786 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4787 # otherwise we return to where we came from
4788 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4789 self
._key
_current
= prev
4792 current
= self
._key
_current
4794 # We are going to another parent node
4795 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4796 child
= self
.GetLastChild(prev
)
4801 # Try to get the previous siblings and see if they are active
4802 while prev
and not self
.IsEnabled(prev
):
4803 prev
= self
.GetPrevSibling(prev
)
4806 # No previous siblings active: go to the parent and up
4807 prev
= self
.GetItemParent(current
)
4808 while prev
and not self
.IsEnabled(prev
):
4809 prev
= self
.GetItemParent(prev
)
4812 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4813 self
._key
_current
= prev
4815 # left arrow goes to the parent
4816 elif keyCode
== wx
.WXK_LEFT
:
4818 prev
= self
.GetItemParent(self
._current
)
4819 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4820 # don't go to root if it is hidden
4821 prev
= self
.GetPrevSibling(self
._current
)
4823 if self
.IsExpanded(self
._current
):
4824 self
.Collapse(self
._current
)
4826 if prev
and self
.IsEnabled(prev
):
4827 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4829 elif keyCode
== wx
.WXK_RIGHT
:
4830 # this works the same as the down arrow except that we
4831 # also expand the item if it wasn't expanded yet
4832 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4833 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4834 if self
.IsEnabled(child
):
4835 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4836 self
._key
_current
= child
4838 self
.Expand(self
._current
)
4841 elif keyCode
== wx
.WXK_DOWN
:
4842 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4844 child
= self
.GetNextActiveItem(self
._key
_current
)
4847 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4848 self
._key
_current
= child
4852 next
= self
.GetNextSibling(self
._key
_current
)
4855 current
= self
._key
_current
4856 while current
and not next
:
4857 current
= self
.GetItemParent(current
)
4859 next
= self
.GetNextSibling(current
)
4860 if not next
or not self
.IsEnabled(next
):
4864 while next
and not self
.IsEnabled(next
):
4865 next
= self
.GetNext(next
)
4868 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4869 self
._key
_current
= next
4872 # <End> selects the last visible tree item
4873 elif keyCode
== wx
.WXK_END
:
4875 last
= self
.GetRootItem()
4877 while last
and self
.IsExpanded(last
):
4879 lastChild
= self
.GetLastChild(last
)
4881 # it may happen if the item was expanded but then all of
4882 # its children have been deleted - so IsExpanded() returned
4883 # true, but GetLastChild() returned invalid item
4889 if last
and self
.IsEnabled(last
):
4891 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4893 # <Home> selects the root item
4894 elif keyCode
== wx
.WXK_HOME
:
4896 prev
= self
.GetRootItem()
4901 if self
.HasFlag(TR_HIDE_ROOT
):
4902 prev
, cookie
= self
.GetFirstChild(prev
)
4906 if self
.IsEnabled(prev
):
4907 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4911 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4912 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4913 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4915 # find the next item starting with the given prefix
4917 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4923 if self
.IsEnabled(id):
4925 self
._findPrefix
+= ch
4927 # also start the timer to reset the current prefix if the user
4928 # doesn't press any more alnum keys soon -- we wouldn't want
4929 # to use this prefix for a new item search
4930 if not self
._findTimer
:
4931 self
._findTimer
= TreeFindTimer(self
)
4933 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4940 def GetNextActiveItem(self
, item
, down
=True):
4941 """Returns the next active item. Used Internally at present. """
4944 sibling
= self
.GetNextSibling
4946 sibling
= self
.GetPrevSibling
4948 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4949 # Is an unchecked radiobutton... all its children are inactive
4950 # try to get the next/previous sibling
4954 child
= sibling(item
)
4955 if (child
and self
.IsEnabled(child
)) or not child
:
4960 # Tha's not a radiobutton... but some of its children can be
4962 child
, cookie
= self
.GetFirstChild(item
)
4963 while child
and not self
.IsEnabled(child
):
4964 child
, cookie
= self
.GetNextChild(item
, cookie
)
4966 if child
and self
.IsEnabled(child
):
4972 def HitTest(self
, point
, flags
=0):
4974 Calculates which (if any) item is under the given point, returning the tree item
4975 at this point plus extra information flags. Flags is a bitlist of the following:
4977 TREE_HITTEST_ABOVE above the client area
4978 TREE_HITTEST_BELOW below the client area
4979 TREE_HITTEST_NOWHERE no item has been hit
4980 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4981 TREE_HITTEST_ONITEMICON on the icon associated to an item
4982 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4983 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4984 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4985 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4986 TREE_HITTEST_TOLEFT on the left of the client area
4987 TREE_HITTEST_TORIGHT on the right of the client area
4988 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4989 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4990 TREE_HITTEST_ONITEM anywhere on the item
4992 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4995 w
, h
= self
.GetSize()
4999 flags |
= TREE_HITTEST_TOLEFT
5001 flags |
= TREE_HITTEST_TORIGHT
5003 flags |
= TREE_HITTEST_ABOVE
5005 flags |
= TREE_HITTEST_BELOW
5010 if self
._anchor
== None:
5011 flags
= TREE_HITTEST_NOWHERE
5014 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5017 flags
= TREE_HITTEST_NOWHERE
5020 if not self
.IsEnabled(hit
):
5026 def GetBoundingRect(self
, item
, textOnly
=False):
5027 """Gets the bounding rectangle of the item."""
5030 raise Exception("\nERROR: Invalid Tree Item. ")
5034 startX
, startY
= self
.GetViewStart()
5037 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5038 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5039 rect
.width
= i
.GetWidth()
5040 rect
.height
= self
.GetLineHeight(i
)
5045 def Edit(self
, item
):
5047 Internal function. Starts the editing of an item label, sending a
5048 EVT_TREE_BEGIN_LABEL_EDIT event.
5051 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5053 te
.SetEventObject(self
)
5054 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5058 # We have to call this here because the label in
5059 # question might just have been added and no screen
5060 # update taken place.
5062 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5067 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5068 self
._textCtrl
.StopEditing()
5070 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5071 self
._textCtrl
.SetFocus()
5074 def GetEditControl(self
):
5076 Returns a pointer to the edit TextCtrl if the item is being edited or
5077 None otherwise (it is assumed that no more than one item may be edited
5081 return self
._textCtrl
5084 def OnRenameAccept(self
, item
, value
):
5086 Called by TreeTextCtrl, to accept the changes and to send the
5087 EVT_TREE_END_LABEL_EDIT event.
5090 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5092 le
.SetEventObject(self
)
5094 le
._editCancelled
= False
5096 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5099 def OnRenameCancelled(self
, item
):
5101 Called by TreeTextCtrl, to cancel the changes and to send the
5102 EVT_TREE_END_LABEL_EDIT event.
5105 # let owner know that the edit was cancelled
5106 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5108 le
.SetEventObject(self
)
5110 le
._editCancelled
= True
5112 self
.GetEventHandler().ProcessEvent(le
)
5115 def OnRenameTimer(self
):
5116 """The timer for renaming has expired. Start editing."""
5118 self
.Edit(self
._current
)
5121 def OnMouse(self
, event
):
5122 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5124 if not self
._anchor
:
5127 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5129 # Is the mouse over a tree item button?
5131 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5132 underMouse
= thisItem
5133 underMouseChanged
= underMouse
!= self
._underMouse
5135 if underMouse
and (flags
& TREE_HITTEST_ONITEM
) and not event
.LeftIsDown() and \
5136 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5137 underMouse
= underMouse
5141 if underMouse
!= self
._underMouse
:
5142 if self
._underMouse
:
5143 # unhighlight old item
5144 self
._underMouse
= None
5146 self
._underMouse
= underMouse
5148 # Determines what item we are hovering over and need a tooltip for
5149 hoverItem
= thisItem
5151 # We do not want a tooltip if we are dragging, or if the rename timer is running
5152 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5154 if hoverItem
is not None:
5155 # Ask the tree control what tooltip (if any) should be shown
5156 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5157 hevent
._item
= hoverItem
5158 hevent
.SetEventObject(self
)
5160 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5161 self
.SetToolTip(hevent
._label
)
5163 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5164 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5165 self
._isonhyperlink
= True
5167 if self
._isonhyperlink
:
5168 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5169 self
._isonhyperlink
= False
5171 # we process left mouse up event (enables in-place edit), right down
5172 # (pass to the user code), left dbl click (activate item) and
5173 # dragging/moving events for items drag-and-drop
5175 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5176 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5182 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5184 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5186 if self
._dragCount
== 0:
5187 self
._dragStart
= pt
5190 self
._dragCount
= self
._dragCount
+ 1
5192 if self
._dragCount
!= 3:
5193 # wait until user drags a bit further...
5196 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5198 nevent
= TreeEvent(command
, self
.GetId())
5199 nevent
._item
= self
._current
5200 nevent
.SetEventObject(self
)
5201 newpt
= self
.CalcScrolledPosition(pt
)
5202 nevent
.SetPoint(newpt
)
5204 # by default the dragging is not supported, the user code must
5205 # explicitly allow the event for it to take place
5208 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5210 # we're going to drag this item
5211 self
._isDragging
= True
5213 # remember the old cursor because we will change it while
5215 self
._oldCursor
= self
._cursor
5217 # in a single selection control, hide the selection temporarily
5218 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5219 self
._oldSelection
= self
.GetSelection()
5221 if self
._oldSelection
:
5223 self
._oldSelection
.SetHilight(False)
5224 self
.RefreshLine(self
._oldSelection
)
5226 selections
= self
.GetSelections()
5227 if len(selections
) == 1:
5228 self
._oldSelection
= selections
[0]
5229 self
._oldSelection
.SetHilight(False)
5230 self
.RefreshLine(self
._oldSelection
)
5235 # Create the custom draw image from the icons and the text of the item
5236 self
._dragImage
= DragImage(self
, self
._current
)
5237 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5238 self
._dragImage
.Show()
5239 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5241 elif event
.Dragging() and self
._isDragging
:
5243 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5245 if self
._countDrag
== 0 and item
:
5246 self
._oldItem
= item
5248 if item
!= self
._dropTarget
:
5250 # unhighlight the previous drop target
5251 if self
._dropTarget
:
5252 self
._dropTarget
.SetHilight(False)
5253 self
.RefreshLine(self
._dropTarget
)
5255 item
.SetHilight(True)
5256 self
.RefreshLine(item
)
5257 self
._countDrag
= self
._countDrag
+ 1
5258 self
._dropTarget
= item
5262 if self
._countDrag
>= 3:
5263 # Here I am trying to avoid ugly repainting problems... hope it works
5264 self
.RefreshLine(self
._oldItem
)
5267 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5270 self
._dragImage
.EndDrag()
5272 if self
._dropTarget
:
5273 self
._dropTarget
.SetHilight(False)
5275 if self
._oldSelection
:
5277 self
._oldSelection
.SetHilight(True)
5278 self
.RefreshLine(self
._oldSelection
)
5279 self
._oldSelection
= None
5281 # generate the drag end event
5282 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5284 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5285 event
.SetEventObject(self
)
5287 self
.GetEventHandler().ProcessEvent(event
)
5289 self
._isDragging
= False
5290 self
._dropTarget
= None
5292 self
.SetCursor(self
._oldCursor
)
5294 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5297 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5302 # If we got to this point, we are not dragging or moving the mouse.
5303 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5304 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5305 # We skip even if we didn't hit an item because we still should
5306 # restore focus to the tree control even if we didn't exactly hit an item.
5307 if event
.LeftDown():
5308 self
._hasFocus
= True
5309 self
.SetFocusIgnoringChildren()
5312 # here we process only the messages which happen on tree items
5317 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5318 self
._textCtrl
.StopEditing()
5319 return # we hit the blank area
5321 if event
.RightDown():
5323 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5324 self
._textCtrl
.StopEditing()
5326 self
._hasFocus
= True
5327 self
.SetFocusIgnoringChildren()
5329 # If the item is already selected, do not update the selection.
5330 # Multi-selections should not be cleared if a selected item is clicked.
5331 if not self
.IsSelected(item
):
5333 self
.DoSelectItem(item
, True, False)
5335 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5337 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5338 nevent
.SetEventObject(self
)
5339 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5341 # Consistent with MSW (for now), send the ITEM_MENU *after*
5342 # the RIGHT_CLICK event. TODO: This behaviour may change.
5343 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5344 nevent2
._item
= item
5345 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5346 nevent2
.SetEventObject(self
)
5347 self
.GetEventHandler().ProcessEvent(nevent2
)
5349 elif event
.LeftUp():
5351 # this facilitates multiple-item drag-and-drop
5353 if self
.HasFlag(TR_MULTIPLE
):
5355 selections
= self
.GetSelections()
5357 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5359 self
.DoSelectItem(item
, True, False)
5361 if self
._lastOnSame
:
5363 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5365 if self
._renameTimer
:
5367 if self
._renameTimer
.IsRunning():
5369 self
._renameTimer
.Stop()
5373 self
._renameTimer
= TreeRenameTimer(self
)
5375 self
._renameTimer
.Start(_DELAY
, True)
5377 self
._lastOnSame
= False
5380 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5382 if not item
or not item
.IsEnabled():
5383 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5384 self
._textCtrl
.StopEditing()
5387 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5388 self
._textCtrl
.StopEditing()
5390 self
._hasFocus
= True
5391 self
.SetFocusIgnoringChildren()
5393 if event
.LeftDown():
5395 self
._lastOnSame
= item
== self
._current
5397 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5399 # only toggle the item for a single click, double click on
5400 # the button doesn't do anything (it toggles the item twice)
5401 if event
.LeftDown():
5405 # don't select the item if the button was clicked
5408 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5410 if event
.LeftDown():
5412 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5416 # clear the previously selected items, if the
5417 # user clicked outside of the present selection.
5418 # otherwise, perform the deselection on mouse-up.
5419 # this allows multiple drag and drop to work.
5420 # but if Cmd is down, toggle selection of the clicked item
5421 if not self
.IsSelected(item
) or event
.CmdDown():
5423 if flags
& TREE_HITTEST_ONITEM
:
5424 # how should the selection work for this event?
5425 if item
.IsHyperText():
5426 self
.SetItemVisited(item
, True)
5428 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5432 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5434 # For some reason, Windows isn't recognizing a left double-click,
5435 # so we need to simulate it here. Allow 200 milliseconds for now.
5436 if event
.LeftDClick():
5438 # double clicking should not start editing the item label
5439 if self
._renameTimer
:
5440 self
._renameTimer
.Stop()
5442 self
._lastOnSame
= False
5444 # send activate event first
5445 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5447 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5448 nevent
.SetEventObject(self
)
5449 if not self
.GetEventHandler().ProcessEvent(nevent
):
5451 # if the user code didn't process the activate event,
5452 # handle it ourselves by toggling the item when it is
5454 ## if item.HasPlus():
5458 def OnInternalIdle(self
):
5459 """Performs operations in idle time (essentially drawing)."""
5461 # Check if we need to select the root item
5462 # because nothing else has been selected.
5463 # Delaying it means that we can invoke event handlers
5464 # as required, when a first item is selected.
5465 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5468 self
.SelectItem(self
._select
_me
)
5469 elif self
.GetRootItem():
5470 self
.SelectItem(self
.GetRootItem())
5472 # after all changes have been done to the tree control,
5473 # we actually redraw the tree when everything is over
5477 if self
._freezeCount
:
5482 self
.CalculatePositions()
5484 self
.AdjustMyScrollbars()
5489 def CalculateSize(self
, item
, dc
):
5490 """Calculates overall position and size of an item."""
5492 attr
= item
.GetAttributes()
5494 if attr
and attr
.HasFont():
5495 dc
.SetFont(attr
.GetFont())
5497 dc
.SetFont(self
._boldFont
)
5499 dc
.SetFont(self
._normalFont
)
5501 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5504 # restore normal font
5505 dc
.SetFont(self
._normalFont
)
5507 image_w
, image_h
= 0, 0
5508 image
= item
.GetCurrentImage()
5510 if image
!= _NO_IMAGE
:
5512 if self
._imageListNormal
:
5514 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5517 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5519 checkimage
= item
.GetCurrentCheckedImage()
5520 if checkimage
is not None:
5521 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5527 total_h
+= 2 # at least 2 pixels
5529 total_h
+= total_h
/10 # otherwise 10% extra spacing
5531 if total_h
> self
._lineHeight
:
5532 self
._lineHeight
= total_h
5534 if not item
.GetWindow():
5535 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5536 item
.SetHeight(total_h
)
5538 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5541 def CalculateLevel(self
, item
, dc
, level
, y
):
5542 """Calculates the level of an item."""
5544 x
= level
*self
._indent
5546 if not self
.HasFlag(TR_HIDE_ROOT
):
5552 # a hidden root is not evaluated, but its
5553 # children are always calculated
5554 children
= item
.GetChildren()
5555 count
= len(children
)
5557 for n
in xrange(count
):
5558 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5562 self
.CalculateSize(item
, dc
)
5565 item
.SetX(x
+self
._spacing
)
5567 y
+= self
.GetLineHeight(item
)
5569 if not item
.IsExpanded():
5570 # we don't need to calculate collapsed branches
5573 children
= item
.GetChildren()
5574 count
= len(children
)
5576 for n
in xrange(count
):
5577 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5582 def CalculatePositions(self
):
5583 """Calculates all the positions of the visible items."""
5585 if not self
._anchor
:
5588 dc
= wx
.ClientDC(self
)
5591 dc
.SetFont(self
._normalFont
)
5592 dc
.SetPen(self
._dottedPen
)
5594 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5597 def RefreshSubtree(self
, item
):
5598 """Refreshes a damaged subtree of an item."""
5602 if self
._freezeCount
:
5605 client
= self
.GetClientSize()
5608 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5609 rect
.width
= client
.x
5610 rect
.height
= client
.y
5612 self
.Refresh(True, rect
)
5613 self
.AdjustMyScrollbars()
5616 def RefreshLine(self
, item
):
5617 """Refreshes a damaged item line."""
5621 if self
._freezeCount
:
5625 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5626 rect
.width
= self
.GetClientSize().x
5627 rect
.height
= self
.GetLineHeight(item
)
5629 self
.Refresh(True, rect
)
5632 def RefreshSelected(self
):
5633 """Refreshes a damaged selected item line."""
5635 if self
._freezeCount
:
5638 # TODO: this is awfully inefficient, we should keep the list of all
5639 # selected items internally, should be much faster
5641 self
.RefreshSelectedUnder(self
._anchor
)
5644 def RefreshSelectedUnder(self
, item
):
5645 """Refreshes the selected items under the given item."""
5647 if self
._freezeCount
:
5650 if item
.IsSelected():
5651 self
.RefreshLine(item
)
5653 children
= item
.GetChildren()
5654 for child
in children
:
5655 self
.RefreshSelectedUnder(child
)
5659 """Freeze CustomTreeCtrl."""
5661 self
._freezeCount
= self
._freezeCount
+ 1
5665 """Thaw CustomTreeCtrl."""
5667 if self
._freezeCount
== 0:
5668 raise Exception("\nERROR: Thawing Unfrozen Tree Control?")
5670 self
._freezeCount
= self
._freezeCount
- 1
5672 if not self
._freezeCount
:
5676 # ----------------------------------------------------------------------------
5677 # changing colours: we need to refresh the tree control
5678 # ----------------------------------------------------------------------------
5680 def SetBackgroundColour(self
, colour
):
5681 """Changes the background colour of CustomTreeCtrl."""
5683 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5686 if self
._freezeCount
:
5694 def SetForegroundColour(self
, colour
):
5695 """Changes the foreground colour of CustomTreeCtrl."""
5697 if not wx
.Window
.SetForegroundColour(self
, colour
):
5700 if self
._freezeCount
:
5708 def OnGetToolTip(self
, event
):
5710 Process the tooltip event, to speed up event processing. Does not actually
5717 def DoGetBestSize(self
):
5718 """Something is better than nothing..."""
5720 # something is better than nothing...
5721 # 100x80 is what the MSW version will get from the default
5722 # wxControl::DoGetBestSize
5724 return wx
.Size(100, 80)
5727 def GetClassDefaultAttributes(self
):
5728 """Gets the class default attributes."""
5730 attr
= wx
.VisualAttributes()
5731 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5732 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5733 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)