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 dc
.DrawRectangleRect(itemrect
)
4303 if item
.IsSelected():
4305 # If it's selected, and there's an image, then we should
4306 # take care to leave the area under the image painted in the
4307 # background colour.
4309 wnd
= item
.GetWindow()
4312 wndx
, wndy
= item
.GetWindowSize()
4314 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4316 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4319 if self
._usegradients
:
4320 if self
._gradientstyle
== 0: # Horizontal
4321 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4323 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4324 elif self
._vistaselection
:
4325 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4327 dc
.DrawRectangleRect(itemrect
)
4329 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4330 # don't allow backgrounds to be customized. Not drawing the background,
4331 # except for custom item backgrounds, works for both kinds of theme.
4332 elif drawItemBackground
:
4334 minusicon
= wcheck
+ image_w
- 2
4335 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4337 item
.GetWidth()-minusicon
,
4340 if self
._usegradients
and self
._hasFocus
:
4341 if self
._gradientstyle
== 0: # Horizontal
4342 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4344 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4346 dc
.DrawRectangleRect(itemrect
)
4348 if image
!= _NO_IMAGE
:
4350 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4351 if item
.IsEnabled():
4352 imglist
= self
._imageListNormal
4354 imglist
= self
._grayedImageList
4356 imglist
.Draw(image
, dc
,
4357 item
.GetX() + wcheck
,
4358 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4359 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4361 dc
.DestroyClippingRegion()
4364 if item
.IsEnabled():
4365 imglist
= self
._imageListCheck
4367 imglist
= self
._grayedCheckList
4369 imglist
.Draw(checkimage
, dc
,
4371 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4372 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4374 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4375 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4377 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4379 if not item
.IsEnabled():
4380 foreground
= dc
.GetTextForeground()
4381 dc
.SetTextForeground(self
._disabledColour
)
4382 dc
.DrawLabel(item
.GetText(), textrect
)
4383 dc
.SetTextForeground(foreground
)
4385 dc
.DrawLabel(item
.GetText(), textrect
)
4387 wnd
= item
.GetWindow()
4389 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4390 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4391 if not wnd
.IsShown():
4393 if wnd
.GetPosition() != (wndx
, ya
):
4394 wnd
.SetPosition((wndx
, ya
))
4396 # restore normal font
4397 dc
.SetFont(self
._normalFont
)
4400 # Now y stands for the top of the item, whereas it used to stand for middle !
4401 def PaintLevel(self
, item
, dc
, level
, y
):
4402 """Paint a level of CustomTreeCtrl."""
4404 x
= level
*self
._indent
4406 if not self
.HasFlag(TR_HIDE_ROOT
):
4412 # always expand hidden root
4414 children
= item
.GetChildren()
4415 count
= len(children
)
4421 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4424 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4426 # draw line down to last child
4427 origY
+= self
.GetLineHeight(children
[0])>>1
4428 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4429 dc
.DrawLine(3, origY
, 3, oldY
)
4433 item
.SetX(x
+self
._spacing
)
4436 h
= self
.GetLineHeight(item
)
4438 y_mid
= y_top
+ (h
>>1)
4441 exposed_x
= dc
.LogicalToDeviceX(0)
4442 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4444 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4445 if wx
.Platform
== "__WXMAC__":
4446 # don't draw rect outline if we already have the
4447 # background color under Mac
4448 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4450 pen
= self
._borderPen
4452 if item
.IsSelected():
4453 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4454 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4456 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4458 attr
= item
.GetAttributes()
4459 if attr
and attr
.HasTextColour():
4460 colText
= attr
.GetTextColour()
4462 colText
= self
.GetForegroundColour()
4464 if self
._vistaselection
:
4468 dc
.SetTextForeground(colText
)
4473 self
.PaintItem(item
, dc
)
4475 if self
.HasFlag(TR_ROW_LINES
):
4477 # if the background colour is white, choose a
4478 # contrasting color for the lines
4479 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4480 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4481 dc
.DrawLine(0, y_top
, 10000, y_top
)
4482 dc
.DrawLine(0, y
, 10000, y
)
4484 # restore DC objects
4485 dc
.SetBrush(wx
.WHITE_BRUSH
)
4486 dc
.SetTextForeground(wx
.BLACK
)
4488 if not self
.HasFlag(TR_NO_LINES
):
4490 # draw the horizontal line here
4491 dc
.SetPen(self
._dottedPen
)
4493 if x
> self
._indent
:
4494 x_start
-= self
._indent
4495 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4497 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4500 # should the item show a button?
4501 if item
.HasPlus() and self
.HasButtons():
4503 if self
._imageListButtons
:
4505 # draw the image button here
4508 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4509 if item
.IsSelected():
4510 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4512 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4514 yy
= y_mid
- image_h
/2
4516 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4517 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4518 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4519 dc
.DestroyClippingRegion()
4521 else: # no custom buttons
4523 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4524 # We draw something like the Mac twist buttons
4526 dc
.SetPen(wx
.BLACK_PEN
)
4527 dc
.SetBrush(self
._hilightBrush
)
4528 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4530 if item
.IsExpanded():
4532 button
[0].y
= y_mid
- 3
4534 button
[1].y
= button
[0].y
4536 button
[2].y
= button
[0].y
+ 6
4539 button
[0].y
= y_mid
- 5
4540 button
[1].x
= button
[0].x
4541 button
[1].y
= y_mid
+ 5
4542 button
[2].x
= button
[0].x
+ 5
4545 dc
.DrawPolygon(button
)
4548 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4555 if item
.IsExpanded():
4556 flag |
= _CONTROL_EXPANDED
4557 if item
== self
._underMouse
:
4558 flag |
= _CONTROL_CURRENT
4560 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4562 if item
.IsExpanded():
4564 children
= item
.GetChildren()
4565 count
= len(children
)
4574 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4577 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4579 # draw line down to last child
4580 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4581 if self
.HasButtons():
4584 # Only draw the portion of the line that is visible, in case it is huge
4585 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4586 yOrigin
= abs(yOrigin
)
4587 width
, height
= self
.GetClientSize()
4589 # Move end points to the begining/end of the view?
4592 if oldY
> yOrigin
+ height
:
4593 oldY
= yOrigin
+ height
4595 # after the adjustments if y_mid is larger than oldY then the line
4596 # isn't visible at all so don't draw anything
4598 dc
.SetPen(self
._dottedPen
)
4599 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4604 # -----------------------------------------------------------------------------
4605 # wxWidgets callbacks
4606 # -----------------------------------------------------------------------------
4608 def OnPaint(self
, event
):
4609 """Handles the wx.EVT_PAINT event."""
4611 dc
= wx
.PaintDC(self
)
4614 if not self
._anchor
:
4617 dc
.SetFont(self
._normalFont
)
4618 dc
.SetPen(self
._dottedPen
)
4621 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4624 def OnEraseBackground(self
, event
):
4625 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4627 # Can we actually do something here (or in OnPaint()) To Handle
4628 # background images that are stretchable or always centered?
4629 # I tried but I get enormous flickering...
4631 if not self
._backgroundImage
:
4635 if self
._imageStretchStyle
== _StyleTile
:
4639 dc
= wx
.ClientDC(self
)
4640 rect
= self
.GetUpdateRegion().GetBox()
4641 dc
.SetClippingRect(rect
)
4643 self
.TileBackground(dc
)
4646 def TileBackground(self
, dc
):
4647 """Tiles the background image to fill all the available area."""
4649 sz
= self
.GetClientSize()
4650 w
= self
._backgroundImage
.GetWidth()
4651 h
= self
._backgroundImage
.GetHeight()
4658 while y
< sz
.height
:
4659 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4665 def OnSetFocus(self
, event
):
4666 """Handles the wx.EVT_SET_FOCUS event."""
4668 self
._hasFocus
= True
4669 self
.RefreshSelected()
4673 def OnKillFocus(self
, event
):
4674 """Handles the wx.EVT_KILL_FOCUS event."""
4676 self
._hasFocus
= False
4677 self
.RefreshSelected()
4681 def OnKeyDown(self
, event
):
4682 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4684 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4686 te
.SetEventObject(self
)
4688 if self
.GetEventHandler().ProcessEvent(te
):
4689 # intercepted by the user code
4692 if self
._current
is None or self
._key
_current
is None:
4697 # how should the selection work for this event?
4698 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4702 # * : Expand all/Collapse all
4703 # ' ' | return : activate
4704 # up : go up (not last children!)
4706 # left : go to parent
4707 # right : open if parent and go next
4709 # end : go to last item without opening parents
4710 # alnum : start or continue searching for the item with this prefix
4712 keyCode
= event
.GetKeyCode()
4714 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4715 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4716 self
.Expand(self
._current
)
4718 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4719 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4721 self
.ExpandAll(self
._current
)
4723 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4724 if self
.IsExpanded(self
._current
):
4725 self
.Collapse(self
._current
)
4727 elif keyCode
== wx
.WXK_MENU
:
4728 # Use the item's bounding rectangle to determine position for the event
4729 itemRect
= self
.GetBoundingRect(self
._current
, True)
4730 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4731 event
._item
= self
._current
4732 # Use the left edge, vertical middle
4733 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4734 event
.SetEventObject(self
)
4735 self
.GetEventHandler().ProcessEvent(event
)
4737 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4739 if not self
.IsEnabled(self
._current
):
4743 if not event
.HasModifiers():
4744 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4745 event
._item
= self
._current
4746 event
.SetEventObject(self
)
4747 self
.GetEventHandler().ProcessEvent(event
)
4749 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4750 checked
= not self
.IsItemChecked(self
._current
)
4751 self
.CheckItem(self
._current
, checked
)
4753 # in any case, also generate the normal key event for this key,
4754 # even if we generated the ACTIVATED event above: this is what
4755 # wxMSW does and it makes sense because you might not want to
4756 # process ACTIVATED event at all and handle Space and Return
4757 # directly (and differently) which would be impossible otherwise
4760 # up goes to the previous sibling or to the last
4761 # of its children if it's expanded
4762 elif keyCode
== wx
.WXK_UP
:
4763 prev
= self
.GetPrevSibling(self
._key
_current
)
4765 prev
= self
.GetItemParent(self
._key
_current
)
4766 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4770 current
= self
._key
_current
4771 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4772 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4773 # otherwise we return to where we came from
4774 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4775 self
._key
_current
= prev
4778 current
= self
._key
_current
4780 # We are going to another parent node
4781 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4782 child
= self
.GetLastChild(prev
)
4787 # Try to get the previous siblings and see if they are active
4788 while prev
and not self
.IsEnabled(prev
):
4789 prev
= self
.GetPrevSibling(prev
)
4792 # No previous siblings active: go to the parent and up
4793 prev
= self
.GetItemParent(current
)
4794 while prev
and not self
.IsEnabled(prev
):
4795 prev
= self
.GetItemParent(prev
)
4798 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4799 self
._key
_current
= prev
4801 # left arrow goes to the parent
4802 elif keyCode
== wx
.WXK_LEFT
:
4804 prev
= self
.GetItemParent(self
._current
)
4805 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4806 # don't go to root if it is hidden
4807 prev
= self
.GetPrevSibling(self
._current
)
4809 if self
.IsExpanded(self
._current
):
4810 self
.Collapse(self
._current
)
4812 if prev
and self
.IsEnabled(prev
):
4813 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4815 elif keyCode
== wx
.WXK_RIGHT
:
4816 # this works the same as the down arrow except that we
4817 # also expand the item if it wasn't expanded yet
4818 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4819 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4820 if self
.IsEnabled(child
):
4821 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4822 self
._key
_current
= child
4824 self
.Expand(self
._current
)
4827 elif keyCode
== wx
.WXK_DOWN
:
4828 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4830 child
= self
.GetNextActiveItem(self
._key
_current
)
4833 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4834 self
._key
_current
= child
4838 next
= self
.GetNextSibling(self
._key
_current
)
4841 current
= self
._key
_current
4842 while current
and not next
:
4843 current
= self
.GetItemParent(current
)
4845 next
= self
.GetNextSibling(current
)
4846 if not self
.IsEnabled(next
):
4850 while next
and not self
.IsEnabled(next
):
4851 next
= self
.GetNext(next
)
4854 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4855 self
._key
_current
= next
4858 # <End> selects the last visible tree item
4859 elif keyCode
== wx
.WXK_END
:
4861 last
= self
.GetRootItem()
4863 while last
and self
.IsExpanded(last
):
4865 lastChild
= self
.GetLastChild(last
)
4867 # it may happen if the item was expanded but then all of
4868 # its children have been deleted - so IsExpanded() returned
4869 # true, but GetLastChild() returned invalid item
4875 if last
and self
.IsEnabled(last
):
4877 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4879 # <Home> selects the root item
4880 elif keyCode
== wx
.WXK_HOME
:
4882 prev
= self
.GetRootItem()
4887 if self
.HasFlag(TR_HIDE_ROOT
):
4888 prev
, cookie
= self
.GetFirstChild(prev
)
4892 if self
.IsEnabled(prev
):
4893 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4897 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4898 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4899 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4901 # find the next item starting with the given prefix
4903 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4909 if self
.IsEnabled(id):
4911 self
._findPrefix
+= ch
4913 # also start the timer to reset the current prefix if the user
4914 # doesn't press any more alnum keys soon -- we wouldn't want
4915 # to use this prefix for a new item search
4916 if not self
._findTimer
:
4917 self
._findTimer
= TreeFindTimer(self
)
4919 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4926 def GetNextActiveItem(self
, item
, down
=True):
4927 """Returns the next active item. Used Internally at present. """
4930 sibling
= self
.GetNextSibling
4932 sibling
= self
.GetPrevSibling
4934 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4935 # Is an unchecked radiobutton... all its children are inactive
4936 # try to get the next/previous sibling
4940 child
= sibling(item
)
4941 if (child
and self
.IsEnabled(child
)) or not child
:
4946 # Tha's not a radiobutton... but some of its children can be
4948 child
, cookie
= self
.GetFirstChild(item
)
4949 while child
and not self
.IsEnabled(child
):
4950 child
, cookie
= self
.GetNextChild(item
, cookie
)
4952 if child
and self
.IsEnabled(child
):
4958 def HitTest(self
, point
, flags
=0):
4960 Calculates which (if any) item is under the given point, returning the tree item
4961 at this point plus extra information flags. Flags is a bitlist of the following:
4963 TREE_HITTEST_ABOVE above the client area
4964 TREE_HITTEST_BELOW below the client area
4965 TREE_HITTEST_NOWHERE no item has been hit
4966 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4967 TREE_HITTEST_ONITEMICON on the icon associated to an item
4968 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4969 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4970 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4971 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4972 TREE_HITTEST_TOLEFT on the left of the client area
4973 TREE_HITTEST_TORIGHT on the right of the client area
4974 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4975 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4976 TREE_HITTEST_ONITEM anywhere on the item
4978 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4981 w
, h
= self
.GetSize()
4985 flags |
= TREE_HITTEST_TOLEFT
4987 flags |
= TREE_HITTEST_TORIGHT
4989 flags |
= TREE_HITTEST_ABOVE
4991 flags |
= TREE_HITTEST_BELOW
4996 if self
._anchor
== None:
4997 flags
= TREE_HITTEST_NOWHERE
5000 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5003 flags
= TREE_HITTEST_NOWHERE
5006 if not self
.IsEnabled(hit
):
5012 def GetBoundingRect(self
, item
, textOnly
=False):
5013 """Gets the bounding rectangle of the item."""
5016 raise "\nERROR: Invalid Tree Item. "
5020 startX
, startY
= self
.GetViewStart()
5023 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5024 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5025 rect
.width
= i
.GetWidth()
5026 rect
.height
= self
.GetLineHeight(i
)
5031 def Edit(self
, item
):
5033 Internal function. Starts the editing of an item label, sending a
5034 EVT_TREE_BEGIN_LABEL_EDIT event.
5037 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5039 te
.SetEventObject(self
)
5040 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5044 # We have to call this here because the label in
5045 # question might just have been added and no screen
5046 # update taken place.
5048 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5053 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5054 self
._textCtrl
.StopEditing()
5056 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5057 self
._textCtrl
.SetFocus()
5060 def GetEditControl(self
):
5062 Returns a pointer to the edit TextCtrl if the item is being edited or
5063 None otherwise (it is assumed that no more than one item may be edited
5067 return self
._textCtrl
5070 def OnRenameAccept(self
, item
, value
):
5072 Called by TreeTextCtrl, to accept the changes and to send the
5073 EVT_TREE_END_LABEL_EDIT event.
5076 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5078 le
.SetEventObject(self
)
5080 le
._editCancelled
= False
5082 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5085 def OnRenameCancelled(self
, item
):
5087 Called by TreeTextCtrl, to cancel the changes and to send the
5088 EVT_TREE_END_LABEL_EDIT event.
5091 # let owner know that the edit was cancelled
5092 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5094 le
.SetEventObject(self
)
5096 le
._editCancelled
= True
5098 self
.GetEventHandler().ProcessEvent(le
)
5101 def OnRenameTimer(self
):
5102 """The timer for renaming has expired. Start editing."""
5104 self
.Edit(self
._current
)
5107 def OnMouse(self
, event
):
5108 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5110 if not self
._anchor
:
5113 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5115 # Is the mouse over a tree item button?
5117 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5118 underMouse
= thisItem
5119 underMouseChanged
= underMouse
!= self
._underMouse
5121 if underMouse
and (flags
& TREE_HITTEST_ONITEMBUTTON
) and not event
.LeftIsDown() and \
5122 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5123 underMouse
= underMouse
5127 if underMouse
!= self
._underMouse
:
5128 if self
._underMouse
:
5129 # unhighlight old item
5130 self
._underMouse
= None
5132 self
._underMouse
= underMouse
5134 # Determines what item we are hovering over and need a tooltip for
5135 hoverItem
= thisItem
5137 # We do not want a tooltip if we are dragging, or if the rename timer is running
5138 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5140 if hoverItem
is not None:
5141 # Ask the tree control what tooltip (if any) should be shown
5142 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5143 hevent
._item
= hoverItem
5144 hevent
.SetEventObject(self
)
5146 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5147 self
.SetToolTip(hevent
._label
)
5149 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5150 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5151 self
._isonhyperlink
= True
5153 if self
._isonhyperlink
:
5154 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5155 self
._isonhyperlink
= False
5157 # we process left mouse up event (enables in-place edit), right down
5158 # (pass to the user code), left dbl click (activate item) and
5159 # dragging/moving events for items drag-and-drop
5161 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5162 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5168 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5170 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5172 if self
._dragCount
== 0:
5173 self
._dragStart
= pt
5176 self
._dragCount
= self
._dragCount
+ 1
5178 if self
._dragCount
!= 3:
5179 # wait until user drags a bit further...
5182 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5184 nevent
= TreeEvent(command
, self
.GetId())
5185 nevent
._item
= self
._current
5186 nevent
.SetEventObject(self
)
5187 newpt
= self
.CalcScrolledPosition(pt
)
5188 nevent
.SetPoint(newpt
)
5190 # by default the dragging is not supported, the user code must
5191 # explicitly allow the event for it to take place
5194 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5196 # we're going to drag this item
5197 self
._isDragging
= True
5199 # remember the old cursor because we will change it while
5201 self
._oldCursor
= self
._cursor
5203 # in a single selection control, hide the selection temporarily
5204 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5205 self
._oldSelection
= self
.GetSelection()
5207 if self
._oldSelection
:
5209 self
._oldSelection
.SetHilight(False)
5210 self
.RefreshLine(self
._oldSelection
)
5212 selections
= self
.GetSelections()
5213 if len(selections
) == 1:
5214 self
._oldSelection
= selections
[0]
5215 self
._oldSelection
.SetHilight(False)
5216 self
.RefreshLine(self
._oldSelection
)
5221 # Create the custom draw image from the icons and the text of the item
5222 self
._dragImage
= DragImage(self
, self
._current
)
5223 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5224 self
._dragImage
.Show()
5225 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5227 elif event
.Dragging() and self
._isDragging
:
5229 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5231 if self
._countDrag
== 0 and item
:
5232 self
._oldItem
= item
5234 if item
!= self
._dropTarget
:
5236 # unhighlight the previous drop target
5237 if self
._dropTarget
:
5238 self
._dropTarget
.SetHilight(False)
5239 self
.RefreshLine(self
._dropTarget
)
5241 item
.SetHilight(True)
5242 self
.RefreshLine(item
)
5243 self
._countDrag
= self
._countDrag
+ 1
5244 self
._dropTarget
= item
5248 if self
._countDrag
>= 3:
5249 # Here I am trying to avoid ugly repainting problems... hope it works
5250 self
.RefreshLine(self
._oldItem
)
5253 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5256 self
._dragImage
.EndDrag()
5258 if self
._dropTarget
:
5259 self
._dropTarget
.SetHilight(False)
5261 if self
._oldSelection
:
5263 self
._oldSelection
.SetHilight(True)
5264 self
.RefreshLine(self
._oldSelection
)
5265 self
._oldSelection
= None
5267 # generate the drag end event
5268 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5270 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5271 event
.SetEventObject(self
)
5273 self
.GetEventHandler().ProcessEvent(event
)
5275 self
._isDragging
= False
5276 self
._dropTarget
= None
5278 self
.SetCursor(self
._oldCursor
)
5280 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5283 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5288 # If we got to this point, we are not dragging or moving the mouse.
5289 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5290 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5291 # We skip even if we didn't hit an item because we still should
5292 # restore focus to the tree control even if we didn't exactly hit an item.
5293 if event
.LeftDown():
5294 self
._hasFocus
= True
5295 self
.SetFocusIgnoringChildren()
5298 # here we process only the messages which happen on tree items
5303 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5304 self
._textCtrl
.StopEditing()
5305 return # we hit the blank area
5307 if event
.RightDown():
5309 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5310 self
._textCtrl
.StopEditing()
5312 self
._hasFocus
= True
5313 self
.SetFocusIgnoringChildren()
5315 # If the item is already selected, do not update the selection.
5316 # Multi-selections should not be cleared if a selected item is clicked.
5317 if not self
.IsSelected(item
):
5319 self
.DoSelectItem(item
, True, False)
5321 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5323 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5324 nevent
.SetEventObject(self
)
5325 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5327 # Consistent with MSW (for now), send the ITEM_MENU *after*
5328 # the RIGHT_CLICK event. TODO: This behaviour may change.
5329 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5330 nevent2
._item
= item
5331 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5332 nevent2
.SetEventObject(self
)
5333 self
.GetEventHandler().ProcessEvent(nevent2
)
5335 elif event
.LeftUp():
5337 # this facilitates multiple-item drag-and-drop
5339 if self
.HasFlag(TR_MULTIPLE
):
5341 selections
= self
.GetSelections()
5343 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5345 self
.DoSelectItem(item
, True, False)
5347 if self
._lastOnSame
:
5349 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5351 if self
._renameTimer
:
5353 if self
._renameTimer
.IsRunning():
5355 self
._renameTimer
.Stop()
5359 self
._renameTimer
= TreeRenameTimer(self
)
5361 self
._renameTimer
.Start(_DELAY
, True)
5363 self
._lastOnSame
= False
5366 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5368 if not item
or not item
.IsEnabled():
5369 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5370 self
._textCtrl
.StopEditing()
5373 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5374 self
._textCtrl
.StopEditing()
5376 self
._hasFocus
= True
5377 self
.SetFocusIgnoringChildren()
5379 if event
.LeftDown():
5381 self
._lastOnSame
= item
== self
._current
5383 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5385 # only toggle the item for a single click, double click on
5386 # the button doesn't do anything (it toggles the item twice)
5387 if event
.LeftDown():
5391 # don't select the item if the button was clicked
5394 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5396 if event
.LeftDown():
5398 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5402 # clear the previously selected items, if the
5403 # user clicked outside of the present selection.
5404 # otherwise, perform the deselection on mouse-up.
5405 # this allows multiple drag and drop to work.
5406 # but if Cmd is down, toggle selection of the clicked item
5407 if not self
.IsSelected(item
) or event
.CmdDown():
5409 if flags
& TREE_HITTEST_ONITEM
:
5410 # how should the selection work for this event?
5411 if item
.IsHyperText():
5412 self
.SetItemVisited(item
, True)
5414 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5418 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5420 # For some reason, Windows isn't recognizing a left double-click,
5421 # so we need to simulate it here. Allow 200 milliseconds for now.
5422 if event
.LeftDClick():
5424 # double clicking should not start editing the item label
5425 if self
._renameTimer
:
5426 self
._renameTimer
.Stop()
5428 self
._lastOnSame
= False
5430 # send activate event first
5431 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5433 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5434 nevent
.SetEventObject(self
)
5435 if not self
.GetEventHandler().ProcessEvent(nevent
):
5437 # if the user code didn't process the activate event,
5438 # handle it ourselves by toggling the item when it is
5440 ## if item.HasPlus():
5444 def OnInternalIdle(self
):
5445 """Performs operations in idle time (essentially drawing)."""
5447 # Check if we need to select the root item
5448 # because nothing else has been selected.
5449 # Delaying it means that we can invoke event handlers
5450 # as required, when a first item is selected.
5451 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5454 self
.SelectItem(self
._select
_me
)
5455 elif self
.GetRootItem():
5456 self
.SelectItem(self
.GetRootItem())
5458 # after all changes have been done to the tree control,
5459 # we actually redraw the tree when everything is over
5463 if self
._freezeCount
:
5468 self
.CalculatePositions()
5470 self
.AdjustMyScrollbars()
5475 def CalculateSize(self
, item
, dc
):
5476 """Calculates overall position and size of an item."""
5478 attr
= item
.GetAttributes()
5480 if attr
and attr
.HasFont():
5481 dc
.SetFont(attr
.GetFont())
5483 dc
.SetFont(self
._boldFont
)
5485 dc
.SetFont(self
._normalFont
)
5487 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5490 # restore normal font
5491 dc
.SetFont(self
._normalFont
)
5493 image_w
, image_h
= 0, 0
5494 image
= item
.GetCurrentImage()
5496 if image
!= _NO_IMAGE
:
5498 if self
._imageListNormal
:
5500 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5503 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5505 checkimage
= item
.GetCurrentCheckedImage()
5506 if checkimage
is not None:
5507 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5513 total_h
+= 2 # at least 2 pixels
5515 total_h
+= total_h
/10 # otherwise 10% extra spacing
5517 if total_h
> self
._lineHeight
:
5518 self
._lineHeight
= total_h
5520 if not item
.GetWindow():
5521 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5522 item
.SetHeight(total_h
)
5524 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5527 def CalculateLevel(self
, item
, dc
, level
, y
):
5528 """Calculates the level of an item."""
5530 x
= level
*self
._indent
5532 if not self
.HasFlag(TR_HIDE_ROOT
):
5538 # a hidden root is not evaluated, but its
5539 # children are always calculated
5540 children
= item
.GetChildren()
5541 count
= len(children
)
5543 for n
in xrange(count
):
5544 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5548 self
.CalculateSize(item
, dc
)
5551 item
.SetX(x
+self
._spacing
)
5553 y
+= self
.GetLineHeight(item
)
5555 if not item
.IsExpanded():
5556 # we don't need to calculate collapsed branches
5559 children
= item
.GetChildren()
5560 count
= len(children
)
5562 for n
in xrange(count
):
5563 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5568 def CalculatePositions(self
):
5569 """Calculates all the positions of the visible items."""
5571 if not self
._anchor
:
5574 dc
= wx
.ClientDC(self
)
5577 dc
.SetFont(self
._normalFont
)
5578 dc
.SetPen(self
._dottedPen
)
5580 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5583 def RefreshSubtree(self
, item
):
5584 """Refreshes a damaged subtree of an item."""
5588 if self
._freezeCount
:
5591 client
= self
.GetClientSize()
5594 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5595 rect
.width
= client
.x
5596 rect
.height
= client
.y
5598 self
.Refresh(True, rect
)
5599 self
.AdjustMyScrollbars()
5602 def RefreshLine(self
, item
):
5603 """Refreshes a damaged item line."""
5607 if self
._freezeCount
:
5611 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5612 rect
.width
= self
.GetClientSize().x
5613 rect
.height
= self
.GetLineHeight(item
)
5615 self
.Refresh(True, rect
)
5618 def RefreshSelected(self
):
5619 """Refreshes a damaged selected item line."""
5621 if self
._freezeCount
:
5624 # TODO: this is awfully inefficient, we should keep the list of all
5625 # selected items internally, should be much faster
5627 self
.RefreshSelectedUnder(self
._anchor
)
5630 def RefreshSelectedUnder(self
, item
):
5631 """Refreshes the selected items under the given item."""
5633 if self
._freezeCount
:
5636 if item
.IsSelected():
5637 self
.RefreshLine(item
)
5639 children
= item
.GetChildren()
5640 for child
in children
:
5641 self
.RefreshSelectedUnder(child
)
5645 """Freeze CustomTreeCtrl."""
5647 self
._freezeCount
= self
._freezeCount
+ 1
5651 """Thaw CustomTreeCtrl."""
5653 if self
._freezeCount
== 0:
5654 raise "\nERROR: Thawing Unfrozen Tree Control?"
5656 self
._freezeCount
= self
._freezeCount
- 1
5658 if not self
._freezeCount
:
5662 # ----------------------------------------------------------------------------
5663 # changing colours: we need to refresh the tree control
5664 # ----------------------------------------------------------------------------
5666 def SetBackgroundColour(self
, colour
):
5667 """Changes the background colour of CustomTreeCtrl."""
5669 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5672 if self
._freezeCount
:
5680 def SetForegroundColour(self
, colour
):
5681 """Changes the foreground colour of CustomTreeCtrl."""
5683 if not wx
.Window
.SetForegroundColour(self
, colour
):
5686 if self
._freezeCount
:
5694 def OnGetToolTip(self
, event
):
5696 Process the tooltip event, to speed up event processing. Does not actually
5703 def DoGetBestSize(self
):
5704 """Something is better than nothing..."""
5706 # something is better than nothing...
5707 # 100x80 is what the MSW version will get from the default
5708 # wxControl::DoGetBestSize
5710 return wx
.Size(100, 80)
5713 def GetClassDefaultAttributes(self
):
5714 """Gets the class default attributes."""
5716 attr
= wx
.VisualAttributes()
5717 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5718 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5719 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)