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 "\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 "\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 "\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
= ((r
>> 1) - 20, (g
>> 1) - 20, (b
>> 1) - 20)
1794 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1795 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1797 # image list for icons
1798 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1799 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1801 # Drag and drop initial settings
1804 self
._isDragging
= False
1805 self
._dropTarget
= self
._oldSelection
= None
1806 self
._dragImage
= None
1807 self
._underMouse
= None
1809 # TextCtrl initial settings for editable items
1810 self
._textCtrl
= None
1811 self
._renameTimer
= None
1813 # This one allows us to handle Freeze() and Thaw() calls
1814 self
._freezeCount
= 0
1816 self
._findPrefix
= ""
1817 self
._findTimer
= None
1819 self
._dropEffectAboveItem
= False
1820 self
._lastOnSame
= False
1822 # Default normal and bold fonts for an item
1823 self
._hasFont
= True
1824 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1825 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1826 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1827 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1831 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1832 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1833 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1834 self
._hypertextnewcolour
= wx
.BLUE
1835 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1836 self
._isonhyperlink
= False
1838 # Default CustomTreeCtrl background colour.
1839 self
._backgroundColour
= wx
.WHITE
1841 # Background image settings
1842 self
._backgroundImage
= None
1843 self
._imageStretchStyle
= _StyleTile
1845 # Disabled items colour
1846 self
._disabledColour
= wx
.Colour(180, 180, 180)
1848 # Gradient selection colours
1849 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1850 self
._secondcolour
= wx
.WHITE
1851 self
._usegradients
= False
1852 self
._gradientstyle
= 0 # Horizontal Gradient
1854 # Vista Selection Styles
1855 self
._vistaselection
= False
1857 # Connection lines style
1858 if wx
.Platform
!= "__WXMAC__":
1859 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1860 self
._dottedPen
.SetDashes([1,1])
1861 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1863 self
._dottedPen
= wx
.Pen("grey", 1)
1865 # Pen Used To Draw The Border Around Selected Items
1866 self
._borderPen
= wx
.BLACK_PEN
1867 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1869 # For Appended Windows
1870 self
._hasWindows
= False
1871 self
._itemWithWindow
= []
1873 if wx
.Platform
== "__WXMAC__":
1874 ctstyle
&= ~TR_LINES_AT_ROOT
1875 ctstyle |
= TR_NO_LINES
1877 platform
, major
, minor
= wx
.GetOsVersion()
1879 ctstyle |
= TR_ROW_LINES
1881 self
._windowStyle
= ctstyle
1883 # Create the default check image list
1884 self
.SetImageListCheck(13, 13)
1886 # A constant to use my translation of RendererNative.DrawTreeItemButton
1887 # if the wxPython version is less than 2.6.2.1.
1888 if wx
.VERSION_STRING
< "2.6.2.1":
1889 self
._drawingfunction
= DrawTreeItemButton
1891 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1893 # Create our container... at last!
1894 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1896 # If the tree display has no buttons, but does have
1897 # connecting lines, we can use a narrower layout.
1898 # It may not be a good idea to force this...
1899 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1903 self
.SetValidator(validator
)
1905 attr
= self
.GetDefaultAttributes()
1906 self
.SetOwnForegroundColour(attr
.colFg
)
1907 self
.SetOwnBackgroundColour(wx
.WHITE
)
1909 if not self
._hasFont
:
1910 self
.SetOwnFont(attr
.font
)
1915 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1916 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1917 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1918 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1919 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1920 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1921 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1922 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1924 # Sets the focus to ourselves: this is useful if you have items
1925 # with associated widgets.
1929 def AcceptsFocus(self
):
1930 # overridden base class method, allows this ctrl to
1931 # participate in the tab-order, etc. It's overridable because
1932 # of deriving this class from wx.PyScrolledWindow...
1936 def OnDestroy(self
, event
):
1937 """Handles the wx.EVT_WINDOW_DESTROY event."""
1939 # Here there may be something I miss... do I have to destroy
1941 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1942 self
._renameTimer
.Stop()
1943 del self
._renameTimer
1945 if self
._findTimer
and self
._findTimer
.IsRunning():
1946 self
._findTimer
.Stop()
1953 """Returns the global number of items in the tree."""
1955 if not self
._anchor
:
1959 count
= self
._anchor
.GetChildrenCount()
1961 if not self
.HasFlag(TR_HIDE_ROOT
):
1962 # take the root itself into account
1968 def GetIndent(self
):
1969 """Returns the item indentation."""
1974 def GetSpacing(self
):
1975 """Returns the spacing between the start and the text."""
1977 return self
._spacing
1980 def GetRootItem(self
):
1981 """Returns the root item."""
1986 def GetSelection(self
):
1987 """Returns the current selection: TR_SINGLE only."""
1989 return self
._current
1992 def ToggleItemSelection(self
, item
):
1993 """Toggles the item selection."""
1996 raise "\nERROR: Invalid Tree Item. "
1998 self
.SelectItem(item
, not self
.IsSelected(item
))
2001 def EnableChildren(self
, item
, enable
=True):
2002 """Enables/disables item children. Used internally."""
2005 if item
.IsExpanded():
2008 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2009 # We hit a radiobutton item not checked, we don't want to
2010 # enable the children
2013 child
, cookie
= self
.GetFirstChild(item
)
2015 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2017 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2018 self
.EnableChildren(child
, enable
)
2019 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2022 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2023 """Enables/disables an item."""
2026 raise "\nERROR: Invalid Tree Item. "
2028 if item
.IsEnabled() == enable
:
2031 if not enable
and item
.IsSelected():
2032 self
.SelectItem(item
, False)
2035 wnd
= item
.GetWindow()
2037 # Handles the eventual window associated to the item
2039 wndenable
= item
.GetWindowEnabled()
2047 # We have to refresh the item line
2048 dc
= wx
.ClientDC(self
)
2049 self
.CalculateSize(item
, dc
)
2050 self
.RefreshLine(item
)
2053 def IsEnabled(self
, item
):
2054 """Returns whether an item is enabled or disabled."""
2057 raise "\nERROR: Invalid Tree Item. "
2059 return item
.IsEnabled()
2062 def SetDisabledColour(self
, colour
):
2063 """Sets the items disabled colour."""
2065 self
._disabledColour
= colour
2069 def GetDisabledColour(self
):
2070 """Returns the items disabled colour."""
2072 return self
._disabledColour
2075 def IsItemChecked(self
, item
):
2076 """Returns whether an item is checked or not."""
2079 raise "\nERROR: Invalid Tree Item. "
2081 return item
.IsChecked()
2084 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2085 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2087 if item
.GetType() == 0:
2093 dc
= wx
.ClientDC(self
)
2094 self
.CalculateSize(item
, dc
)
2095 self
.RefreshLine(item
)
2098 def UnCheckRadioParent(self
, item
, checked
=False):
2099 """Used internally to handle radio node parent correctly."""
2101 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2103 e
.SetEventObject(self
)
2105 if self
.GetEventHandler().ProcessEvent(e
):
2109 self
.RefreshLine(item
)
2110 self
.EnableChildren(item
, checked
)
2111 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2113 e
.SetEventObject(self
)
2114 self
.GetEventHandler().ProcessEvent(e
)
2119 def CheckItem(self
, item
, checked
=True):
2121 Actually checks/uncheks an item, sending (eventually) the two
2122 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2126 raise "\nERROR: Invalid Tree Item. "
2128 # Should we raise an error here?!?
2129 if item
.GetType() == 0:
2132 if item
.GetType() == 2: # it's a radio button
2133 if not checked
and item
.IsChecked(): # Try To Unckeck?
2134 if item
.HasChildren():
2135 self
.UnCheckRadioParent(item
, checked
)
2138 if not self
.UnCheckRadioParent(item
, checked
):
2141 self
.CheckSameLevel(item
, False)
2144 # Radiobuttons are done, let's handle checkbuttons...
2145 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2147 e
.SetEventObject(self
)
2149 if self
.GetEventHandler().ProcessEvent(e
):
2154 dc
= wx
.ClientDC(self
)
2155 self
.RefreshLine(item
)
2157 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2158 ischeck
= self
.IsItemChecked(item
)
2159 self
.AutoCheckChild(item
, ischeck
)
2160 if self
._windowStyle
& TR_AUTO_CHECK_PARENT
:
2161 ischeck
= self
.IsItemChecked(item
)
2162 self
.AutoCheckParent(item
, ischeck
)
2163 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2164 self
.AutoToggleChild(item
)
2166 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2168 e
.SetEventObject(self
)
2169 self
.GetEventHandler().ProcessEvent(e
)
2172 def AutoToggleChild(self
, item
):
2173 """Transverses the tree and toggles the items. Meaningful only for check items."""
2176 raise "\nERROR: Invalid Tree Item. "
2178 child
, cookie
= self
.GetFirstChild(item
)
2181 if item
.IsExpanded():
2186 if child
.GetType() == 1 and child
.IsEnabled():
2187 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2188 self
.AutoToggleChild(child
)
2189 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2192 def AutoCheckChild(self
, item
, checked
):
2193 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2196 raise "\nERROR: Invalid Tree Item. "
2198 (child
, cookie
) = self
.GetFirstChild(item
)
2201 if item
.IsExpanded():
2205 if child
.GetType() == 1 and child
.IsEnabled():
2206 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2207 self
.AutoCheckChild(child
, checked
)
2208 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2211 def AutoCheckParent(self
, item
, checked
):
2212 """Traverses up the tree and checks/unchecks parent items.
2213 Meaningful only for check items."""
2216 raise "\nERROR: Invalid Tree Item. "
2218 parent
= item
.GetParent()
2219 if not parent
or parent
.GetType() != 1:
2222 (child
, cookie
) = self
.GetFirstChild(parent
)
2224 if child
.GetType() == 1 and child
.IsEnabled():
2225 if checked
!= child
.IsChecked():
2227 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2229 self
.CheckItem2(parent
, checked
, torefresh
=True)
2230 self
.AutoCheckParent(parent
, checked
)
2233 def CheckChilds(self
, item
, checked
=True):
2234 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2237 raise "\nERROR: Invalid Tree Item. "
2240 self
.AutoToggleChild(item
)
2242 self
.AutoCheckChild(item
, checked
)
2245 def CheckSameLevel(self
, item
, checked
=False):
2247 Uncheck radio items which are on the same level of the checked one.
2251 parent
= item
.GetParent()
2257 if parent
.IsExpanded():
2260 (child
, cookie
) = self
.GetFirstChild(parent
)
2262 if child
.GetType() == 2 and child
!= item
:
2263 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2264 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2265 self
.EnableChildren(child
, checked
)
2266 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2269 def EditLabel(self
, item
):
2270 """Starts editing an item label."""
2273 raise "\nERROR: Invalid Tree Item. "
2278 def ShouldInheritColours(self
):
2279 """We don't inherit colours from anyone."""
2284 def SetIndent(self
, indent
):
2285 """Sets item indentation."""
2287 self
._indent
= indent
2291 def SetSpacing(self
, spacing
):
2292 """Sets item spacing."""
2294 self
._spacing
= spacing
2298 def HasFlag(self
, flag
):
2299 """Returns whether CustomTreeCtrl has a flag."""
2301 return self
._windowStyle
& flag
2304 def HasChildren(self
, item
):
2305 """Returns whether an item has children or not."""
2308 raise "\nERROR: Invalid Tree Item. "
2310 return len(item
.GetChildren()) > 0
2313 def GetChildrenCount(self
, item
, recursively
=True):
2314 """Gets the item children count."""
2317 raise "\nERROR: Invalid Tree Item. "
2319 return item
.GetChildrenCount(recursively
)
2322 def SetTreeStyle(self
, styles
):
2323 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2325 # Do not try to expand the root node if it hasn't been created yet
2326 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2328 # if we will hide the root, make sure children are visible
2329 self
._anchor
.SetHasPlus()
2330 self
._anchor
.Expand()
2331 self
.CalculatePositions()
2333 # right now, just sets the styles. Eventually, we may
2334 # want to update the inherited styles, but right now
2335 # none of the parents has updatable styles
2337 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2338 selections
= self
.GetSelections()
2339 for select
in selections
[0:-1]:
2340 self
.SelectItem(select
, False)
2342 self
._windowStyle
= styles
2346 def GetTreeStyle(self
):
2347 """Returns the CustomTreeCtrl style."""
2349 return self
._windowStyle
2352 def HasButtons(self
):
2353 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2355 return self
.HasFlag(TR_HAS_BUTTONS
)
2358 # -----------------------------------------------------------------------------
2359 # functions to work with tree items
2360 # -----------------------------------------------------------------------------
2362 def GetItemText(self
, item
):
2363 """Returns the item text."""
2366 raise "\nERROR: Invalid Tree Item. "
2368 return item
.GetText()
2371 def GetItemImage(self
, item
, which
):
2372 """Returns the item image."""
2375 raise "\nERROR: Invalid Tree Item. "
2377 return item
.GetImage(which
)
2380 def GetPyData(self
, item
):
2381 """Returns the data associated to an item."""
2384 raise "\nERROR: Invalid Tree Item. "
2386 return item
.GetData()
2388 GetItemPyData
= GetPyData
2391 def GetItemTextColour(self
, item
):
2392 """Returns the item text colour."""
2395 raise "\nERROR: Invalid Tree Item. "
2397 return item
.Attr().GetTextColour()
2400 def GetItemBackgroundColour(self
, item
):
2401 """Returns the item background colour."""
2404 raise "\nERROR: Invalid Tree Item. "
2406 return item
.Attr().GetBackgroundColour()
2409 def GetItemFont(self
, item
):
2410 """Returns the item font."""
2413 raise "\nERROR: Invalid Tree Item. "
2415 return item
.Attr().GetFont()
2418 def IsItemHyperText(self
, item
):
2419 """Returns whether an item is hypertext or not."""
2422 raise "\nERROR: Invalid Tree Item. "
2424 return item
.IsHyperText()
2427 def SetItemText(self
, item
, text
):
2428 """Sets the item text."""
2431 raise "\nERROR: Invalid Tree Item. "
2433 dc
= wx
.ClientDC(self
)
2435 self
.CalculateSize(item
, dc
)
2436 self
.RefreshLine(item
)
2439 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2440 """Sets the item image, depending on the item state."""
2443 raise "\nERROR: Invalid Tree Item. "
2445 item
.SetImage(image
, which
)
2447 dc
= wx
.ClientDC(self
)
2448 self
.CalculateSize(item
, dc
)
2449 self
.RefreshLine(item
)
2452 def SetPyData(self
, item
, data
):
2453 """Sets the data associated to an item."""
2456 raise "\nERROR: Invalid Tree Item. "
2460 SetItemPyData
= SetPyData
2463 def SetItemHasChildren(self
, item
, has
=True):
2464 """Forces the appearance of the button next to the item."""
2467 raise "\nERROR: Invalid Tree Item. "
2469 item
.SetHasPlus(has
)
2470 self
.RefreshLine(item
)
2473 def SetItemBold(self
, item
, bold
=True):
2474 """Sets the item font bold/unbold."""
2477 raise "\nERROR: Invalid Tree Item. "
2479 # avoid redrawing the tree if no real change
2480 if item
.IsBold() != bold
:
2485 def SetItemItalic(self
, item
, italic
=True):
2486 """Sets the item font italic/non-italic."""
2489 raise "\nERROR: Invalid Tree Item. "
2491 if item
.IsItalic() != italic
:
2492 itemFont
= self
.GetItemFont(item
)
2493 if itemFont
!= wx
.NullFont
:
2498 item
.SetItalic(italic
)
2499 itemFont
.SetStyle(style
)
2500 self
.SetItemFont(item
, itemFont
)
2504 def SetItemDropHighlight(self
, item
, highlight
=True):
2506 Gives the item the visual feedback for drag and drop operations.
2507 This is useful when something is dragged from outside the CustomTreeCtrl.
2511 raise "\nERROR: Invalid Tree Item. "
2514 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2515 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2517 item
.Attr().SetTextColour(fg
)
2518 item
.Attr
.SetBackgroundColour(bg
)
2519 self
.RefreshLine(item
)
2522 def SetItemTextColour(self
, item
, col
):
2523 """Sets the item text colour."""
2526 raise "\nERROR: Invalid Tree Item. "
2528 if self
.GetItemTextColour(item
) == col
:
2531 item
.Attr().SetTextColour(col
)
2532 self
.RefreshLine(item
)
2535 def SetItemBackgroundColour(self
, item
, col
):
2536 """Sets the item background colour."""
2539 raise "\nERROR: Invalid Tree Item. "
2541 item
.Attr().SetBackgroundColour(col
)
2542 self
.RefreshLine(item
)
2545 def SetItemHyperText(self
, item
, hyper
=True):
2546 """Sets whether the item is hypertext or not."""
2549 raise "\nERROR: Invalid Tree Item. "
2551 item
.SetHyperText(hyper
)
2552 self
.RefreshLine(item
)
2555 def SetItemFont(self
, item
, font
):
2556 """Sets the item font."""
2559 raise "\nERROR: Invalid Tree Item. "
2561 if self
.GetItemFont(item
) == font
:
2564 item
.Attr().SetFont(font
)
2568 def SetFont(self
, font
):
2569 """Sets the CustomTreeCtrl font."""
2571 wx
.ScrolledWindow
.SetFont(self
, font
)
2573 self
._normalFont
= font
2574 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2575 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2576 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2581 def GetHyperTextFont(self
):
2582 """Returns the font used to render an hypertext item."""
2584 return self
._hypertextfont
2587 def SetHyperTextFont(self
, font
):
2588 """Sets the font used to render an hypertext item."""
2590 self
._hypertextfont
= font
2594 def SetHyperTextNewColour(self
, colour
):
2595 """Sets the colour used to render a non-visited hypertext item."""
2597 self
._hypertextnewcolour
= colour
2601 def GetHyperTextNewColour(self
):
2602 """Returns the colour used to render a non-visited hypertext item."""
2604 return self
._hypertextnewcolour
2607 def SetHyperTextVisitedColour(self
, colour
):
2608 """Sets the colour used to render a visited hypertext item."""
2610 self
._hypertextvisitedcolour
= colour
2614 def GetHyperTextVisitedColour(self
):
2615 """Returns the colour used to render a visited hypertext item."""
2617 return self
._hypertextvisitedcolour
2620 def SetItemVisited(self
, item
, visited
=True):
2621 """Sets whether an hypertext item was visited."""
2624 raise "\nERROR: Invalid Tree Item. "
2626 item
.SetVisited(visited
)
2627 self
.RefreshLine(item
)
2630 def GetItemVisited(self
, item
):
2631 """Returns whether an hypertext item was visited."""
2634 raise "\nERROR: Invalid Tree Item. "
2636 return item
.GetVisited()
2639 def SetHilightFocusColour(self
, colour
):
2641 Sets the colour used to highlight focused selected items.
2642 This is applied only if gradient and Windows Vista styles are disabled.
2645 self
._hilightBrush
= wx
.Brush(colour
)
2646 self
.RefreshSelected()
2649 def SetHilightNonFocusColour(self
, colour
):
2651 Sets the colour used to highlight unfocused selected items.
2652 This is applied only if gradient and Windows Vista styles are disabled.
2655 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2656 self
.RefreshSelected()
2659 def GetHilightFocusColour(self
):
2661 Returns the colour used to highlight focused selected items.
2662 This is applied only if gradient and Windows Vista styles are disabled.
2665 return self
._hilightBrush
.GetColour()
2668 def GetHilightNonFocusColour(self
):
2670 Returns the colour used to highlight unfocused selected items.
2671 This is applied only if gradient and Windows Vista styles are disabled.
2674 return self
._hilightUnfocusedBrush
.GetColour()
2677 def SetFirstGradientColour(self
, colour
=None):
2678 """Sets the first gradient colour."""
2681 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2683 self
._firstcolour
= colour
2684 if self
._usegradients
:
2685 self
.RefreshSelected()
2688 def SetSecondGradientColour(self
, colour
=None):
2689 """Sets the second gradient colour."""
2692 # No colour given, generate a slightly darker from the
2693 # CustomTreeCtrl background colour
2694 color
= self
.GetBackgroundColour()
2695 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2696 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2697 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2699 self
._secondcolour
= colour
2701 if self
._usegradients
:
2702 self
.RefreshSelected()
2705 def GetFirstGradientColour(self
):
2706 """Returns the first gradient colour."""
2708 return self
._firstcolour
2711 def GetSecondGradientColour(self
):
2712 """Returns the second gradient colour."""
2714 return self
._secondcolour
2717 def EnableSelectionGradient(self
, enable
=True):
2718 """Globally enables/disables drawing of gradient selection."""
2720 self
._usegradients
= enable
2721 self
._vistaselection
= False
2722 self
.RefreshSelected()
2725 def SetGradientStyle(self
, vertical
=0):
2727 Sets the gradient style:
2728 0: horizontal gradient
2729 1: vertical gradient
2732 # 0 = Horizontal, 1 = Vertical
2733 self
._gradientstyle
= vertical
2735 if self
._usegradients
:
2736 self
.RefreshSelected()
2739 def GetGradientStyle(self
):
2741 Returns the gradient style:
2742 0: horizontal gradient
2743 1: vertical gradient
2746 return self
._gradientstyle
2749 def EnableSelectionVista(self
, enable
=True):
2750 """Globally enables/disables drawing of Windows Vista selection."""
2752 self
._usegradients
= False
2753 self
._vistaselection
= enable
2754 self
.RefreshSelected()
2757 def SetBorderPen(self
, pen
):
2759 Sets the pen used to draw the selected item border.
2760 The border pen is not used if the Windows Vista style is applied.
2763 self
._borderPen
= pen
2764 self
.RefreshSelected()
2767 def GetBorderPen(self
):
2769 Returns the pen used to draw the selected item border.
2770 The border pen is not used if the Windows Vista style is applied.
2773 return self
._borderPen
2776 def SetConnectionPen(self
, pen
):
2777 """Sets the pen used to draw the connecting lines between items."""
2779 self
._dottedPen
= pen
2783 def GetConnectionPen(self
):
2784 """Returns the pen used to draw the connecting lines between items."""
2786 return self
._dottedPen
2789 def SetBackgroundImage(self
, image
):
2790 """Sets the CustomTreeCtrl background image (can be none)."""
2792 self
._backgroundImage
= image
2796 def GetBackgroundImage(self
):
2797 """Returns the CustomTreeCtrl background image (can be none)."""
2799 return self
._backgroundImage
2802 def GetItemWindow(self
, item
):
2803 """Returns the window associated to the item (if any)."""
2806 raise "\nERROR: Invalid Item"
2808 return item
.GetWindow()
2811 def GetItemWindowEnabled(self
, item
):
2812 """Returns whether the window associated to the item is enabled."""
2815 raise "\nERROR: Invalid Item"
2817 return item
.GetWindowEnabled()
2820 def SetItemWindowEnabled(self
, item
, enable
=True):
2821 """Enables/disables the window associated to the item."""
2824 raise "\nERROR: Invalid Item"
2826 item
.SetWindowEnabled(enable
)
2829 def GetItemType(self
, item
):
2831 Returns the item type:
2838 raise "\nERROR: Invalid Item"
2840 return item
.GetType()
2842 # -----------------------------------------------------------------------------
2843 # item status inquiries
2844 # -----------------------------------------------------------------------------
2846 def IsVisible(self
, item
):
2847 """Returns whether the item is visible or not."""
2850 raise "\nERROR: Invalid Tree Item. "
2852 # An item is only visible if it's not a descendant of a collapsed item
2853 parent
= item
.GetParent()
2857 if not parent
.IsExpanded():
2860 parent
= parent
.GetParent()
2862 startX
, startY
= self
.GetViewStart()
2863 clientSize
= self
.GetClientSize()
2865 rect
= self
.GetBoundingRect(item
)
2869 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2871 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2873 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2879 def ItemHasChildren(self
, item
):
2880 """Returns whether the item has children or not."""
2883 raise "\nERROR: Invalid Tree Item. "
2885 # consider that the item does have children if it has the "+" button: it
2886 # might not have them (if it had never been expanded yet) but then it
2887 # could have them as well and it's better to err on this side rather than
2888 # disabling some operations which are restricted to the items with
2889 # children for an item which does have them
2890 return item
.HasPlus()
2893 def IsExpanded(self
, item
):
2894 """Returns whether the item is expanded or not."""
2897 raise "\nERROR: Invalid Tree Item. "
2899 return item
.IsExpanded()
2902 def IsSelected(self
, item
):
2903 """Returns whether the item is selected or not."""
2906 raise "\nERROR: Invalid Tree Item. "
2908 return item
.IsSelected()
2911 def IsBold(self
, item
):
2912 """Returns whether the item font is bold or not."""
2915 raise "\nERROR: Invalid Tree Item. "
2917 return item
.IsBold()
2920 def IsItalic(self
, item
):
2921 """Returns whether the item font is italic or not."""
2924 raise "\nERROR: Invalid Tree Item. "
2926 return item
.IsItalic()
2929 # -----------------------------------------------------------------------------
2931 # -----------------------------------------------------------------------------
2933 def GetItemParent(self
, item
):
2934 """Gets the item parent."""
2937 raise "\nERROR: Invalid Tree Item. "
2939 return item
.GetParent()
2942 def GetFirstChild(self
, item
):
2943 """Gets the item first child."""
2946 raise "\nERROR: Invalid Tree Item. "
2949 return self
.GetNextChild(item
, cookie
)
2952 def GetNextChild(self
, item
, cookie
):
2954 Gets the item next child based on the 'cookie' parameter.
2955 This method has no sense if you do not call GetFirstChild() before.
2959 raise "\nERROR: Invalid Tree Item. "
2961 children
= item
.GetChildren()
2963 # it's ok to cast cookie to size_t, we never have indices big enough to
2966 if cookie
< len(children
):
2968 return children
[cookie
], cookie
+1
2972 # there are no more of them
2976 def GetLastChild(self
, item
):
2977 """Gets the item last child."""
2980 raise "\nERROR: Invalid Tree Item. "
2982 children
= item
.GetChildren()
2983 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2986 def GetNextSibling(self
, item
):
2987 """Gets the next sibling of an item."""
2990 raise "\nERROR: Invalid Tree Item. "
2993 parent
= i
.GetParent()
2997 # root item doesn't have any siblings
3000 siblings
= parent
.GetChildren()
3001 index
= siblings
.index(i
)
3004 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
3007 def GetPrevSibling(self
, item
):
3008 """Gets the previous sibling of an item."""
3011 raise "\nERROR: Invalid Tree Item. "
3014 parent
= i
.GetParent()
3018 # root item doesn't have any siblings
3021 siblings
= parent
.GetChildren()
3022 index
= siblings
.index(i
)
3024 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3027 def GetNext(self
, item
):
3028 """Gets the next item. Only for internal use right now."""
3031 raise "\nERROR: Invalid Tree Item. "
3035 # First see if there are any children.
3036 children
= i
.GetChildren()
3037 if len(children
) > 0:
3040 # Try a sibling of this or ancestor instead
3043 while p
and not toFind
:
3044 toFind
= self
.GetNextSibling(p
)
3045 p
= self
.GetItemParent(p
)
3050 def GetFirstVisibleItem(self
):
3051 """Returns the first visible item."""
3053 id = self
.GetRootItem()
3058 if self
.IsVisible(id):
3060 id = self
.GetNext(id)
3065 def GetNextVisible(self
, item
):
3066 """Returns the next visible item."""
3069 raise "\nERROR: Invalid Tree Item. "
3074 id = self
.GetNext(id)
3075 if id and self
.IsVisible(id):
3081 def GetPrevVisible(self
, item
):
3084 raise "\nERROR: Invalid Tree Item. "
3086 raise "\nERROR: Not Implemented"
3091 def ResetTextControl(self
):
3092 """Called by TreeTextCtrl when it marks itself for deletion."""
3094 self
._textCtrl
.Destroy()
3095 self
._textCtrl
= None
3098 def FindItem(self
, idParent
, prefixOrig
):
3099 """Finds the first item starting with the given prefix after the given item."""
3101 # match is case insensitive as this is more convenient to the user: having
3102 # to press Shift-letter to go to the item starting with a capital letter
3103 # would be too bothersome
3104 prefix
= prefixOrig
.lower()
3106 # determine the starting point: we shouldn't take the current item (this
3107 # allows to switch between two items starting with the same letter just by
3108 # pressing it) but we shouldn't jump to the next one if the user is
3109 # continuing to type as otherwise he might easily skip the item he wanted
3112 if len(prefix
) == 1:
3113 id = self
.GetNext(id)
3115 # look for the item starting with the given prefix after it
3116 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3118 id = self
.GetNext(id)
3120 # if we haven't found anything...
3123 # ... wrap to the beginning
3124 id = self
.GetRootItem()
3125 if self
.HasFlag(TR_HIDE_ROOT
):
3126 # can't select virtual root
3127 id = self
.GetNext(id)
3129 # and try all the items (stop when we get to the one we started from)
3130 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3131 id = self
.GetNext(id)
3136 # -----------------------------------------------------------------------------
3138 # -----------------------------------------------------------------------------
3140 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3141 """Actually inserts an item in the tree."""
3143 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3144 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3146 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3147 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3149 if ct_type
< 0 or ct_type
> 2:
3150 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3156 # should we give a warning here?
3157 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3159 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3161 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3164 self
._hasWindows
= True
3165 self
._itemWithWindow
.append(item
)
3167 parent
.Insert(item
, previous
)
3172 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3173 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3176 raise "\nERROR: Tree Can Have Only One Root"
3178 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3179 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3181 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3182 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3184 if ct_type
< 0 or ct_type
> 2:
3185 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3187 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3189 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3192 self
._hasWindows
= True
3193 self
._itemWithWindow
.append(self
._anchor
)
3195 if self
.HasFlag(TR_HIDE_ROOT
):
3197 # if root is hidden, make sure we can navigate
3199 self
._anchor
.SetHasPlus()
3200 self
._anchor
.Expand()
3201 self
.CalculatePositions()
3203 if not self
.HasFlag(TR_MULTIPLE
):
3205 self
._current
= self
._key
_current
= self
._anchor
3206 self
._current
.SetHilight(True)
3211 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3212 """Appends an item as a first child of parent."""
3214 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3215 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3217 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3218 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3220 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3223 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3224 """Auxiliary function to cope with the C++ hideous multifunction."""
3226 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3227 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3229 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3230 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3235 # should we give a warning here?
3236 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3242 index
= parent
.GetChildren().index(idPrevious
)
3244 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3246 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3249 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3250 """Auxiliary function to cope with the C++ hideous multifunction."""
3252 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3253 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3255 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3256 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3261 # should we give a warning here?
3262 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3264 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3267 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3268 """Inserts an item after the given previous."""
3270 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3271 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3273 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3274 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3276 if type(input) == type(1):
3277 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3279 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3282 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3283 """Appends an item as a last child of its parent."""
3285 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3286 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3288 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3289 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3294 # should we give a warning here?
3295 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3297 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3300 def SendDeleteEvent(self
, item
):
3301 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3303 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3305 event
.SetEventObject(self
)
3306 self
.ProcessEvent(event
)
3309 def IsDescendantOf(self
, parent
, item
):
3310 """Checks if the given item is under another one."""
3316 # item is a descendant of parent
3319 item
= item
.GetParent()
3324 # Don't leave edit or selection on a child which is about to disappear
3325 def ChildrenClosing(self
, item
):
3326 """We are about to destroy the item children."""
3328 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3329 self
._textCtrl
.StopEditing()
3331 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3332 self
._key
_current
= None
3334 if self
.IsDescendantOf(item
, self
._select
_me
):
3335 self
._select
_me
= item
3337 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3338 self
._current
.SetHilight(False)
3339 self
._current
= None
3340 self
._select
_me
= item
3343 def DeleteChildren(self
, item
):
3344 """Delete item children."""
3347 raise "\nERROR: Invalid Tree Item. "
3349 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3351 self
.ChildrenClosing(item
)
3352 item
.DeleteChildren(self
)
3355 def Delete(self
, item
):
3356 """Delete an item."""
3359 raise "\nERROR: Invalid Tree Item. "
3361 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3363 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3364 # can't delete the item being edited, cancel editing it first
3365 self
._textCtrl
.StopEditing()
3367 parent
= item
.GetParent()
3369 # don't keep stale pointers around!
3370 if self
.IsDescendantOf(item
, self
._key
_current
):
3372 # Don't silently change the selection:
3373 # do it properly in idle time, so event
3374 # handlers get called.
3376 # self._key_current = parent
3377 self
._key
_current
= None
3379 # self._select_me records whether we need to select
3380 # a different item, in idle time.
3381 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3382 self
._select
_me
= parent
3384 if self
.IsDescendantOf(item
, self
._current
):
3386 # Don't silently change the selection:
3387 # do it properly in idle time, so event
3388 # handlers get called.
3390 # self._current = parent
3391 self
._current
= None
3392 self
._select
_me
= parent
3394 # remove the item from the tree
3397 parent
.GetChildren().remove(item
) # remove by value
3399 else: # deleting the root
3401 # nothing will be left in the tree
3404 # and delete all of its children and the item itself now
3405 item
.DeleteChildren(self
)
3406 self
.SendDeleteEvent(item
)
3408 if item
== self
._select
_me
:
3409 self
._select
_me
= None
3411 # Remove the item with window
3412 if item
in self
._itemWithWindow
:
3413 wnd
= item
.GetWindow()
3417 self
._itemWithWindow
.remove(item
)
3422 def DeleteAllItems(self
):
3423 """Delete all items in the CustomTreeCtrl."""
3426 self
.Delete(self
._anchor
)
3429 def Expand(self
, item
):
3431 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3432 EVT_TREE_ITEM_EXPANDED events.
3436 raise "\nERROR: Invalid Tree Item. "
3438 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3439 raise "\nERROR: Can't Expand An Hidden Root. "
3441 if not item
.HasPlus():
3444 if item
.IsExpanded():
3447 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3449 event
.SetEventObject(self
)
3451 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3452 # cancelled by program
3456 self
.CalculatePositions()
3458 self
.RefreshSubtree(item
)
3460 if self
._hasWindows
:
3461 # We hide the associated window here, we may show it after
3464 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3465 self
.ProcessEvent(event
)
3468 def ExpandAll(self
, item
):
3469 """Expands all the items."""
3472 raise "\nERROR: Invalid Tree Item. "
3474 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3476 if not self
.IsExpanded(item
):
3479 child
, cookie
= self
.GetFirstChild(item
)
3482 self
.ExpandAll(child
)
3483 child
, cookie
= self
.GetNextChild(item
, cookie
)
3486 def Collapse(self
, item
):
3488 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3489 EVT_TREE_ITEM_COLLAPSED events.
3493 raise "\nERROR: Invalid Tree Item. "
3495 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3496 raise "\nERROR: Can't Collapse An Hidden Root. "
3498 if not item
.IsExpanded():
3501 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3503 event
.SetEventObject(self
)
3504 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3505 # cancelled by program
3508 self
.ChildrenClosing(item
)
3511 self
.CalculatePositions()
3512 self
.RefreshSubtree(item
)
3514 if self
._hasWindows
:
3517 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3518 self
.ProcessEvent(event
)
3521 def CollapseAndReset(self
, item
):
3522 """Collapse the given item and deletes its children."""
3525 self
.DeleteChildren(item
)
3528 def Toggle(self
, item
):
3529 """Toggles the item state (collapsed/expanded)."""
3531 if item
.IsExpanded():
3537 def HideWindows(self
):
3538 """Hides the windows associated to the items. Used internally."""
3540 for child
in self
._itemWithWindow
:
3541 if not self
.IsVisible(child
):
3542 wnd
= child
.GetWindow()
3547 """Unselects the current selection."""
3551 self
._current
.SetHilight(False)
3552 self
.RefreshLine(self
._current
)
3554 self
._current
= None
3555 self
._select
_me
= None
3558 def UnselectAllChildren(self
, item
):
3559 """Unselects all the children of the given item."""
3561 if item
.IsSelected():
3563 item
.SetHilight(False)
3564 self
.RefreshLine(item
)
3566 if item
.HasChildren():
3567 for child
in item
.GetChildren():
3568 self
.UnselectAllChildren(child
)
3571 def UnselectAll(self
):
3572 """Unselect all the items."""
3574 rootItem
= self
.GetRootItem()
3576 # the tree might not have the root item at all
3578 self
.UnselectAllChildren(rootItem
)
3581 # Recursive function !
3582 # To stop we must have crt_item<last_item
3584 # Tag all next children, when no more children,
3585 # Move to parent (not to tag)
3586 # Keep going... if we found last_item, we stop.
3588 def TagNextChildren(self
, crt_item
, last_item
, select
):
3589 """Used internally."""
3591 parent
= crt_item
.GetParent()
3593 if parent
== None: # This is root item
3594 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3596 children
= parent
.GetChildren()
3597 index
= children
.index(crt_item
)
3599 count
= len(children
)
3601 for n
in xrange(index
+1, count
):
3602 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3605 return self
.TagNextChildren(parent
, last_item
, select
)
3608 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3609 """Used internally."""
3611 crt_item
.SetHilight(select
)
3612 self
.RefreshLine(crt_item
)
3614 if crt_item
== last_item
:
3617 if crt_item
.HasChildren():
3618 for child
in crt_item
.GetChildren():
3619 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3625 def SelectItemRange(self
, item1
, item2
):
3626 """Selects all the items between item1 and item2."""
3628 self
._select
_me
= None
3630 # item2 is not necessary after item1
3631 # choice first' and 'last' between item1 and item2
3632 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3633 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3635 select
= self
._current
.IsSelected()
3637 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3640 self
.TagNextChildren(first
, last
, select
)
3643 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3644 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3647 raise "\nERROR: Invalid Tree Item. "
3649 self
._select
_me
= None
3651 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3653 # to keep going anyhow !!!
3655 if item
.IsSelected():
3656 return # nothing to do
3657 unselect_others
= True
3658 extended_select
= False
3660 elif unselect_others
and item
.IsSelected():
3662 # selection change if there is more than one item currently selected
3663 if len(self
.GetSelections()) == 1:
3666 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3668 event
._itemOld
= self
._current
3669 event
.SetEventObject(self
)
3670 # TODO : Here we don't send any selection mode yet !
3672 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3675 parent
= self
.GetItemParent(item
)
3677 if not self
.IsExpanded(parent
):
3680 parent
= self
.GetItemParent(parent
)
3685 self
.Unselect() # to speed up thing
3691 if not self
._current
:
3692 self
._current
= self
._key
_current
= self
.GetRootItem()
3694 # don't change the mark (self._current)
3695 self
.SelectItemRange(self
._current
, item
)
3699 select
= True # the default
3701 # Check if we need to toggle hilight (ctrl mode)
3702 if not unselect_others
:
3703 select
= not item
.IsSelected()
3705 self
._current
= self
._key
_current
= item
3706 self
._current
.SetHilight(select
)
3707 self
.RefreshLine(self
._current
)
3709 # This can cause idle processing to select the root
3710 # if no item is selected, so it must be after the
3712 self
.EnsureVisible(item
)
3714 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3715 self
.GetEventHandler().ProcessEvent(event
)
3717 # Handles hypertext items
3718 if self
.IsItemHyperText(item
):
3719 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3721 self
.GetEventHandler().ProcessEvent(event
)
3724 def SelectItem(self
, item
, select
=True):
3725 """Selects/deselects an item."""
3728 raise "\nERROR: Invalid Tree Item. "
3732 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3736 item
.SetHilight(False)
3737 self
.RefreshLine(item
)
3740 def FillArray(self
, item
, array
=[]):
3742 Internal function. Used to populate an array of selected items when
3743 the style TR_MULTIPLE is used.
3749 if item
.IsSelected():
3752 if item
.HasChildren():
3753 for child
in item
.GetChildren():
3754 array
= self
.FillArray(child
, array
)
3759 def GetSelections(self
):
3761 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3762 the TR_MULTIPLE style set.
3766 idRoot
= self
.GetRootItem()
3768 array
= self
.FillArray(idRoot
, array
)
3770 #else: the tree is empty, so no selections
3775 def EnsureVisible(self
, item
):
3776 """Ensure that an item is visible in CustomTreeCtrl."""
3779 raise "\nERROR: Invalid Tree Item. "
3781 # first expand all parent branches
3782 parent
= item
.GetParent()
3784 if self
.HasFlag(TR_HIDE_ROOT
):
3785 while parent
and parent
!= self
._anchor
:
3787 parent
= parent
.GetParent()
3791 parent
= parent
.GetParent()
3796 def ScrollTo(self
, item
):
3797 """Scrolls the specified item into view."""
3802 # We have to call this here because the label in
3803 # question might just have been added and no screen
3804 # update taken place.
3806 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3811 # now scroll to the item
3812 item_y
= item
.GetY()
3813 start_x
, start_y
= self
.GetViewStart()
3814 start_y
*= _PIXELS_PER_UNIT
3816 client_w
, client_h
= self
.GetClientSize()
3820 if item_y
< start_y
+3:
3823 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3824 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3825 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3826 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3827 # Item should appear at top
3828 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3830 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3833 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3834 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3835 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3836 item_y
+= _PIXELS_PER_UNIT
+2
3837 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3838 # Item should appear at bottom
3839 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
)
3842 def OnCompareItems(self
, item1
, item2
):
3844 Returns whether 2 items have the same text.
3845 Override this function in the derived class to change the sort order of the items
3846 in the CustomTreeCtrl. The function should return a negative, zero or positive
3847 value if the first item is less than, equal to or greater than the second one.
3849 The base class version compares items alphabetically.
3852 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3855 def SortChildren(self
, item
):
3857 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3858 You should override that method to change the sort order (the default is ascending
3859 case-sensitive alphabetical order).
3863 raise "\nERROR: Invalid Tree Item. "
3865 children
= item
.GetChildren()
3867 if len(children
) > 1:
3869 children
.sort(self
.OnCompareItems
)
3872 def GetImageList(self
):
3873 """Returns the normal image list."""
3875 return self
._imageListNormal
3878 def GetButtonsImageList(self
):
3879 """Returns the buttons image list (from which application-defined button images are taken)."""
3881 return self
._imageListButtons
3884 def GetStateImageList(self
):
3885 """Returns the state image list (from which application-defined state images are taken)."""
3887 return self
._imageListState
3890 def GetImageListCheck(self
):
3891 """Returns the image list used to build the check/radio buttons."""
3893 return self
._imageListCheck
3896 def CalculateLineHeight(self
):
3897 """Calculates the height of a line."""
3899 dc
= wx
.ClientDC(self
)
3900 self
._lineHeight
= dc
.GetCharHeight()
3902 if self
._imageListNormal
:
3904 # Calculate a self._lineHeight value from the normal Image sizes.
3905 # May be toggle off. Then CustomTreeCtrl will spread when
3906 # necessary (which might look ugly).
3907 n
= self
._imageListNormal
.GetImageCount()
3911 width
, height
= self
._imageListNormal
.GetSize(i
)
3913 if height
> self
._lineHeight
:
3914 self
._lineHeight
= height
3916 if self
._imageListButtons
:
3918 # Calculate a self._lineHeight value from the Button image sizes.
3919 # May be toggle off. Then CustomTreeCtrl will spread when
3920 # necessary (which might look ugly).
3921 n
= self
._imageListButtons
.GetImageCount()
3925 width
, height
= self
._imageListButtons
.GetSize(i
)
3927 if height
> self
._lineHeight
:
3928 self
._lineHeight
= height
3930 if self
._imageListCheck
:
3932 # Calculate a self._lineHeight value from the check/radio image sizes.
3933 # May be toggle off. Then CustomTreeCtrl will spread when
3934 # necessary (which might look ugly).
3935 n
= self
._imageListCheck
.GetImageCount()
3939 width
, height
= self
._imageListCheck
.GetSize(i
)
3941 if height
> self
._lineHeight
:
3942 self
._lineHeight
= height
3944 if self
._lineHeight
< 30:
3945 self
._lineHeight
+= 2 # at least 2 pixels
3947 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3950 def SetImageList(self
, imageList
):
3951 """Sets the normal image list."""
3953 if self
._ownsImageListNormal
:
3954 del self
._imageListNormal
3956 self
._imageListNormal
= imageList
3957 self
._ownsImageListNormal
= False
3959 # Don't do any drawing if we're setting the list to NULL,
3960 # since we may be in the process of deleting the tree control.
3962 self
.CalculateLineHeight()
3964 # We gray out the image list to use the grayed icons with disabled items
3965 self
._grayedImageList
= wx
.ImageList(16, 16, True, 0)
3967 for ii
in xrange(imageList
.GetImageCount()):
3969 bmp
= imageList
.GetBitmap(ii
)
3970 image
= wx
.ImageFromBitmap(bmp
)
3971 image
= GrayOut(image
)
3972 newbmp
= wx
.BitmapFromImage(image
)
3973 self
._grayedImageList
.Add(newbmp
)
3976 def SetStateImageList(self
, imageList
):
3977 """Sets the state image list (from which application-defined state images are taken)."""
3979 if self
._ownsImageListState
:
3980 del self
._imageListState
3982 self
._imageListState
= imageList
3983 self
._ownsImageListState
= False
3986 def SetButtonsImageList(self
, imageList
):
3987 """Sets the buttons image list (from which application-defined button images are taken)."""
3989 if self
._ownsImageListButtons
:
3990 del self
._imageListButtons
3992 self
._imageListButtons
= imageList
3993 self
._ownsImageListButtons
= False
3995 self
.CalculateLineHeight()
3998 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
3999 """Sets the check image list."""
4003 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
4004 self
._imageListCheck
.Add(GetCheckedBitmap())
4005 self
._imageListCheck
.Add(GetNotCheckedBitmap())
4006 self
._imageListCheck
.Add(GetFlaggedBitmap())
4007 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
4011 sizex
, sizey
= imglist
.GetSize(0)
4012 self
._imageListCheck
= imglist
4014 # We gray out the image list to use the grayed icons with disabled items
4015 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
4017 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
4019 bmp
= self
._imageListCheck
.GetBitmap(ii
)
4020 image
= wx
.ImageFromBitmap(bmp
)
4021 image
= GrayOut(image
)
4022 newbmp
= wx
.BitmapFromImage(image
)
4023 self
._grayedCheckList
.Add(newbmp
)
4028 self
.CalculateLineHeight()
4031 def AssignImageList(self
, imageList
):
4032 """Assigns the normal image list."""
4034 self
.SetImageList(imageList
)
4035 self
._ownsImageListNormal
= True
4038 def AssignStateImageList(self
, imageList
):
4039 """Assigns the state image list."""
4041 self
.SetStateImageList(imageList
)
4042 self
._ownsImageListState
= True
4045 def AssignButtonsImageList(self
, imageList
):
4046 """Assigns the button image list."""
4048 self
.SetButtonsImageList(imageList
)
4049 self
._ownsImageListButtons
= True
4052 # -----------------------------------------------------------------------------
4054 # -----------------------------------------------------------------------------
4056 def AdjustMyScrollbars(self
):
4057 """Adjust the wx.ScrolledWindow scrollbars."""
4061 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4062 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4063 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4064 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4065 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4066 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4070 self
.SetScrollbars(0, 0, 0, 0)
4073 def GetLineHeight(self
, item
):
4074 """Returns the line height for the given item."""
4076 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4077 return item
.GetHeight()
4079 return self
._lineHeight
4082 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4083 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4085 oldpen
= dc
.GetPen()
4086 oldbrush
= dc
.GetBrush()
4087 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4089 # calculate gradient coefficients
4091 col2
= self
._secondcolour
4092 col1
= self
._firstcolour
4094 col2
= self
._hilightUnfocusedBrush
.GetColour()
4095 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4097 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4098 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4100 flrect
= float(rect
.height
)
4102 rstep
= float((r2
- r1
)) / flrect
4103 gstep
= float((g2
- g1
)) / flrect
4104 bstep
= float((b2
- b1
)) / flrect
4106 rf
, gf
, bf
= 0, 0, 0
4108 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4109 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4110 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4111 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4117 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4118 dc
.DrawRectangleRect(rect
)
4119 dc
.SetBrush(oldbrush
)
4122 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4123 """Gradient fill from colour 1 to colour 2 from left to right."""
4125 oldpen
= dc
.GetPen()
4126 oldbrush
= dc
.GetBrush()
4127 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4129 # calculate gradient coefficients
4132 col2
= self
._secondcolour
4133 col1
= self
._firstcolour
4135 col2
= self
._hilightUnfocusedBrush
.GetColour()
4136 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4138 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4139 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4141 flrect
= float(rect
.width
)
4143 rstep
= float((r2
- r1
)) / flrect
4144 gstep
= float((g2
- g1
)) / flrect
4145 bstep
= float((b2
- b1
)) / flrect
4147 rf
, gf
, bf
= 0, 0, 0
4149 for x
in xrange(rect
.x
, rect
.x
+ rect
.width
):
4150 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4151 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4152 dc
.DrawRectangle(x
, rect
.y
, 1, rect
.height
)
4158 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4159 dc
.DrawRectangleRect(rect
)
4160 dc
.SetBrush(oldbrush
)
4163 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4164 """Draw the selected item(s) with the Windows Vista style."""
4168 outer
= _rgbSelectOuter
4169 inner
= _rgbSelectInner
4171 bottom
= _rgbSelectBottom
4175 outer
= _rgbNoFocusOuter
4176 inner
= _rgbNoFocusInner
4177 top
= _rgbNoFocusTop
4178 bottom
= _rgbNoFocusBottom
4180 oldpen
= dc
.GetPen()
4181 oldbrush
= dc
.GetBrush()
4183 bdrRect
= wx
.Rect(*rect
.Get())
4184 filRect
= wx
.Rect(*rect
.Get())
4185 filRect
.Deflate(1,1)
4187 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4188 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4190 flrect
= float(filRect
.height
)
4192 rstep
= float((r2
- r1
)) / flrect
4193 gstep
= float((g2
- g1
)) / flrect
4194 bstep
= float((b2
- b1
)) / flrect
4196 rf
, gf
, bf
= 0, 0, 0
4197 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4199 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4200 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4201 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4202 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4207 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4208 dc
.SetPen(wx
.Pen(outer
))
4209 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4210 bdrRect
.Deflate(1, 1)
4211 dc
.SetPen(wx
.Pen(inner
))
4212 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4215 dc
.SetBrush(oldbrush
)
4218 def PaintItem(self
, item
, dc
):
4219 """Actually paint an item."""
4221 attr
= item
.GetAttributes()
4223 if attr
and attr
.HasFont():
4224 dc
.SetFont(attr
.GetFont())
4226 dc
.SetFont(self
._boldFont
)
4227 if item
.IsHyperText():
4228 dc
.SetFont(self
.GetHyperTextFont())
4229 if item
.GetVisited():
4230 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4232 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4234 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4236 image
= item
.GetCurrentImage()
4237 checkimage
= item
.GetCurrentCheckedImage()
4238 image_w
, image_h
= 0, 0
4240 if image
!= _NO_IMAGE
:
4242 if self
._imageListNormal
:
4244 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4251 if item
.GetType() != 0:
4252 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4255 wcheck
, hcheck
= 0, 0
4257 total_h
= self
.GetLineHeight(item
)
4258 drawItemBackground
= False
4260 if item
.IsSelected():
4262 # under mac selections are only a rectangle in case they don't have the focus
4263 if wx
.Platform
== "__WXMAC__":
4264 if not self
._hasFocus
:
4265 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4266 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4268 dc
.SetBrush(self
._hilightBrush
)
4270 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4271 drawItemBackground
= True
4273 if attr
and attr
.HasBackgroundColour():
4274 drawItemBackground
= True
4275 colBg
= attr
.GetBackgroundColour()
4277 colBg
= self
._backgroundColour
4279 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4280 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4282 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4284 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4286 w
, h
= self
.GetClientSize()
4288 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4290 if item
.IsSelected():
4291 if self
._usegradients
:
4292 if self
._gradientstyle
== 0: # Horizontal
4293 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4295 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4296 elif self
._vistaselection
:
4297 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4299 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4300 flags
= wx
.CONTROL_SELECTED
4301 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4302 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4304 dc
.DrawRectangleRect(itemrect
)
4308 if item
.IsSelected():
4310 # If it's selected, and there's an image, then we should
4311 # take care to leave the area under the image painted in the
4312 # background colour.
4314 wnd
= item
.GetWindow()
4317 wndx
, wndy
= item
.GetWindowSize()
4319 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4321 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4324 if self
._usegradients
:
4325 if self
._gradientstyle
== 0: # Horizontal
4326 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4328 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4329 elif self
._vistaselection
:
4330 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4332 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4333 flags
= wx
.CONTROL_SELECTED
4334 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4335 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4337 dc
.DrawRectangleRect(itemrect
)
4339 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4340 # don't allow backgrounds to be customized. Not drawing the background,
4341 # except for custom item backgrounds, works for both kinds of theme.
4342 elif drawItemBackground
:
4344 minusicon
= wcheck
+ image_w
- 2
4345 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4347 item
.GetWidth()-minusicon
,
4350 if self
._usegradients
and self
._hasFocus
:
4351 if self
._gradientstyle
== 0: # Horizontal
4352 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4354 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4356 dc
.DrawRectangleRect(itemrect
)
4358 if image
!= _NO_IMAGE
:
4360 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4361 if item
.IsEnabled():
4362 imglist
= self
._imageListNormal
4364 imglist
= self
._grayedImageList
4366 imglist
.Draw(image
, dc
,
4367 item
.GetX() + wcheck
,
4368 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4369 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4371 dc
.DestroyClippingRegion()
4374 if item
.IsEnabled():
4375 imglist
= self
._imageListCheck
4377 imglist
= self
._grayedCheckList
4379 imglist
.Draw(checkimage
, dc
,
4381 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4382 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4384 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4385 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4387 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4389 if not item
.IsEnabled():
4390 foreground
= dc
.GetTextForeground()
4391 dc
.SetTextForeground(self
._disabledColour
)
4392 dc
.DrawLabel(item
.GetText(), textrect
)
4393 dc
.SetTextForeground(foreground
)
4395 if wx
.Platform
== "__WXMAC__" and item
.IsSelected() and self
._hasFocus
:
4396 dc
.SetTextForeground(wx
.WHITE
)
4397 dc
.DrawLabel(item
.GetText(), textrect
)
4399 wnd
= item
.GetWindow()
4401 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4402 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4403 if not wnd
.IsShown():
4405 if wnd
.GetPosition() != (wndx
, ya
):
4406 wnd
.SetPosition((wndx
, ya
))
4408 # restore normal font
4409 dc
.SetFont(self
._normalFont
)
4412 # Now y stands for the top of the item, whereas it used to stand for middle !
4413 def PaintLevel(self
, item
, dc
, level
, y
):
4414 """Paint a level of CustomTreeCtrl."""
4416 x
= level
*self
._indent
4418 if not self
.HasFlag(TR_HIDE_ROOT
):
4424 # always expand hidden root
4426 children
= item
.GetChildren()
4427 count
= len(children
)
4433 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4436 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4438 # draw line down to last child
4439 origY
+= self
.GetLineHeight(children
[0])>>1
4440 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4441 dc
.DrawLine(3, origY
, 3, oldY
)
4445 item
.SetX(x
+self
._spacing
)
4448 h
= self
.GetLineHeight(item
)
4450 y_mid
= y_top
+ (h
>>1)
4453 exposed_x
= dc
.LogicalToDeviceX(0)
4454 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4456 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4457 if wx
.Platform
== "__WXMAC__":
4458 # don't draw rect outline if we already have the
4459 # background color under Mac
4460 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4462 pen
= self
._borderPen
4464 if item
.IsSelected():
4465 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4466 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4468 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4470 attr
= item
.GetAttributes()
4471 if attr
and attr
.HasTextColour():
4472 colText
= attr
.GetTextColour()
4474 colText
= self
.GetForegroundColour()
4476 if self
._vistaselection
:
4480 dc
.SetTextForeground(colText
)
4485 self
.PaintItem(item
, dc
)
4487 if self
.HasFlag(TR_ROW_LINES
):
4489 # if the background colour is white, choose a
4490 # contrasting color for the lines
4491 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4492 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4493 dc
.DrawLine(0, y_top
, 10000, y_top
)
4494 dc
.DrawLine(0, y
, 10000, y
)
4496 # restore DC objects
4497 dc
.SetBrush(wx
.WHITE_BRUSH
)
4498 dc
.SetTextForeground(wx
.BLACK
)
4500 if not self
.HasFlag(TR_NO_LINES
):
4502 # draw the horizontal line here
4503 dc
.SetPen(self
._dottedPen
)
4505 if x
> self
._indent
:
4506 x_start
-= self
._indent
4507 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4509 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4512 # should the item show a button?
4513 if item
.HasPlus() and self
.HasButtons():
4515 if self
._imageListButtons
:
4517 # draw the image button here
4520 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4521 if item
.IsSelected():
4522 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4524 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4526 yy
= y_mid
- image_h
/2
4528 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4529 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4530 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4531 dc
.DestroyClippingRegion()
4533 else: # no custom buttons
4535 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4536 # We draw something like the Mac twist buttons
4538 dc
.SetPen(wx
.BLACK_PEN
)
4539 dc
.SetBrush(self
._hilightBrush
)
4540 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4542 if item
.IsExpanded():
4544 button
[0].y
= y_mid
- 3
4546 button
[1].y
= button
[0].y
4548 button
[2].y
= button
[0].y
+ 6
4551 button
[0].y
= y_mid
- 5
4552 button
[1].x
= button
[0].x
4553 button
[1].y
= y_mid
+ 5
4554 button
[2].x
= button
[0].x
+ 5
4557 dc
.DrawPolygon(button
)
4560 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4567 if item
.IsExpanded():
4568 flag |
= _CONTROL_EXPANDED
4569 if item
== self
._underMouse
:
4570 flag |
= _CONTROL_CURRENT
4572 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4574 if item
.IsExpanded():
4576 children
= item
.GetChildren()
4577 count
= len(children
)
4586 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4589 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4591 # draw line down to last child
4592 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4593 if self
.HasButtons():
4596 # Only draw the portion of the line that is visible, in case it is huge
4597 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4598 yOrigin
= abs(yOrigin
)
4599 width
, height
= self
.GetClientSize()
4601 # Move end points to the begining/end of the view?
4604 if oldY
> yOrigin
+ height
:
4605 oldY
= yOrigin
+ height
4607 # after the adjustments if y_mid is larger than oldY then the line
4608 # isn't visible at all so don't draw anything
4610 dc
.SetPen(self
._dottedPen
)
4611 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4616 # -----------------------------------------------------------------------------
4617 # wxWidgets callbacks
4618 # -----------------------------------------------------------------------------
4620 def OnPaint(self
, event
):
4621 """Handles the wx.EVT_PAINT event."""
4623 dc
= wx
.PaintDC(self
)
4626 if not self
._anchor
:
4629 dc
.SetFont(self
._normalFont
)
4630 dc
.SetPen(self
._dottedPen
)
4633 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4636 def OnEraseBackground(self
, event
):
4637 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4639 # Can we actually do something here (or in OnPaint()) To Handle
4640 # background images that are stretchable or always centered?
4641 # I tried but I get enormous flickering...
4643 if not self
._backgroundImage
:
4647 if self
._imageStretchStyle
== _StyleTile
:
4651 dc
= wx
.ClientDC(self
)
4652 rect
= self
.GetUpdateRegion().GetBox()
4653 dc
.SetClippingRect(rect
)
4655 self
.TileBackground(dc
)
4658 def TileBackground(self
, dc
):
4659 """Tiles the background image to fill all the available area."""
4661 sz
= self
.GetClientSize()
4662 w
= self
._backgroundImage
.GetWidth()
4663 h
= self
._backgroundImage
.GetHeight()
4670 while y
< sz
.height
:
4671 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4677 def OnSetFocus(self
, event
):
4678 """Handles the wx.EVT_SET_FOCUS event."""
4680 self
._hasFocus
= True
4681 self
.RefreshSelected()
4685 def OnKillFocus(self
, event
):
4686 """Handles the wx.EVT_KILL_FOCUS event."""
4688 self
._hasFocus
= False
4689 self
.RefreshSelected()
4693 def OnKeyDown(self
, event
):
4694 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4696 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4698 te
.SetEventObject(self
)
4700 if self
.GetEventHandler().ProcessEvent(te
):
4701 # intercepted by the user code
4704 if self
._current
is None or self
._key
_current
is None:
4709 # how should the selection work for this event?
4710 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4714 # * : Expand all/Collapse all
4715 # ' ' | return : activate
4716 # up : go up (not last children!)
4718 # left : go to parent
4719 # right : open if parent and go next
4721 # end : go to last item without opening parents
4722 # alnum : start or continue searching for the item with this prefix
4724 keyCode
= event
.GetKeyCode()
4726 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4727 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4728 self
.Expand(self
._current
)
4730 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4731 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4733 self
.ExpandAll(self
._current
)
4735 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4736 if self
.IsExpanded(self
._current
):
4737 self
.Collapse(self
._current
)
4739 elif keyCode
== wx
.WXK_MENU
:
4740 # Use the item's bounding rectangle to determine position for the event
4741 itemRect
= self
.GetBoundingRect(self
._current
, True)
4742 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4743 event
._item
= self
._current
4744 # Use the left edge, vertical middle
4745 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4746 event
.SetEventObject(self
)
4747 self
.GetEventHandler().ProcessEvent(event
)
4749 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4751 if not self
.IsEnabled(self
._current
):
4755 if not event
.HasModifiers():
4756 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4757 event
._item
= self
._current
4758 event
.SetEventObject(self
)
4759 self
.GetEventHandler().ProcessEvent(event
)
4761 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4762 checked
= not self
.IsItemChecked(self
._current
)
4763 self
.CheckItem(self
._current
, checked
)
4765 # in any case, also generate the normal key event for this key,
4766 # even if we generated the ACTIVATED event above: this is what
4767 # wxMSW does and it makes sense because you might not want to
4768 # process ACTIVATED event at all and handle Space and Return
4769 # directly (and differently) which would be impossible otherwise
4772 # up goes to the previous sibling or to the last
4773 # of its children if it's expanded
4774 elif keyCode
== wx
.WXK_UP
:
4775 prev
= self
.GetPrevSibling(self
._key
_current
)
4777 prev
= self
.GetItemParent(self
._key
_current
)
4778 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4782 current
= self
._key
_current
4783 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4784 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4785 # otherwise we return to where we came from
4786 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4787 self
._key
_current
= prev
4790 current
= self
._key
_current
4792 # We are going to another parent node
4793 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4794 child
= self
.GetLastChild(prev
)
4799 # Try to get the previous siblings and see if they are active
4800 while prev
and not self
.IsEnabled(prev
):
4801 prev
= self
.GetPrevSibling(prev
)
4804 # No previous siblings active: go to the parent and up
4805 prev
= self
.GetItemParent(current
)
4806 while prev
and not self
.IsEnabled(prev
):
4807 prev
= self
.GetItemParent(prev
)
4810 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4811 self
._key
_current
= prev
4813 # left arrow goes to the parent
4814 elif keyCode
== wx
.WXK_LEFT
:
4816 prev
= self
.GetItemParent(self
._current
)
4817 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4818 # don't go to root if it is hidden
4819 prev
= self
.GetPrevSibling(self
._current
)
4821 if self
.IsExpanded(self
._current
):
4822 self
.Collapse(self
._current
)
4824 if prev
and self
.IsEnabled(prev
):
4825 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4827 elif keyCode
== wx
.WXK_RIGHT
:
4828 # this works the same as the down arrow except that we
4829 # also expand the item if it wasn't expanded yet
4830 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4831 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4832 if self
.IsEnabled(child
):
4833 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4834 self
._key
_current
= child
4836 self
.Expand(self
._current
)
4839 elif keyCode
== wx
.WXK_DOWN
:
4840 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4842 child
= self
.GetNextActiveItem(self
._key
_current
)
4845 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4846 self
._key
_current
= child
4850 next
= self
.GetNextSibling(self
._key
_current
)
4853 current
= self
._key
_current
4854 while current
and not next
:
4855 current
= self
.GetItemParent(current
)
4857 next
= self
.GetNextSibling(current
)
4858 if not next
or not self
.IsEnabled(next
):
4862 while next
and not self
.IsEnabled(next
):
4863 next
= self
.GetNext(next
)
4866 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4867 self
._key
_current
= next
4870 # <End> selects the last visible tree item
4871 elif keyCode
== wx
.WXK_END
:
4873 last
= self
.GetRootItem()
4875 while last
and self
.IsExpanded(last
):
4877 lastChild
= self
.GetLastChild(last
)
4879 # it may happen if the item was expanded but then all of
4880 # its children have been deleted - so IsExpanded() returned
4881 # true, but GetLastChild() returned invalid item
4887 if last
and self
.IsEnabled(last
):
4889 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4891 # <Home> selects the root item
4892 elif keyCode
== wx
.WXK_HOME
:
4894 prev
= self
.GetRootItem()
4899 if self
.HasFlag(TR_HIDE_ROOT
):
4900 prev
, cookie
= self
.GetFirstChild(prev
)
4904 if self
.IsEnabled(prev
):
4905 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4909 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4910 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4911 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4913 # find the next item starting with the given prefix
4915 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4921 if self
.IsEnabled(id):
4923 self
._findPrefix
+= ch
4925 # also start the timer to reset the current prefix if the user
4926 # doesn't press any more alnum keys soon -- we wouldn't want
4927 # to use this prefix for a new item search
4928 if not self
._findTimer
:
4929 self
._findTimer
= TreeFindTimer(self
)
4931 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4938 def GetNextActiveItem(self
, item
, down
=True):
4939 """Returns the next active item. Used Internally at present. """
4942 sibling
= self
.GetNextSibling
4944 sibling
= self
.GetPrevSibling
4946 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4947 # Is an unchecked radiobutton... all its children are inactive
4948 # try to get the next/previous sibling
4952 child
= sibling(item
)
4953 if (child
and self
.IsEnabled(child
)) or not child
:
4958 # Tha's not a radiobutton... but some of its children can be
4960 child
, cookie
= self
.GetFirstChild(item
)
4961 while child
and not self
.IsEnabled(child
):
4962 child
, cookie
= self
.GetNextChild(item
, cookie
)
4964 if child
and self
.IsEnabled(child
):
4970 def HitTest(self
, point
, flags
=0):
4972 Calculates which (if any) item is under the given point, returning the tree item
4973 at this point plus extra information flags. Flags is a bitlist of the following:
4975 TREE_HITTEST_ABOVE above the client area
4976 TREE_HITTEST_BELOW below the client area
4977 TREE_HITTEST_NOWHERE no item has been hit
4978 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4979 TREE_HITTEST_ONITEMICON on the icon associated to an item
4980 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4981 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4982 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4983 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4984 TREE_HITTEST_TOLEFT on the left of the client area
4985 TREE_HITTEST_TORIGHT on the right of the client area
4986 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4987 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4988 TREE_HITTEST_ONITEM anywhere on the item
4990 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4993 w
, h
= self
.GetSize()
4997 flags |
= TREE_HITTEST_TOLEFT
4999 flags |
= TREE_HITTEST_TORIGHT
5001 flags |
= TREE_HITTEST_ABOVE
5003 flags |
= TREE_HITTEST_BELOW
5008 if self
._anchor
== None:
5009 flags
= TREE_HITTEST_NOWHERE
5012 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5015 flags
= TREE_HITTEST_NOWHERE
5018 if not self
.IsEnabled(hit
):
5024 def GetBoundingRect(self
, item
, textOnly
=False):
5025 """Gets the bounding rectangle of the item."""
5028 raise "\nERROR: Invalid Tree Item. "
5032 startX
, startY
= self
.GetViewStart()
5035 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5036 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5037 rect
.width
= i
.GetWidth()
5038 rect
.height
= self
.GetLineHeight(i
)
5043 def Edit(self
, item
):
5045 Internal function. Starts the editing of an item label, sending a
5046 EVT_TREE_BEGIN_LABEL_EDIT event.
5049 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5051 te
.SetEventObject(self
)
5052 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5056 # We have to call this here because the label in
5057 # question might just have been added and no screen
5058 # update taken place.
5060 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5065 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5066 self
._textCtrl
.StopEditing()
5068 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5069 self
._textCtrl
.SetFocus()
5072 def GetEditControl(self
):
5074 Returns a pointer to the edit TextCtrl if the item is being edited or
5075 None otherwise (it is assumed that no more than one item may be edited
5079 return self
._textCtrl
5082 def OnRenameAccept(self
, item
, value
):
5084 Called by TreeTextCtrl, to accept the changes and to send the
5085 EVT_TREE_END_LABEL_EDIT event.
5088 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5090 le
.SetEventObject(self
)
5092 le
._editCancelled
= False
5094 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5097 def OnRenameCancelled(self
, item
):
5099 Called by TreeTextCtrl, to cancel the changes and to send the
5100 EVT_TREE_END_LABEL_EDIT event.
5103 # let owner know that the edit was cancelled
5104 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5106 le
.SetEventObject(self
)
5108 le
._editCancelled
= True
5110 self
.GetEventHandler().ProcessEvent(le
)
5113 def OnRenameTimer(self
):
5114 """The timer for renaming has expired. Start editing."""
5116 self
.Edit(self
._current
)
5119 def OnMouse(self
, event
):
5120 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5122 if not self
._anchor
:
5125 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5127 # Is the mouse over a tree item button?
5129 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5130 underMouse
= thisItem
5131 underMouseChanged
= underMouse
!= self
._underMouse
5133 if underMouse
and (flags
& TREE_HITTEST_ONITEM
) and not event
.LeftIsDown() and \
5134 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5135 underMouse
= underMouse
5139 if underMouse
!= self
._underMouse
:
5140 if self
._underMouse
:
5141 # unhighlight old item
5142 self
._underMouse
= None
5144 self
._underMouse
= underMouse
5146 # Determines what item we are hovering over and need a tooltip for
5147 hoverItem
= thisItem
5149 # We do not want a tooltip if we are dragging, or if the rename timer is running
5150 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5152 if hoverItem
is not None:
5153 # Ask the tree control what tooltip (if any) should be shown
5154 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5155 hevent
._item
= hoverItem
5156 hevent
.SetEventObject(self
)
5158 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5159 self
.SetToolTip(hevent
._label
)
5161 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5162 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5163 self
._isonhyperlink
= True
5165 if self
._isonhyperlink
:
5166 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5167 self
._isonhyperlink
= False
5169 # we process left mouse up event (enables in-place edit), right down
5170 # (pass to the user code), left dbl click (activate item) and
5171 # dragging/moving events for items drag-and-drop
5173 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5174 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5180 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5182 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5184 if self
._dragCount
== 0:
5185 self
._dragStart
= pt
5188 self
._dragCount
= self
._dragCount
+ 1
5190 if self
._dragCount
!= 3:
5191 # wait until user drags a bit further...
5194 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5196 nevent
= TreeEvent(command
, self
.GetId())
5197 nevent
._item
= self
._current
5198 nevent
.SetEventObject(self
)
5199 newpt
= self
.CalcScrolledPosition(pt
)
5200 nevent
.SetPoint(newpt
)
5202 # by default the dragging is not supported, the user code must
5203 # explicitly allow the event for it to take place
5206 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5208 # we're going to drag this item
5209 self
._isDragging
= True
5211 # remember the old cursor because we will change it while
5213 self
._oldCursor
= self
._cursor
5215 # in a single selection control, hide the selection temporarily
5216 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5217 self
._oldSelection
= self
.GetSelection()
5219 if self
._oldSelection
:
5221 self
._oldSelection
.SetHilight(False)
5222 self
.RefreshLine(self
._oldSelection
)
5224 selections
= self
.GetSelections()
5225 if len(selections
) == 1:
5226 self
._oldSelection
= selections
[0]
5227 self
._oldSelection
.SetHilight(False)
5228 self
.RefreshLine(self
._oldSelection
)
5233 # Create the custom draw image from the icons and the text of the item
5234 self
._dragImage
= DragImage(self
, self
._current
)
5235 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5236 self
._dragImage
.Show()
5237 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5239 elif event
.Dragging() and self
._isDragging
:
5241 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5243 if self
._countDrag
== 0 and item
:
5244 self
._oldItem
= item
5246 if item
!= self
._dropTarget
:
5248 # unhighlight the previous drop target
5249 if self
._dropTarget
:
5250 self
._dropTarget
.SetHilight(False)
5251 self
.RefreshLine(self
._dropTarget
)
5253 item
.SetHilight(True)
5254 self
.RefreshLine(item
)
5255 self
._countDrag
= self
._countDrag
+ 1
5256 self
._dropTarget
= item
5260 if self
._countDrag
>= 3:
5261 # Here I am trying to avoid ugly repainting problems... hope it works
5262 self
.RefreshLine(self
._oldItem
)
5265 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5268 self
._dragImage
.EndDrag()
5270 if self
._dropTarget
:
5271 self
._dropTarget
.SetHilight(False)
5273 if self
._oldSelection
:
5275 self
._oldSelection
.SetHilight(True)
5276 self
.RefreshLine(self
._oldSelection
)
5277 self
._oldSelection
= None
5279 # generate the drag end event
5280 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5282 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5283 event
.SetEventObject(self
)
5285 self
.GetEventHandler().ProcessEvent(event
)
5287 self
._isDragging
= False
5288 self
._dropTarget
= None
5290 self
.SetCursor(self
._oldCursor
)
5292 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5295 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5300 # If we got to this point, we are not dragging or moving the mouse.
5301 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5302 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5303 # We skip even if we didn't hit an item because we still should
5304 # restore focus to the tree control even if we didn't exactly hit an item.
5305 if event
.LeftDown():
5306 self
._hasFocus
= True
5307 self
.SetFocusIgnoringChildren()
5310 # here we process only the messages which happen on tree items
5315 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5316 self
._textCtrl
.StopEditing()
5317 return # we hit the blank area
5319 if event
.RightDown():
5321 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5322 self
._textCtrl
.StopEditing()
5324 self
._hasFocus
= True
5325 self
.SetFocusIgnoringChildren()
5327 # If the item is already selected, do not update the selection.
5328 # Multi-selections should not be cleared if a selected item is clicked.
5329 if not self
.IsSelected(item
):
5331 self
.DoSelectItem(item
, True, False)
5333 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5335 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5336 nevent
.SetEventObject(self
)
5337 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5339 # Consistent with MSW (for now), send the ITEM_MENU *after*
5340 # the RIGHT_CLICK event. TODO: This behaviour may change.
5341 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5342 nevent2
._item
= item
5343 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5344 nevent2
.SetEventObject(self
)
5345 self
.GetEventHandler().ProcessEvent(nevent2
)
5347 elif event
.LeftUp():
5349 # this facilitates multiple-item drag-and-drop
5351 if self
.HasFlag(TR_MULTIPLE
):
5353 selections
= self
.GetSelections()
5355 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5357 self
.DoSelectItem(item
, True, False)
5359 if self
._lastOnSame
:
5361 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5363 if self
._renameTimer
:
5365 if self
._renameTimer
.IsRunning():
5367 self
._renameTimer
.Stop()
5371 self
._renameTimer
= TreeRenameTimer(self
)
5373 self
._renameTimer
.Start(_DELAY
, True)
5375 self
._lastOnSame
= False
5378 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5380 if not item
or not item
.IsEnabled():
5381 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5382 self
._textCtrl
.StopEditing()
5385 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5386 self
._textCtrl
.StopEditing()
5388 self
._hasFocus
= True
5389 self
.SetFocusIgnoringChildren()
5391 if event
.LeftDown():
5393 self
._lastOnSame
= item
== self
._current
5395 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5397 # only toggle the item for a single click, double click on
5398 # the button doesn't do anything (it toggles the item twice)
5399 if event
.LeftDown():
5403 # don't select the item if the button was clicked
5406 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5408 if event
.LeftDown():
5410 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5414 # clear the previously selected items, if the
5415 # user clicked outside of the present selection.
5416 # otherwise, perform the deselection on mouse-up.
5417 # this allows multiple drag and drop to work.
5418 # but if Cmd is down, toggle selection of the clicked item
5419 if not self
.IsSelected(item
) or event
.CmdDown():
5421 if flags
& TREE_HITTEST_ONITEM
:
5422 # how should the selection work for this event?
5423 if item
.IsHyperText():
5424 self
.SetItemVisited(item
, True)
5426 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5430 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5432 # For some reason, Windows isn't recognizing a left double-click,
5433 # so we need to simulate it here. Allow 200 milliseconds for now.
5434 if event
.LeftDClick():
5436 # double clicking should not start editing the item label
5437 if self
._renameTimer
:
5438 self
._renameTimer
.Stop()
5440 self
._lastOnSame
= False
5442 # send activate event first
5443 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5445 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5446 nevent
.SetEventObject(self
)
5447 if not self
.GetEventHandler().ProcessEvent(nevent
):
5449 # if the user code didn't process the activate event,
5450 # handle it ourselves by toggling the item when it is
5452 ## if item.HasPlus():
5456 def OnInternalIdle(self
):
5457 """Performs operations in idle time (essentially drawing)."""
5459 # Check if we need to select the root item
5460 # because nothing else has been selected.
5461 # Delaying it means that we can invoke event handlers
5462 # as required, when a first item is selected.
5463 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5466 self
.SelectItem(self
._select
_me
)
5467 elif self
.GetRootItem():
5468 self
.SelectItem(self
.GetRootItem())
5470 # after all changes have been done to the tree control,
5471 # we actually redraw the tree when everything is over
5475 if self
._freezeCount
:
5480 self
.CalculatePositions()
5482 self
.AdjustMyScrollbars()
5487 def CalculateSize(self
, item
, dc
):
5488 """Calculates overall position and size of an item."""
5490 attr
= item
.GetAttributes()
5492 if attr
and attr
.HasFont():
5493 dc
.SetFont(attr
.GetFont())
5495 dc
.SetFont(self
._boldFont
)
5497 dc
.SetFont(self
._normalFont
)
5499 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5502 # restore normal font
5503 dc
.SetFont(self
._normalFont
)
5505 image_w
, image_h
= 0, 0
5506 image
= item
.GetCurrentImage()
5508 if image
!= _NO_IMAGE
:
5510 if self
._imageListNormal
:
5512 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5515 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5517 checkimage
= item
.GetCurrentCheckedImage()
5518 if checkimage
is not None:
5519 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5525 total_h
+= 2 # at least 2 pixels
5527 total_h
+= total_h
/10 # otherwise 10% extra spacing
5529 if total_h
> self
._lineHeight
:
5530 self
._lineHeight
= total_h
5532 if not item
.GetWindow():
5533 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5534 item
.SetHeight(total_h
)
5536 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5539 def CalculateLevel(self
, item
, dc
, level
, y
):
5540 """Calculates the level of an item."""
5542 x
= level
*self
._indent
5544 if not self
.HasFlag(TR_HIDE_ROOT
):
5550 # a hidden root is not evaluated, but its
5551 # children are always calculated
5552 children
= item
.GetChildren()
5553 count
= len(children
)
5555 for n
in xrange(count
):
5556 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5560 self
.CalculateSize(item
, dc
)
5563 item
.SetX(x
+self
._spacing
)
5565 y
+= self
.GetLineHeight(item
)
5567 if not item
.IsExpanded():
5568 # we don't need to calculate collapsed branches
5571 children
= item
.GetChildren()
5572 count
= len(children
)
5574 for n
in xrange(count
):
5575 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5580 def CalculatePositions(self
):
5581 """Calculates all the positions of the visible items."""
5583 if not self
._anchor
:
5586 dc
= wx
.ClientDC(self
)
5589 dc
.SetFont(self
._normalFont
)
5590 dc
.SetPen(self
._dottedPen
)
5592 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5595 def RefreshSubtree(self
, item
):
5596 """Refreshes a damaged subtree of an item."""
5600 if self
._freezeCount
:
5603 client
= self
.GetClientSize()
5606 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5607 rect
.width
= client
.x
5608 rect
.height
= client
.y
5610 self
.Refresh(True, rect
)
5611 self
.AdjustMyScrollbars()
5614 def RefreshLine(self
, item
):
5615 """Refreshes a damaged item line."""
5619 if self
._freezeCount
:
5623 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5624 rect
.width
= self
.GetClientSize().x
5625 rect
.height
= self
.GetLineHeight(item
)
5627 self
.Refresh(True, rect
)
5630 def RefreshSelected(self
):
5631 """Refreshes a damaged selected item line."""
5633 if self
._freezeCount
:
5636 # TODO: this is awfully inefficient, we should keep the list of all
5637 # selected items internally, should be much faster
5639 self
.RefreshSelectedUnder(self
._anchor
)
5642 def RefreshSelectedUnder(self
, item
):
5643 """Refreshes the selected items under the given item."""
5645 if self
._freezeCount
:
5648 if item
.IsSelected():
5649 self
.RefreshLine(item
)
5651 children
= item
.GetChildren()
5652 for child
in children
:
5653 self
.RefreshSelectedUnder(child
)
5657 """Freeze CustomTreeCtrl."""
5659 self
._freezeCount
= self
._freezeCount
+ 1
5663 """Thaw CustomTreeCtrl."""
5665 if self
._freezeCount
== 0:
5666 raise "\nERROR: Thawing Unfrozen Tree Control?"
5668 self
._freezeCount
= self
._freezeCount
- 1
5670 if not self
._freezeCount
:
5674 # ----------------------------------------------------------------------------
5675 # changing colours: we need to refresh the tree control
5676 # ----------------------------------------------------------------------------
5678 def SetBackgroundColour(self
, colour
):
5679 """Changes the background colour of CustomTreeCtrl."""
5681 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5684 if self
._freezeCount
:
5692 def SetForegroundColour(self
, colour
):
5693 """Changes the foreground colour of CustomTreeCtrl."""
5695 if not wx
.Window
.SetForegroundColour(self
, colour
):
5698 if self
._freezeCount
:
5706 def OnGetToolTip(self
, event
):
5708 Process the tooltip event, to speed up event processing. Does not actually
5715 def DoGetBestSize(self
):
5716 """Something is better than nothing..."""
5718 # something is better than nothing...
5719 # 100x80 is what the MSW version will get from the default
5720 # wxControl::DoGetBestSize
5722 return wx
.Size(100, 80)
5725 def GetClassDefaultAttributes(self
):
5726 """Gets the class default attributes."""
5728 attr
= wx
.VisualAttributes()
5729 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5730 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5731 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)