1 # --------------------------------------------------------------------------------- #
2 # CUSTOMTREECTRL wxPython IMPLEMENTATION
3 # Inspired By And Heavily Based On wxGenericTreeCtrl.
5 # Andrea Gavana, @ 17 May 2006
6 # Latest Revision: 02 Mar 2007, 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 @ 02 Mar 2007, 22.30 CET
147 # ----------------------------------------------------------------------------
149 # ----------------------------------------------------------------------------
152 _PIXELS_PER_UNIT
= 10
154 # Start editing the current item after half a second (if the mouse hasn't
155 # been clicked/moved)
158 # ----------------------------------------------------------------------------
160 # ----------------------------------------------------------------------------
162 # Enum for different images associated with a treectrl item
163 TreeItemIcon_Normal
= 0 # not selected, not expanded
164 TreeItemIcon_Selected
= 1 # selected, not expanded
165 TreeItemIcon_Expanded
= 2 # not selected, expanded
166 TreeItemIcon_SelectedExpanded
= 3 # selected, expanded
168 TreeItemIcon_Checked
= 0 # check button, checked
169 TreeItemIcon_NotChecked
= 1 # check button, not checked
170 TreeItemIcon_Flagged
= 2 # radio button, selected
171 TreeItemIcon_NotFlagged
= 3 # radio button, not selected
173 # ----------------------------------------------------------------------------
174 # CustomTreeCtrl flags
175 # ----------------------------------------------------------------------------
177 TR_NO_BUTTONS
= wx
.TR_NO_BUTTONS
# for convenience
178 TR_HAS_BUTTONS
= wx
.TR_HAS_BUTTONS
# draw collapsed/expanded btns
179 TR_NO_LINES
= wx
.TR_NO_LINES
# don't draw lines at all
180 TR_LINES_AT_ROOT
= wx
.TR_LINES_AT_ROOT
# connect top-level nodes
181 TR_TWIST_BUTTONS
= wx
.TR_TWIST_BUTTONS
# still used by wxTreeListCtrl
183 TR_SINGLE
= wx
.TR_SINGLE
# for convenience
184 TR_MULTIPLE
= wx
.TR_MULTIPLE
# can select multiple items
185 TR_EXTENDED
= wx
.TR_EXTENDED
# TODO: allow extended selection
186 TR_HAS_VARIABLE_ROW_HEIGHT
= wx
.TR_HAS_VARIABLE_ROW_HEIGHT
# what it says
188 TR_EDIT_LABELS
= wx
.TR_EDIT_LABELS
# can edit item labels
189 TR_ROW_LINES
= wx
.TR_ROW_LINES
# put border around items
190 TR_HIDE_ROOT
= wx
.TR_HIDE_ROOT
# don't display root node
192 TR_FULL_ROW_HIGHLIGHT
= wx
.TR_FULL_ROW_HIGHLIGHT
# highlight full horz space
194 TR_AUTO_CHECK_CHILD
= 0x04000 # only meaningful for checkboxes
195 TR_AUTO_TOGGLE_CHILD
= 0x08000 # only meaningful for checkboxes
196 TR_AUTO_CHECK_PARENT
= 0x10000 # only meaningful for checkboxes
198 TR_DEFAULT_STYLE
= wx
.TR_DEFAULT_STYLE
# default style for the tree control
200 # Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine
201 # where exactly the specified point is situated:
203 TREE_HITTEST_ABOVE
= wx
.TREE_HITTEST_ABOVE
204 TREE_HITTEST_BELOW
= wx
.TREE_HITTEST_BELOW
205 TREE_HITTEST_NOWHERE
= wx
.TREE_HITTEST_NOWHERE
206 # on the button associated with an item.
207 TREE_HITTEST_ONITEMBUTTON
= wx
.TREE_HITTEST_ONITEMBUTTON
208 # on the bitmap associated with an item.
209 TREE_HITTEST_ONITEMICON
= wx
.TREE_HITTEST_ONITEMICON
210 # on the indent associated with an item.
211 TREE_HITTEST_ONITEMINDENT
= wx
.TREE_HITTEST_ONITEMINDENT
212 # on the label (string) associated with an item.
213 TREE_HITTEST_ONITEMLABEL
= wx
.TREE_HITTEST_ONITEMLABEL
214 # on the right of the label associated with an item.
215 TREE_HITTEST_ONITEMRIGHT
= wx
.TREE_HITTEST_ONITEMRIGHT
216 # on the label (string) associated with an item.
217 TREE_HITTEST_ONITEMSTATEICON
= wx
.TREE_HITTEST_ONITEMSTATEICON
218 # on the left of the CustomTreeCtrl.
219 TREE_HITTEST_TOLEFT
= wx
.TREE_HITTEST_TOLEFT
220 # on the right of the CustomTreeCtrl.
221 TREE_HITTEST_TORIGHT
= wx
.TREE_HITTEST_TORIGHT
222 # on the upper part (first half) of the item.
223 TREE_HITTEST_ONITEMUPPERPART
= wx
.TREE_HITTEST_ONITEMUPPERPART
224 # on the lower part (second half) of the item.
225 TREE_HITTEST_ONITEMLOWERPART
= wx
.TREE_HITTEST_ONITEMLOWERPART
226 # on the check icon, if present
227 TREE_HITTEST_ONITEMCHECKICON
= 0x4000
228 # anywhere on the item
229 TREE_HITTEST_ONITEM
= TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON
232 # Background Image Style
236 # Windows Vista Colours
237 _rgbSelectOuter
= wx
.Colour(170, 200, 245)
238 _rgbSelectInner
= wx
.Colour(230, 250, 250)
239 _rgbSelectTop
= wx
.Colour(210, 240, 250)
240 _rgbSelectBottom
= wx
.Colour(185, 215, 250)
241 _rgbNoFocusTop
= wx
.Colour(250, 250, 250)
242 _rgbNoFocusBottom
= wx
.Colour(235, 235, 235)
243 _rgbNoFocusOuter
= wx
.Colour(220, 220, 220)
244 _rgbNoFocusInner
= wx
.Colour(245, 245, 245)
246 # Flags for wx.RendererNative
247 _CONTROL_EXPANDED
= 8
248 _CONTROL_CURRENT
= 16
254 # ----------------------------------------------------------------------------
255 # CustomTreeCtrl events and binding for handling them
256 # ----------------------------------------------------------------------------
258 wxEVT_TREE_BEGIN_DRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_DRAG
259 wxEVT_TREE_BEGIN_RDRAG
= wx
.wxEVT_COMMAND_TREE_BEGIN_RDRAG
260 wxEVT_TREE_BEGIN_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
261 wxEVT_TREE_END_LABEL_EDIT
= wx
.wxEVT_COMMAND_TREE_END_LABEL_EDIT
262 wxEVT_TREE_DELETE_ITEM
= wx
.wxEVT_COMMAND_TREE_DELETE_ITEM
263 wxEVT_TREE_GET_INFO
= wx
.wxEVT_COMMAND_TREE_GET_INFO
264 wxEVT_TREE_SET_INFO
= wx
.wxEVT_COMMAND_TREE_SET_INFO
265 wxEVT_TREE_ITEM_EXPANDED
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDED
266 wxEVT_TREE_ITEM_EXPANDING
= wx
.wxEVT_COMMAND_TREE_ITEM_EXPANDING
267 wxEVT_TREE_ITEM_COLLAPSED
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
268 wxEVT_TREE_ITEM_COLLAPSING
= wx
.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
269 wxEVT_TREE_SEL_CHANGED
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGED
270 wxEVT_TREE_SEL_CHANGING
= wx
.wxEVT_COMMAND_TREE_SEL_CHANGING
271 wxEVT_TREE_KEY_DOWN
= wx
.wxEVT_COMMAND_TREE_KEY_DOWN
272 wxEVT_TREE_ITEM_ACTIVATED
= wx
.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
273 wxEVT_TREE_ITEM_RIGHT_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
274 wxEVT_TREE_ITEM_MIDDLE_CLICK
= wx
.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
275 wxEVT_TREE_END_DRAG
= wx
.wxEVT_COMMAND_TREE_END_DRAG
276 wxEVT_TREE_STATE_IMAGE_CLICK
= wx
.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
277 wxEVT_TREE_ITEM_GETTOOLTIP
= wx
.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
278 wxEVT_TREE_ITEM_MENU
= wx
.wxEVT_COMMAND_TREE_ITEM_MENU
279 wxEVT_TREE_ITEM_CHECKING
= wx
.NewEventType()
280 wxEVT_TREE_ITEM_CHECKED
= wx
.NewEventType()
281 wxEVT_TREE_ITEM_HYPERLINK
= wx
.NewEventType()
283 EVT_TREE_BEGIN_DRAG
= wx
.EVT_TREE_BEGIN_DRAG
284 EVT_TREE_BEGIN_RDRAG
= wx
.EVT_TREE_BEGIN_RDRAG
285 EVT_TREE_BEGIN_LABEL_EDIT
= wx
.EVT_TREE_BEGIN_LABEL_EDIT
286 EVT_TREE_END_LABEL_EDIT
= wx
.EVT_TREE_END_LABEL_EDIT
287 EVT_TREE_DELETE_ITEM
= wx
.EVT_TREE_DELETE_ITEM
288 EVT_TREE_GET_INFO
= wx
.EVT_TREE_GET_INFO
289 EVT_TREE_SET_INFO
= wx
.EVT_TREE_SET_INFO
290 EVT_TREE_ITEM_EXPANDED
= wx
.EVT_TREE_ITEM_EXPANDED
291 EVT_TREE_ITEM_EXPANDING
= wx
.EVT_TREE_ITEM_EXPANDING
292 EVT_TREE_ITEM_COLLAPSED
= wx
.EVT_TREE_ITEM_COLLAPSED
293 EVT_TREE_ITEM_COLLAPSING
= wx
.EVT_TREE_ITEM_COLLAPSING
294 EVT_TREE_SEL_CHANGED
= wx
.EVT_TREE_SEL_CHANGED
295 EVT_TREE_SEL_CHANGING
= wx
.EVT_TREE_SEL_CHANGING
296 EVT_TREE_KEY_DOWN
= wx
.EVT_TREE_KEY_DOWN
297 EVT_TREE_ITEM_ACTIVATED
= wx
.EVT_TREE_ITEM_ACTIVATED
298 EVT_TREE_ITEM_RIGHT_CLICK
= wx
.EVT_TREE_ITEM_RIGHT_CLICK
299 EVT_TREE_ITEM_MIDDLE_CLICK
= wx
.EVT_TREE_ITEM_MIDDLE_CLICK
300 EVT_TREE_END_DRAG
= wx
.EVT_TREE_END_DRAG
301 EVT_TREE_STATE_IMAGE_CLICK
= wx
.EVT_TREE_STATE_IMAGE_CLICK
302 EVT_TREE_ITEM_GETTOOLTIP
= wx
.EVT_TREE_ITEM_GETTOOLTIP
303 EVT_TREE_ITEM_MENU
= wx
.EVT_TREE_ITEM_MENU
304 EVT_TREE_ITEM_CHECKING
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKING
, 1)
305 EVT_TREE_ITEM_CHECKED
= wx
.PyEventBinder(wxEVT_TREE_ITEM_CHECKED
, 1)
306 EVT_TREE_ITEM_HYPERLINK
= wx
.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK
, 1)
309 def GetFlaggedData():
310 return zlib
.decompress(
311 'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
312 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
313 |\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\
314 \x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\
315 \xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\
316 \xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\
317 \xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\
318 \xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\
319 \r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\
320 \xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\
321 \xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\
322 \xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\
323 \x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\
324 \xb3\xb7H,a\xe1\xa9)\xdf<e$2Ble\x85\x94e\xb1\x96\xcep\xfb\xdd-D\x04\xa5\x14\
325 \xdeZ\'\xb1\x84\x85\xd8\x8bm\x84\xe6\x977\x7f8kog)\xba\xc4\xb7\xe5\xef$\xe2?\
326 \xe9\xa9\xbf\x86R\n\x11a&\x1c\xc1^lC|\r.\x02\xb3\x8b\x9b\xa6&G\x13W\xaa\xbb\
327 \x91_\x05\x0c\x1d\xbfI\xc7\xa1\x8e\xbf&a|:\x8c\xaf\xc1\x05J4\x8e\xd6>36\x192\
328 \xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\
329 \xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\
330 nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\
331 \xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\
332 \xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' )
334 def GetFlaggedBitmap():
335 return wx
.BitmapFromImage(GetFlaggedImage())
337 def GetFlaggedImage():
338 stream
= cStringIO
.StringIO(GetFlaggedData())
339 return wx
.ImageFromStream(stream
)
341 #----------------------------------------------------------------------
342 def GetNotFlaggedData():
343 return zlib
.decompress(
344 'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
345 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
346 |\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\
347 \x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\
348 \x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\
349 \x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\
350 \x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\
351 \xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\
352 \x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\
353 \xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\
354 \x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\
355 \xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\
356 \x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\
357 \x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\
358 \xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\
359 \xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\
360 \xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' )
362 def GetNotFlaggedBitmap():
363 return wx
.BitmapFromImage(GetNotFlaggedImage())
365 def GetNotFlaggedImage():
366 stream
= cStringIO
.StringIO(GetNotFlaggedData())
367 return wx
.ImageFromStream(stream
)
369 #----------------------------------------------------------------------
370 def GetCheckedData():
371 return zlib
.decompress(
372 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
373 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\
374 \x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\
375 \xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\
376 \x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\
377 \xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\
378 \xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\
379 \xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\
380 \x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\
381 \xeb\x9c\x12\x9a\x00\x92\xccS\x9f" )
383 def GetCheckedBitmap():
384 return wx
.BitmapFromImage(GetCheckedImage())
386 def GetCheckedImage():
387 stream
= cStringIO
.StringIO(GetCheckedData())
388 return wx
.ImageFromStream(stream
)
390 #----------------------------------------------------------------------
391 def GetNotCheckedData():
392 return zlib
.decompress(
393 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
394 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\
395 \xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\
396 \xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\
397 \x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\
398 \xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\
399 \xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\
402 def GetNotCheckedBitmap():
403 return wx
.BitmapFromImage(GetNotCheckedImage())
405 def GetNotCheckedImage():
406 stream
= cStringIO
.StringIO(GetNotCheckedData())
407 return wx
.ImageFromStream(stream
)
410 def GrayOut(anImage
):
412 Convert the given image (in place) to a grayed-out version,
413 appropriate for a 'disabled' appearance.
416 factor
= 0.7 # 0 < f < 1. Higher Is Grayer
418 if anImage
.HasMask():
419 maskColor
= (anImage
.GetMaskRed(), anImage
.GetMaskGreen(), anImage
.GetMaskBlue())
423 data
= map(ord, list(anImage
.GetData()))
425 for i
in range(0, len(data
), 3):
427 pixel
= (data
[i
], data
[i
+1], data
[i
+2])
428 pixel
= MakeGray(pixel
, factor
, maskColor
)
433 anImage
.SetData(''.join(map(chr, data
)))
438 def MakeGray((r
,g
,b
), factor
, maskColor
):
440 Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be
444 if (r
,g
,b
) != maskColor
:
445 return map(lambda x
: int((230 - x
) * factor
) + x
, (r
,g
,b
))
450 def DrawTreeItemButton(win
, dc
, rect
, flags
):
451 """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """
454 dc
.SetPen(wx
.GREY_PEN
)
455 dc
.SetBrush(wx
.WHITE_BRUSH
)
456 dc
.DrawRectangleRect(rect
)
459 xMiddle
= rect
.x
+ rect
.width
/2
460 yMiddle
= rect
.y
+ rect
.height
/2
462 # half of the length of the horz lines in "-" and "+"
463 halfWidth
= rect
.width
/2 - 2
464 dc
.SetPen(wx
.BLACK_PEN
)
465 dc
.DrawLine(xMiddle
- halfWidth
, yMiddle
,
466 xMiddle
+ halfWidth
+ 1, yMiddle
)
468 if not flags
& _CONTROL_EXPANDED
:
471 halfHeight
= rect
.height
/2 - 2
472 dc
.DrawLine(xMiddle
, yMiddle
- halfHeight
,
473 xMiddle
, yMiddle
+ halfHeight
+ 1)
476 #---------------------------------------------------------------------------
477 # DragImage Implementation
478 # This Class Handles The Creation Of A Custom Image In Case Of Item Drag
480 #---------------------------------------------------------------------------
482 class DragImage(wx
.DragImage
):
484 This class handles the creation of a custom image in case of item drag
488 def __init__(self
, treeCtrl
, item
):
490 Default class constructor.
491 For internal use: do not call it in your code!
494 text
= item
.GetText()
495 font
= item
.Attr().GetFont()
496 colour
= item
.Attr().GetTextColour()
500 font
= treeCtrl
._normalFont
502 backcolour
= treeCtrl
.GetBackgroundColour()
503 r
, g
, b
= int(backcolour
.Red()), int(backcolour
.Green()), int(backcolour
.Blue())
504 backcolour
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
505 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
506 self
._backgroundColour
= backcolour
508 tempdc
= wx
.ClientDC(treeCtrl
)
510 width
, height
, dummy
= tempdc
.GetMultiLineTextExtent(text
+ "M")
512 image
= item
.GetCurrentImage()
514 image_w
, image_h
= 0, 0
515 wcheck
, hcheck
= 0, 0
523 if image
!= _NO_IMAGE
:
524 if treeCtrl
._imageListNormal
:
525 image_w
, image_h
= treeCtrl
._imageListNormal
.GetSize(image
)
527 itemimage
= treeCtrl
._imageListNormal
.GetBitmap(image
)
529 checkimage
= item
.GetCurrentCheckedImage()
531 if checkimage
is not None:
532 if treeCtrl
._imageListCheck
:
533 wcheck
, hcheck
= treeCtrl
._imageListCheck
.GetSize(checkimage
)
535 itemcheck
= treeCtrl
._imageListCheck
.GetBitmap(checkimage
)
537 total_h
= max(hcheck
, height
)
538 total_h
= max(image_h
, total_h
)
542 yimagepos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0]
544 if checkimage
is not None:
546 ycheckpos
= ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0] + 2
548 extraH
= ((total_h
> height
) and [(total_h
- height
)/2] or [0])[0]
550 xtextpos
= wcheck
+ image_w
553 total_h
= max(image_h
, hcheck
)
554 total_h
= max(total_h
, height
)
557 total_h
+= 2 # at least 2 pixels
559 total_h
+= total_h
/10 # otherwise 10% extra spacing
561 total_w
= image_w
+ wcheck
+ width
563 self
._total
_w
= total_w
564 self
._total
_h
= total_h
565 self
._itemimage
= itemimage
566 self
._itemcheck
= itemcheck
568 self
._colour
= colour
570 self
._xtextpos
= xtextpos
571 self
._ytextpos
= ytextpos
572 self
._ximagepos
= ximagepos
573 self
._yimagepos
= yimagepos
574 self
._xcheckpos
= xcheckpos
575 self
._ycheckpos
= ycheckpos
576 self
._textwidth
= width
577 self
._textheight
= height
578 self
._extraH
= extraH
580 self
._bitmap
= self
.CreateBitmap()
582 wx
.DragImage
.__init
__(self
, self
._bitmap
)
585 def CreateBitmap(self
):
586 """Actually creates the dnd bitmap."""
588 memory
= wx
.MemoryDC()
590 bitmap
= wx
.EmptyBitmap(self
._total
_w
, self
._total
_h
)
591 memory
.SelectObject(bitmap
)
593 memory
.SetTextBackground(self
._backgroundColour
)
594 memory
.SetBackground(wx
.Brush(self
._backgroundColour
))
595 memory
.SetFont(self
._font
)
596 memory
.SetTextForeground(self
._colour
)
600 memory
.DrawBitmap(self
._itemimage
, self
._ximagepos
, self
._yimagepos
, True)
603 memory
.DrawBitmap(self
._itemcheck
, self
._xcheckpos
, self
._ycheckpos
, True)
605 textrect
= wx
.Rect(self
._xtextpos
, self
._ytextpos
+self
._extraH
, self
._textwidth
, self
._textheight
)
606 memory
.DrawLabel(self
._text
, textrect
)
608 memory
.SelectObject(wx
.NullBitmap
)
613 # ----------------------------------------------------------------------------
614 # TreeItemAttr: a structure containing the visual attributes of an item
615 # ----------------------------------------------------------------------------
618 """Creates the item attributes (text colour, background colour and font)."""
620 def __init__(self
, colText
=wx
.NullColour
, colBack
=wx
.NullColour
, font
=wx
.NullFont
):
622 Default class constructor.
623 For internal use: do not call it in your code!
626 self
._colText
= colText
627 self
._colBack
= colBack
631 def SetTextColour(self
, colText
):
632 """Sets the attribute text colour."""
634 self
._colText
= colText
637 def SetBackgroundColour(self
, colBack
):
638 """Sets the attribute background colour."""
640 self
._colBack
= colBack
643 def SetFont(self
, font
):
644 """Sets the attribute font."""
650 def HasTextColour(self
):
651 """Returns whether the attribute has text colour."""
653 return self
._colText
!= wx
.NullColour
656 def HasBackgroundColour(self
):
657 """Returns whether the attribute has background colour."""
659 return self
._colBack
!= wx
.NullColour
663 """Returns whether the attribute has font."""
665 return self
._font
!= wx
.NullFont
669 def GetTextColour(self
):
670 """Returns the attribute text colour."""
675 def GetBackgroundColour(self
):
676 """Returns the attribute background colour."""
682 """Returns the attribute font."""
687 # ----------------------------------------------------------------------------
688 # CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent
690 # NB: Note That Not All The Accessors Make Sense For All The Events, See The
691 # Event Description Below.
692 # ----------------------------------------------------------------------------
694 class CommandTreeEvent(wx
.PyCommandEvent
):
696 CommandTreeEvent is a special subclassing of wx.PyCommandEvent.
697 NB: note that not all the accessors make sense for all the events, see the
698 event description for every method in this class.
701 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
702 label
=None, **kwargs
):
704 Default class constructor.
705 For internal use: do not call it in your code!
708 wx
.PyCommandEvent
.__init
__(self
, type, id, **kwargs
)
710 self
._evtKey
= evtKey
711 self
._pointDrag
= point
717 Gets the item on which the operation was performed or the newly selected
718 item for EVT_TREE_SEL_CHANGED/ING events.
724 def SetItem(self
, item
):
726 Sets the item on which the operation was performed or the newly selected
727 item for EVT_TREE_SEL_CHANGED/ING events.
733 def GetOldItem(self
):
734 """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item."""
739 def SetOldItem(self
, item
):
740 """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item."""
747 Returns the point where the mouse was when the drag operation started
748 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
751 return self
._pointDrag
754 def SetPoint(self
, pt
):
756 Sets the point where the mouse was when the drag operation started
757 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
763 def GetKeyEvent(self
):
764 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
769 def GetKeyCode(self
):
770 """Returns the integer key code (for EVT_TREE_KEY_DOWN only)."""
772 return self
._evtKey
.GetKeyCode()
775 def SetKeyEvent(self
, evt
):
776 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
782 """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
787 def SetLabel(self
, label
):
788 """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
793 def IsEditCancelled(self
):
794 """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
796 return self
._editCancelled
799 def SetEditCanceled(self
, editCancelled
):
800 """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
802 self
._editCancelled
= editCancelled
805 def SetToolTip(self
, toolTip
):
806 """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
808 self
._label
= toolTip
811 def GetToolTip(self
):
812 """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
817 # ----------------------------------------------------------------------------
818 # TreeEvent is a special class for all events associated with tree controls
820 # NB: note that not all accessors make sense for all events, see the event
822 # ----------------------------------------------------------------------------
824 class TreeEvent(CommandTreeEvent
):
826 def __init__(self
, type, id, item
=None, evtKey
=None, point
=None,
827 label
=None, **kwargs
):
829 Default class constructor.
830 For internal use: do not call it in your code!
833 CommandTreeEvent
.__init
__(self
, type, id, item
, evtKey
, point
, label
, **kwargs
)
834 self
.notify
= wx
.NotifyEvent(type, id)
837 def GetNotifyEvent(self
):
838 """Returns the actual wx.NotifyEvent."""
844 """Returns whether the event is allowed or not."""
846 return self
.notify
.IsAllowed()
850 """Vetos the event."""
856 """The event is allowed."""
861 # -----------------------------------------------------------------------------
862 # Auxiliary Classes: TreeRenameTimer
863 # -----------------------------------------------------------------------------
865 class TreeRenameTimer(wx
.Timer
):
866 """Timer used for enabling in-place edit."""
868 def __init__(self
, owner
):
870 Default class constructor.
871 For internal use: do not call it in your code!
874 wx
.Timer
.__init
__(self
)
879 """The timer has expired."""
881 self
._owner
.OnRenameTimer()
884 # -----------------------------------------------------------------------------
885 # Auxiliary Classes: TreeTextCtrl
886 # This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item
887 # -----------------------------------------------------------------------------
889 class TreeTextCtrl(wx
.TextCtrl
):
890 """Control used for in-place edit."""
892 def __init__(self
, owner
, item
=None):
894 Default class constructor.
895 For internal use: do not call it in your code!
899 self
._itemEdited
= item
900 self
._startValue
= item
.GetText()
901 self
._finished
= False
902 self
._aboutToFinish
= False
904 w
= self
._itemEdited
.GetWidth()
905 h
= self
._itemEdited
.GetHeight()
907 wnd
= self
._itemEdited
.GetWindow()
909 w
= w
- self
._itemEdited
.GetWindowSize()[0]
912 x
, y
= self
._owner
.CalcScrolledPosition(item
.GetX(), item
.GetY())
917 image
= item
.GetCurrentImage()
919 if image
!= _NO_IMAGE
:
921 if self
._owner
._imageListNormal
:
922 image_w
, image_h
= self
._owner
._imageListNormal
.GetSize(image
)
927 raise Exception("\n ERROR: You Must Create An Image List To Use Images!")
929 checkimage
= item
.GetCurrentCheckedImage()
931 if checkimage
is not None:
932 wcheck
, hcheck
= self
._owner
._imageListCheck
.GetSize(checkimage
)
938 h
= max(hcheck
, image_h
)
939 dc
= wx
.ClientDC(self
._owner
)
940 h
= max(h
, dc
.GetTextExtent("Aq")[1])
943 # FIXME: what are all these hardcoded 4, 8 and 11s really?
944 x
+= image_w
+ wcheck
945 w
-= image_w
+ 4 + wcheck
947 wx
.TextCtrl
.__init
__(self
, self
._owner
, wx
.ID_ANY
, self
._startValue
,
948 wx
.Point(x
- 4, y
), wx
.Size(w
+ 15, h
))
949 if wx
.Platform
== "__WXMAC__":
950 self
.SetFont(owner
.GetFont())
951 bs
= self
.GetBestSize()
952 self
.SetSize((-1, bs
.height
))
954 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
955 self
.Bind(wx
.EVT_KEY_UP
, self
.OnKeyUp
)
956 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
959 def AcceptChanges(self
):
960 """Accepts/refuses the changes made by the user."""
962 value
= self
.GetValue()
964 if value
== self
._startValue
:
965 # nothing changed, always accept
966 # when an item remains unchanged, the owner
967 # needs to be notified that the user decided
968 # not to change the tree item label, and that
969 # the edit has been cancelled
970 self
._owner
.OnRenameCancelled(self
._itemEdited
)
973 if not self
._owner
.OnRenameAccept(self
._itemEdited
, value
):
977 # accepted, do rename the item
978 self
._owner
.SetItemText(self
._itemEdited
, value
)
984 """Finish editing."""
986 if not self
._finished
:
988 ## wxPendingDelete.Append(this)
989 self
._finished
= True
990 self
._owner
.SetFocusIgnoringChildren()
991 self
._owner
.ResetTextControl()
994 def OnChar(self
, event
):
995 """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
997 keycode
= event
.GetKeyCode()
999 if keycode
== wx
.WXK_RETURN
:
1000 self
._aboutToFinish
= True
1001 # Notify the owner about the changes
1002 self
.AcceptChanges()
1003 # Even if vetoed, close the control (consistent with MSW)
1004 wx
.CallAfter(self
.Finish
)
1006 elif keycode
== wx
.WXK_ESCAPE
:
1013 def OnKeyUp(self
, event
):
1014 """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
1016 if not self
._finished
:
1018 # auto-grow the textctrl:
1019 parentSize
= self
._owner
.GetSize()
1020 myPos
= self
.GetPosition()
1021 mySize
= self
.GetSize()
1023 sx
, sy
= self
.GetTextExtent(self
.GetValue() + "M")
1024 if myPos
.x
+ sx
> parentSize
.x
:
1025 sx
= parentSize
.x
- myPos
.x
1029 self
.SetSize((sx
, -1))
1034 def OnKillFocus(self
, event
):
1035 """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
1037 # I commented out those lines, and everything seems to work fine.
1038 # But why in the world are these lines of code here? Maybe GTK
1039 # or MAC give troubles?
1041 ## if not self._finished and not self._aboutToFinish:
1043 ## # We must finish regardless of success, otherwise we'll get
1044 ## # focus problems:
1046 ## if not self.AcceptChanges():
1047 ## self._owner.OnRenameCancelled(self._itemEdited)
1049 # We must let the native text control handle focus, too, otherwise
1050 # it could have problems with the cursor (e.g., in wxGTK).
1054 def StopEditing(self
):
1055 """Suddenly stops the editing."""
1057 self
._owner
.OnRenameCancelled(self
._itemEdited
)
1062 """Returns the item currently edited."""
1064 return self
._itemEdited
1067 # -----------------------------------------------------------------------------
1068 # Auxiliary Classes: TreeFindTimer
1069 # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
1070 # Sufficiently Long Time.
1071 # -----------------------------------------------------------------------------
1073 class TreeFindTimer(wx
.Timer
):
1075 Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
1076 for a sufficiently long time.
1079 def __init__(self
, owner
):
1081 Default class constructor.
1082 For internal use: do not call it in your code!
1085 wx
.Timer
.__init
__(self
)
1090 """The timer has expired."""
1092 self
._owner
._findPrefix
= ""
1095 # -----------------------------------------------------------------------------
1096 # GenericTreeItem Implementation.
1097 # This Class Holds All The Information And Methods For Every Single Item In
1099 # -----------------------------------------------------------------------------
1101 class GenericTreeItem
:
1103 This class holds all the information and methods for every single item in
1104 CustomTreeCtrl. No wx based.
1107 def __init__(self
, parent
, text
="", ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
1109 Default class constructor.
1110 For internal use: do not call it in your code!
1113 # since there can be very many of these, we save size by chosing
1114 # the smallest representation for the elements and by ordering
1115 # the members to avoid padding.
1116 self
._text
= text
# label to be rendered for item
1117 self
._data
= data
# user-provided data
1119 self
._children
= [] # list of children
1120 self
._parent
= parent
# parent of this item
1122 self
._attr
= None # attributes???
1124 # tree ctrl images for the normal, selected, expanded and
1125 # expanded+selected states
1126 self
._images
= [-1, -1, -1, -1]
1127 self
._images
[TreeItemIcon_Normal
] = image
1128 self
._images
[TreeItemIcon_Selected
] = selImage
1129 self
._images
[TreeItemIcon_Expanded
] = _NO_IMAGE
1130 self
._images
[TreeItemIcon_SelectedExpanded
] = _NO_IMAGE
1132 self
._checkedimages
= [None, None, None, None]
1134 self
._x
= 0 # (virtual) offset from top
1135 self
._y
= 0 # (virtual) offset from left
1136 self
._width
= 0 # width of this item
1137 self
._height
= 0 # height of this item
1139 self
._isCollapsed
= True
1140 self
._hasHilight
= False # same as focused
1141 self
._hasPlus
= False # used for item which doesn't have
1142 # children but has a [+] button
1143 self
._isBold
= False # render the label in bold font
1144 self
._isItalic
= False # render the label in italic font
1145 self
._ownsAttr
= False # delete attribute when done
1146 self
._type
= ct_type
# item type: 0=normal, 1=check, 2=radio
1147 self
._checked
= False # only meaningful for check and radio
1148 self
._enabled
= True # flag to enable/disable an item
1149 self
._hypertext
= False # indicates if the item is hypertext
1150 self
._visited
= False # visited state for an hypertext item
1153 # do not construct the array for normal items
1154 self
._checkedimages
[TreeItemIcon_Checked
] = 0
1155 self
._checkedimages
[TreeItemIcon_NotChecked
] = 1
1156 self
._checkedimages
[TreeItemIcon_Flagged
] = 2
1157 self
._checkedimages
[TreeItemIcon_NotFlagged
] = 3
1160 if parent
.GetType() == 2 and not parent
.IsChecked():
1161 # if the node parent is a radio not enabled, we are disabled
1162 self
._enabled
= False
1164 self
._wnd
= wnd
# are we holding a window?
1167 if wnd
.GetSizer(): # the window is a complex one hold by a sizer
1168 size
= wnd
.GetBestSize()
1169 else: # simple window, without sizers
1170 size
= wnd
.GetSize()
1172 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1173 # No other solution to handle the focus changing from an item in
1174 # CustomTreeCtrl and the window associated to an item
1175 # Do better strategies exist?
1176 self
._wnd
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1178 self
._height
= size
.GetHeight() + 2
1179 self
._width
= size
.GetWidth()
1180 self
._windowsize
= size
1182 # We don't show the window if the item is collapsed
1183 if self
._isCollapsed
:
1184 self
._wnd
.Show(False)
1186 # The window is enabled only if the item is enabled
1187 self
._wnd
.Enable(self
._enabled
)
1188 self
._windowenabled
= self
._enabled
1193 Returns whether the item is ok or not. Useless on Python, but added for
1194 backward compatibility with the C++ implementation.
1200 def GetChildren(self
):
1201 """Returns the item's children."""
1203 return self
._children
1207 """Returns the item text."""
1212 def GetImage(self
, which
=TreeItemIcon_Normal
):
1213 """Returns the item image for a particular state."""
1215 return self
._images
[which
]
1218 def GetCheckedImage(self
, which
=TreeItemIcon_Checked
):
1219 """Returns the item check image. Meaningful only for radio & check items."""
1221 return self
._checkedimages
[which
]
1225 """Returns the data associated to this item."""
1230 def SetImage(self
, image
, which
):
1231 """Sets the item image."""
1233 self
._images
[which
] = image
1236 def SetData(self
, data
):
1237 """Sets the data associated to this item."""
1242 def SetHasPlus(self
, has
=True):
1243 """Sets whether an item has the 'plus' button."""
1248 def SetBold(self
, bold
):
1249 """Sets the item font bold."""
1254 def SetItalic(self
, italic
):
1255 """Sets the item font italic."""
1257 self
._isItalic
= italic
1261 """Returns the x position on an item in the ScrolledWindow."""
1267 """Returns the y position on an item in the ScrolledWindow."""
1273 """Sets the x position on an item in the ScrolledWindow."""
1279 """Sets the y position on an item in the ScrolledWindow."""
1284 def GetHeight(self
):
1285 """Returns the height of the item."""
1291 """Returns the width of the item."""
1296 def SetHeight(self
, h
):
1297 """Sets the height of the item."""
1302 def SetWidth(self
, w
):
1303 """Sets the width of the item."""
1308 def SetWindow(self
, wnd
):
1309 """Sets the window associated to the item."""
1314 def GetWindow(self
):
1315 """Returns the window associated to the item."""
1320 def GetWindowEnabled(self
):
1321 """Returns whether the associated window is enabled or not."""
1324 raise Exception("\nERROR: This Item Has No Window Associated")
1326 return self
._windowenabled
1329 def SetWindowEnabled(self
, enable
=True):
1330 """Sets whether the associated window is enabled or not."""
1333 raise Exception("\nERROR: This Item Has No Window Associated")
1335 self
._windowenabled
= enable
1336 self
._wnd
.Enable(enable
)
1339 def GetWindowSize(self
):
1340 """Returns the associated window size."""
1342 return self
._windowsize
1345 def OnSetFocus(self
, event
):
1346 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1348 treectrl
= self
._wnd
.GetParent()
1349 select
= treectrl
.GetSelection()
1351 # If the window is associated to an item that currently is selected
1352 # (has focus) we don't kill the focus. Otherwise we do it.
1354 treectrl
._hasFocus
= False
1356 treectrl
._hasFocus
= True
1363 Returns the item type. It should be one of:
1372 def SetHyperText(self
, hyper
=True):
1373 """Sets whether the item is hypertext or not."""
1375 self
._hypertext
= hyper
1378 def SetVisited(self
, visited
=True):
1379 """Sets whether an hypertext item was visited or not."""
1381 self
._visited
= visited
1384 def GetVisited(self
):
1385 """Returns whether an hypertext item was visited or not."""
1387 return self
._visited
1390 def IsHyperText(self
):
1391 """Returns whether the item is hypetext or not."""
1393 return self
._hypertext
1396 def GetParent(self
):
1397 """Gets the item parent."""
1402 def Insert(self
, child
, index
):
1403 """Inserts an item in the item children."""
1405 self
._children
.insert(index
, child
)
1409 """Expand the item."""
1411 self
._isCollapsed
= False
1415 """Collapse the item."""
1417 self
._isCollapsed
= True
1420 def SetHilight(self
, set=True):
1421 """Sets the item focus/unfocus."""
1423 self
._hasHilight
= set
1426 def HasChildren(self
):
1427 """Returns whether the item has children or not."""
1429 return len(self
._children
) > 0
1432 def IsSelected(self
):
1433 """Returns whether the item is selected or not."""
1435 return self
._hasHilight
!= 0
1438 def IsExpanded(self
):
1439 """Returns whether the item is expanded or not."""
1441 return not self
._isCollapsed
1444 def IsChecked(self
):
1445 """Returns whether the item is checked or not."""
1447 return self
._checked
1450 def Check(self
, checked
=True):
1451 """Check an item. Meaningful only for check and radio items."""
1453 self
._checked
= checked
1457 """Returns whether the item has the plus button or not."""
1459 return self
._hasPlus
or self
.HasChildren()
1463 """Returns whether the item font is bold or not."""
1465 return self
._isBold
!= 0
1469 """Returns whether the item font is italic or not."""
1471 return self
._isItalic
!= 0
1474 def Enable(self
, enable
=True):
1475 """Enables/disables the item."""
1477 self
._enabled
= enable
1480 def IsEnabled(self
):
1481 """Returns whether the item is enabled or not."""
1483 return self
._enabled
1486 def GetAttributes(self
):
1487 """Returns the item attributes (font, colours)."""
1493 """Creates a new attribute (font, colours)."""
1497 self
._attr
= TreeItemAttr()
1498 self
._ownsAttr
= True
1503 def SetAttributes(self
, attr
):
1504 """Sets the item attributes (font, colours)."""
1510 self
._ownsAttr
= False
1513 def AssignAttributes(self
, attr
):
1514 """Assigns the item attributes (font, colours)."""
1516 self
.SetAttributes(attr
)
1517 self
._ownsAttr
= True
1520 def DeleteChildren(self
, tree
):
1521 """Deletes the item children."""
1523 for child
in self
._children
:
1525 tree
.SendDeleteEvent(child
)
1527 child
.DeleteChildren(tree
)
1529 if child
== tree
._select
_me
:
1530 tree
._select
_me
= None
1532 # We have to destroy the associated window
1533 wnd
= child
.GetWindow()
1538 if child
in tree
._itemWithWindow
:
1539 tree
._itemWithWindow
.remove(child
)
1546 def SetText(self
, text
):
1547 """Sets the item text."""
1552 def GetChildrenCount(self
, recursively
=True):
1553 """Gets the number of children."""
1555 count
= len(self
._children
)
1562 for n
in xrange(count
):
1563 total
+= self
._children
[n
].GetChildrenCount()
1568 def GetSize(self
, x
, y
, theButton
):
1569 """Returns the item size."""
1571 bottomY
= self
._y
+ theButton
.GetLineHeight(self
)
1576 width
= self
._x
+ self
._width
1581 if self
.IsExpanded():
1582 for child
in self
._children
:
1583 x
, y
= child
.GetSize(x
, y
, theButton
)
1588 def HitTest(self
, point
, theCtrl
, flags
=0, level
=0):
1590 HitTest method for an item. Called from the main window HitTest.
1591 see the CustomTreeCtrl HitTest method for the flags explanation.
1594 # for a hidden root node, don't evaluate it, but do evaluate children
1595 if not (level
== 0 and theCtrl
.HasFlag(TR_HIDE_ROOT
)):
1598 h
= theCtrl
.GetLineHeight(self
)
1600 if point
.y
> self
._y
and point
.y
< self
._y
+ h
:
1602 y_mid
= self
._y
+ h
/2
1605 flags |
= TREE_HITTEST_ONITEMUPPERPART
1607 flags |
= TREE_HITTEST_ONITEMLOWERPART
1609 xCross
= self
._x
- theCtrl
.GetSpacing()
1611 if wx
.Platform
== "__WXMAC__":
1612 # according to the drawing code the triangels are drawn
1613 # at -4 , -4 from the position up to +10/+10 max
1614 if point
.x
> xCross
-4 and point
.x
< xCross
+10 and point
.y
> y_mid
-4 and \
1615 point
.y
< y_mid
+10 and self
.HasPlus() and theCtrl
.HasButtons():
1617 flags |
= TREE_HITTEST_ONITEMBUTTON
1620 # 5 is the size of the plus sign
1621 if point
.x
> xCross
-6 and point
.x
< xCross
+6 and point
.y
> y_mid
-6 and \
1622 point
.y
< y_mid
+6 and self
.HasPlus() and theCtrl
.HasButtons():
1624 flags |
= TREE_HITTEST_ONITEMBUTTON
1627 if point
.x
>= self
._x
and point
.x
<= self
._x
+ self
._width
:
1632 # assuming every image (normal and selected) has the same size!
1633 if self
.GetImage() != _NO_IMAGE
and theCtrl
._imageListNormal
:
1634 image_w
, image_h
= theCtrl
._imageListNormal
.GetSize(self
.GetImage())
1636 if self
.GetCheckedImage() is not None:
1637 wcheck
, hcheck
= theCtrl
._imageListCheck
.GetSize(self
.GetCheckedImage())
1639 if wcheck
and point
.x
<= self
._x
+ wcheck
+ 1:
1640 flags |
= TREE_HITTEST_ONITEMCHECKICON
1643 if image_w
!= -1 and point
.x
<= self
._x
+ wcheck
+ image_w
+ 1:
1644 flags |
= TREE_HITTEST_ONITEMICON
1646 flags |
= TREE_HITTEST_ONITEMLABEL
1650 if point
.x
< self
._x
:
1651 flags |
= TREE_HITTEST_ONITEMINDENT
1652 if point
.x
> self
._x
+ self
._width
:
1653 flags |
= TREE_HITTEST_ONITEMRIGHT
1657 # if children are expanded, fall through to evaluate them
1658 if self
._isCollapsed
:
1662 for child
in self
._children
:
1663 res
, flags
= child
.HitTest(point
, theCtrl
, flags
, level
+ 1)
1670 def GetCurrentImage(self
):
1671 """Returns the current item image."""
1675 if self
.IsExpanded():
1677 if self
.IsSelected():
1679 image
= self
.GetImage(TreeItemIcon_SelectedExpanded
)
1681 if image
== _NO_IMAGE
:
1683 # we usually fall back to the normal item, but try just the
1684 # expanded one (and not selected) first in this case
1685 image
= self
.GetImage(TreeItemIcon_Expanded
)
1687 else: # not expanded
1689 if self
.IsSelected():
1690 image
= self
.GetImage(TreeItemIcon_Selected
)
1692 # maybe it doesn't have the specific image we want,
1693 # try the default one instead
1694 if image
== _NO_IMAGE
:
1695 image
= self
.GetImage()
1700 def GetCurrentCheckedImage(self
):
1701 """Returns the current item check image."""
1706 if self
.IsChecked():
1707 if self
._type
== 1: # Checkbox
1708 return self
._checkedimages
[TreeItemIcon_Checked
]
1710 return self
._checkedimages
[TreeItemIcon_Flagged
]
1712 if self
._type
== 1: # Checkbox
1713 return self
._checkedimages
[TreeItemIcon_NotChecked
]
1715 return self
._checkedimages
[TreeItemIcon_NotFlagged
]
1718 def EventFlagsToSelType(style
, shiftDown
=False, ctrlDown
=False):
1720 Translate the key or mouse event flag to the type of selection we
1724 is_multiple
= (style
& TR_MULTIPLE
) != 0
1725 extended_select
= shiftDown
and is_multiple
1726 unselect_others
= not (extended_select
or (ctrlDown
and is_multiple
))
1728 return is_multiple
, extended_select
, unselect_others
1731 # -----------------------------------------------------------------------------
1732 # CustomTreeCtrl Main Implementation.
1733 # This Is The Main Class.
1734 # -----------------------------------------------------------------------------
1736 class CustomTreeCtrl(wx
.PyScrolledWindow
):
1738 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1739 style
=TR_DEFAULT_STYLE
, ctstyle
=0, 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 + CustomTreeCtrl window style. This can be one of:
1755 TR_HAS_BUTTONS # draw collapsed/expanded btns
1756 TR_NO_LINES # don't draw lines at all
1757 TR_LINES_AT_ROOT # connect top-level nodes
1758 TR_TWIST_BUTTONS # draw mac-like twist buttons
1759 TR_SINGLE # single selection mode
1760 TR_MULTIPLE # can select multiple items
1761 TR_EXTENDED # todo: allow extended selection
1762 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1763 TR_EDIT_LABELS # can edit item labels
1764 TR_ROW_LINES # put border around items
1765 TR_HIDE_ROOT # don't display root node
1766 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1767 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1768 TR_AUTO_CHECK_PARENT # only meaningful for checkboxes
1769 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1771 ctstyle: kept for backward compatibility.
1773 validator: window validator.
1778 style
= style | ctstyle
1780 self
._current
= self
._key
_current
= self
._anchor
= self
._select
_me
= None
1781 self
._hasFocus
= False
1784 # Default line height: it will soon be changed
1785 self
._lineHeight
= 10
1786 # Item indent wrt parent
1788 # item horizontal spacing between the start and the text
1791 # Brushes for focused/unfocused items (also gradient type)
1792 self
._hilightBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
))
1793 btnshadow
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1794 self
._hilightUnfocusedBrush
= wx
.Brush(btnshadow
)
1795 r
, g
, b
= btnshadow
.Red(), btnshadow
.Green(), btnshadow
.Blue()
1796 backcolour
= (max((r
>> 1) - 20, 0),
1797 max((g
>> 1) - 20, 0),
1798 max((b
>> 1) - 20, 0))
1799 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1800 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1802 # image list for icons
1803 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1804 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1806 # Drag and drop initial settings
1809 self
._isDragging
= False
1810 self
._dropTarget
= self
._oldSelection
= None
1811 self
._dragImage
= None
1812 self
._underMouse
= None
1814 # TextCtrl initial settings for editable items
1815 self
._textCtrl
= None
1816 self
._renameTimer
= None
1818 # This one allows us to handle Freeze() and Thaw() calls
1819 self
._freezeCount
= 0
1821 self
._findPrefix
= ""
1822 self
._findTimer
= None
1824 self
._dropEffectAboveItem
= False
1825 self
._lastOnSame
= False
1827 # Default normal and bold fonts for an item
1828 self
._hasFont
= True
1829 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1830 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1831 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1832 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1836 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1837 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1838 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1839 self
._hypertextnewcolour
= wx
.BLUE
1840 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1841 self
._isonhyperlink
= False
1843 # Default CustomTreeCtrl background colour.
1844 self
._backgroundColour
= wx
.WHITE
1846 # Background image settings
1847 self
._backgroundImage
= None
1848 self
._imageStretchStyle
= _StyleTile
1850 # Disabled items colour
1851 self
._disabledColour
= wx
.Colour(180, 180, 180)
1853 # Gradient selection colours
1854 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1855 self
._secondcolour
= wx
.WHITE
1856 self
._usegradients
= False
1857 self
._gradientstyle
= 0 # Horizontal Gradient
1859 # Vista Selection Styles
1860 self
._vistaselection
= False
1862 # Connection lines style
1863 if wx
.Platform
!= "__WXMAC__":
1864 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1865 self
._dottedPen
.SetDashes([1,1])
1866 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1868 self
._dottedPen
= wx
.Pen("grey", 1)
1870 # Pen Used To Draw The Border Around Selected Items
1871 self
._borderPen
= wx
.BLACK_PEN
1872 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1874 # For Appended Windows
1875 self
._hasWindows
= False
1876 self
._itemWithWindow
= []
1878 if wx
.Platform
== "__WXMAC__":
1879 style
&= ~TR_LINES_AT_ROOT
1880 style |
= TR_NO_LINES
1882 platform
, major
, minor
= wx
.GetOsVersion()
1884 style |
= TR_ROW_LINES
1886 self
._windowStyle
= style
1888 # Create the default check image list
1889 self
.SetImageListCheck(13, 13)
1891 # A constant to use my translation of RendererNative.DrawTreeItemButton
1892 # if the wxPython version is less than 2.6.2.1.
1893 if wx
.VERSION_STRING
< "2.6.2.1":
1894 self
._drawingfunction
= DrawTreeItemButton
1896 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1898 # Create our container... at last!
1899 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1901 # If the tree display has no buttons, but does have
1902 # connecting lines, we can use a narrower layout.
1903 # It may not be a good idea to force this...
1904 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1908 self
.SetValidator(validator
)
1910 attr
= self
.GetDefaultAttributes()
1911 self
.SetOwnForegroundColour(attr
.colFg
)
1912 self
.SetOwnBackgroundColour(wx
.WHITE
)
1914 if not self
._hasFont
:
1915 self
.SetOwnFont(attr
.font
)
1920 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1921 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1922 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1923 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1924 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1925 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1926 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1927 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1929 # Sets the focus to ourselves: this is useful if you have items
1930 # with associated widgets.
1934 def AcceptsFocus(self
):
1935 # overridden base class method, allows this ctrl to
1936 # participate in the tab-order, etc. It's overridable because
1937 # of deriving this class from wx.PyScrolledWindow...
1941 def OnDestroy(self
, event
):
1942 """Handles the wx.EVT_WINDOW_DESTROY event."""
1944 # Here there may be something I miss... do I have to destroy
1946 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1947 self
._renameTimer
.Stop()
1948 del self
._renameTimer
1950 if self
._findTimer
and self
._findTimer
.IsRunning():
1951 self
._findTimer
.Stop()
1958 """Returns the global number of items in the tree."""
1960 if not self
._anchor
:
1964 count
= self
._anchor
.GetChildrenCount()
1966 if not self
.HasFlag(TR_HIDE_ROOT
):
1967 # take the root itself into account
1973 def GetIndent(self
):
1974 """Returns the item indentation."""
1979 def GetSpacing(self
):
1980 """Returns the spacing between the start and the text."""
1982 return self
._spacing
1985 def GetRootItem(self
):
1986 """Returns the root item."""
1991 def GetSelection(self
):
1992 """Returns the current selection: TR_SINGLE only."""
1994 return self
._current
1997 def ToggleItemSelection(self
, item
):
1998 """Toggles the item selection."""
2001 raise Exception("\nERROR: Invalid Tree Item. ")
2003 self
.SelectItem(item
, not self
.IsSelected(item
))
2006 def EnableChildren(self
, item
, enable
=True):
2007 """Enables/disables item children. Used internally."""
2010 if item
.IsExpanded():
2013 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2014 # We hit a radiobutton item not checked, we don't want to
2015 # enable the children
2018 child
, cookie
= self
.GetFirstChild(item
)
2020 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2022 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2023 self
.EnableChildren(child
, enable
)
2024 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2027 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2028 """Enables/disables an item."""
2031 raise Exception("\nERROR: Invalid Tree Item. ")
2033 if item
.IsEnabled() == enable
:
2036 if not enable
and item
.IsSelected():
2037 self
.SelectItem(item
, False)
2040 wnd
= item
.GetWindow()
2042 # Handles the eventual window associated to the item
2044 wndenable
= item
.GetWindowEnabled()
2052 # We have to refresh the item line
2053 dc
= wx
.ClientDC(self
)
2054 self
.CalculateSize(item
, dc
)
2055 self
.RefreshLine(item
)
2058 def IsEnabled(self
, item
):
2059 """Returns whether an item is enabled or disabled."""
2062 raise Exception("\nERROR: Invalid Tree Item. ")
2064 return item
.IsEnabled()
2067 def SetDisabledColour(self
, colour
):
2068 """Sets the items disabled colour."""
2070 self
._disabledColour
= colour
2074 def GetDisabledColour(self
):
2075 """Returns the items disabled colour."""
2077 return self
._disabledColour
2080 def IsItemChecked(self
, item
):
2081 """Returns whether an item is checked or not."""
2084 raise Exception("\nERROR: Invalid Tree Item. ")
2086 return item
.IsChecked()
2089 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2090 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2092 if item
.GetType() == 0:
2098 dc
= wx
.ClientDC(self
)
2099 self
.CalculateSize(item
, dc
)
2100 self
.RefreshLine(item
)
2103 def UnCheckRadioParent(self
, item
, checked
=False):
2104 """Used internally to handle radio node parent correctly."""
2106 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2108 e
.SetEventObject(self
)
2110 if self
.GetEventHandler().ProcessEvent(e
):
2114 self
.RefreshLine(item
)
2115 self
.EnableChildren(item
, checked
)
2116 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2118 e
.SetEventObject(self
)
2119 self
.GetEventHandler().ProcessEvent(e
)
2124 def CheckItem(self
, item
, checked
=True):
2126 Actually checks/uncheks an item, sending (eventually) the two
2127 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2131 raise Exception("\nERROR: Invalid Tree Item. ")
2133 # Should we raise an error here?!?
2134 if item
.GetType() == 0:
2137 if item
.GetType() == 2: # it's a radio button
2138 if not checked
and item
.IsChecked(): # Try To Unckeck?
2139 if item
.HasChildren():
2140 self
.UnCheckRadioParent(item
, checked
)
2143 if not self
.UnCheckRadioParent(item
, checked
):
2146 self
.CheckSameLevel(item
, False)
2149 # Radiobuttons are done, let's handle checkbuttons...
2150 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2152 e
.SetEventObject(self
)
2154 if self
.GetEventHandler().ProcessEvent(e
):
2159 dc
= wx
.ClientDC(self
)
2160 self
.RefreshLine(item
)
2162 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2163 ischeck
= self
.IsItemChecked(item
)
2164 self
.AutoCheckChild(item
, ischeck
)
2165 if self
._windowStyle
& TR_AUTO_CHECK_PARENT
:
2166 ischeck
= self
.IsItemChecked(item
)
2167 self
.AutoCheckParent(item
, ischeck
)
2168 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2169 self
.AutoToggleChild(item
)
2171 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2173 e
.SetEventObject(self
)
2174 self
.GetEventHandler().ProcessEvent(e
)
2177 def AutoToggleChild(self
, item
):
2178 """Transverses the tree and toggles the items. Meaningful only for check items."""
2181 raise Exception("\nERROR: Invalid Tree Item. ")
2183 child
, cookie
= self
.GetFirstChild(item
)
2186 if item
.IsExpanded():
2191 if child
.GetType() == 1 and child
.IsEnabled():
2192 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2193 self
.AutoToggleChild(child
)
2194 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2197 def AutoCheckChild(self
, item
, checked
):
2198 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2201 raise Exception("\nERROR: Invalid Tree Item. ")
2203 (child
, cookie
) = self
.GetFirstChild(item
)
2206 if item
.IsExpanded():
2210 if child
.GetType() == 1 and child
.IsEnabled():
2211 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2212 self
.AutoCheckChild(child
, checked
)
2213 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2216 def AutoCheckParent(self
, item
, checked
):
2217 """Traverses up the tree and checks/unchecks parent items.
2218 Meaningful only for check items."""
2221 raise Exception("\nERROR: Invalid Tree Item. ")
2223 parent
= item
.GetParent()
2224 if not parent
or parent
.GetType() != 1:
2227 (child
, cookie
) = self
.GetFirstChild(parent
)
2229 if child
.GetType() == 1 and child
.IsEnabled():
2230 if checked
!= child
.IsChecked():
2232 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2234 self
.CheckItem2(parent
, checked
, torefresh
=True)
2235 self
.AutoCheckParent(parent
, checked
)
2238 def CheckChilds(self
, item
, checked
=True):
2239 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2242 raise Exception("\nERROR: Invalid Tree Item. ")
2245 self
.AutoToggleChild(item
)
2247 self
.AutoCheckChild(item
, checked
)
2250 def CheckSameLevel(self
, item
, checked
=False):
2252 Uncheck radio items which are on the same level of the checked one.
2256 parent
= item
.GetParent()
2262 if parent
.IsExpanded():
2265 (child
, cookie
) = self
.GetFirstChild(parent
)
2267 if child
.GetType() == 2 and child
!= item
:
2268 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2269 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2270 self
.EnableChildren(child
, checked
)
2271 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2274 def EditLabel(self
, item
):
2275 """Starts editing an item label."""
2278 raise Exception("\nERROR: Invalid Tree Item. ")
2283 def ShouldInheritColours(self
):
2284 """We don't inherit colours from anyone."""
2289 def SetIndent(self
, indent
):
2290 """Sets item indentation."""
2292 self
._indent
= indent
2296 def SetSpacing(self
, spacing
):
2297 """Sets item spacing."""
2299 self
._spacing
= spacing
2303 def HasFlag(self
, flag
):
2304 """Returns whether CustomTreeCtrl has a flag."""
2306 return self
._windowStyle
& flag
2309 def HasChildren(self
, item
):
2310 """Returns whether an item has children or not."""
2313 raise Exception("\nERROR: Invalid Tree Item. ")
2315 return len(item
.GetChildren()) > 0
2318 def GetChildrenCount(self
, item
, recursively
=True):
2319 """Gets the item children count."""
2322 raise Exception("\nERROR: Invalid Tree Item. ")
2324 return item
.GetChildrenCount(recursively
)
2327 def SetTreeStyle(self
, styles
):
2328 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2330 # Do not try to expand the root node if it hasn't been created yet
2331 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2333 # if we will hide the root, make sure children are visible
2334 self
._anchor
.SetHasPlus()
2335 self
._anchor
.Expand()
2336 self
.CalculatePositions()
2338 # right now, just sets the styles. Eventually, we may
2339 # want to update the inherited styles, but right now
2340 # none of the parents has updatable styles
2342 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2343 selections
= self
.GetSelections()
2344 for select
in selections
[0:-1]:
2345 self
.SelectItem(select
, False)
2347 self
._windowStyle
= styles
2351 def GetTreeStyle(self
):
2352 """Returns the CustomTreeCtrl style."""
2354 return self
._windowStyle
2357 def HasButtons(self
):
2358 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2360 return self
.HasFlag(TR_HAS_BUTTONS
)
2363 # -----------------------------------------------------------------------------
2364 # functions to work with tree items
2365 # -----------------------------------------------------------------------------
2367 def GetItemText(self
, item
):
2368 """Returns the item text."""
2371 raise Exception("\nERROR: Invalid Tree Item. ")
2373 return item
.GetText()
2376 def GetItemImage(self
, item
, which
=TreeItemIcon_Normal
):
2377 """Returns the item image."""
2380 raise Exception("\nERROR: Invalid Tree Item. ")
2382 return item
.GetImage(which
)
2385 def GetPyData(self
, item
):
2386 """Returns the data associated to an item."""
2389 raise Exception("\nERROR: Invalid Tree Item. ")
2391 return item
.GetData()
2393 GetItemPyData
= GetPyData
2396 def GetItemTextColour(self
, item
):
2397 """Returns the item text colour."""
2400 raise Exception("\nERROR: Invalid Tree Item. ")
2402 return item
.Attr().GetTextColour()
2405 def GetItemBackgroundColour(self
, item
):
2406 """Returns the item background colour."""
2409 raise Exception("\nERROR: Invalid Tree Item. ")
2411 return item
.Attr().GetBackgroundColour()
2414 def GetItemFont(self
, item
):
2415 """Returns the item font."""
2418 raise Exception("\nERROR: Invalid Tree Item. ")
2420 return item
.Attr().GetFont()
2423 def IsItemHyperText(self
, item
):
2424 """Returns whether an item is hypertext or not."""
2427 raise Exception("\nERROR: Invalid Tree Item. ")
2429 return item
.IsHyperText()
2432 def SetItemText(self
, item
, text
):
2433 """Sets the item text."""
2436 raise Exception("\nERROR: Invalid Tree Item. ")
2438 dc
= wx
.ClientDC(self
)
2440 self
.CalculateSize(item
, dc
)
2441 self
.RefreshLine(item
)
2444 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2445 """Sets the item image, depending on the item state."""
2448 raise Exception("\nERROR: Invalid Tree Item. ")
2450 item
.SetImage(image
, which
)
2452 dc
= wx
.ClientDC(self
)
2453 self
.CalculateSize(item
, dc
)
2454 self
.RefreshLine(item
)
2457 def SetPyData(self
, item
, data
):
2458 """Sets the data associated to an item."""
2461 raise Exception("\nERROR: Invalid Tree Item. ")
2465 SetItemPyData
= SetPyData
2468 def SetItemHasChildren(self
, item
, has
=True):
2469 """Forces the appearance of the button next to the item."""
2472 raise Exception("\nERROR: Invalid Tree Item. ")
2474 item
.SetHasPlus(has
)
2475 self
.RefreshLine(item
)
2478 def SetItemBold(self
, item
, bold
=True):
2479 """Sets the item font bold/unbold."""
2482 raise Exception("\nERROR: Invalid Tree Item. ")
2484 # avoid redrawing the tree if no real change
2485 if item
.IsBold() != bold
:
2490 def SetItemItalic(self
, item
, italic
=True):
2491 """Sets the item font italic/non-italic."""
2494 raise Exception("\nERROR: Invalid Tree Item. ")
2496 if item
.IsItalic() != italic
:
2497 itemFont
= self
.GetItemFont(item
)
2498 if itemFont
!= wx
.NullFont
:
2503 item
.SetItalic(italic
)
2504 itemFont
.SetStyle(style
)
2505 self
.SetItemFont(item
, itemFont
)
2509 def SetItemDropHighlight(self
, item
, highlight
=True):
2511 Gives the item the visual feedback for drag and drop operations.
2512 This is useful when something is dragged from outside the CustomTreeCtrl.
2516 raise Exception("\nERROR: Invalid Tree Item. ")
2519 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2520 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2522 item
.Attr().SetTextColour(fg
)
2523 item
.Attr
.SetBackgroundColour(bg
)
2524 self
.RefreshLine(item
)
2527 def SetItemTextColour(self
, item
, col
):
2528 """Sets the item text colour."""
2531 raise Exception("\nERROR: Invalid Tree Item. ")
2533 if self
.GetItemTextColour(item
) == col
:
2536 item
.Attr().SetTextColour(col
)
2537 self
.RefreshLine(item
)
2540 def SetItemBackgroundColour(self
, item
, col
):
2541 """Sets the item background colour."""
2544 raise Exception("\nERROR: Invalid Tree Item. ")
2546 item
.Attr().SetBackgroundColour(col
)
2547 self
.RefreshLine(item
)
2550 def SetItemHyperText(self
, item
, hyper
=True):
2551 """Sets whether the item is hypertext or not."""
2554 raise Exception("\nERROR: Invalid Tree Item. ")
2556 item
.SetHyperText(hyper
)
2557 self
.RefreshLine(item
)
2560 def SetItemFont(self
, item
, font
):
2561 """Sets the item font."""
2564 raise Exception("\nERROR: Invalid Tree Item. ")
2566 if self
.GetItemFont(item
) == font
:
2569 item
.Attr().SetFont(font
)
2573 def SetFont(self
, font
):
2574 """Sets the CustomTreeCtrl font."""
2576 wx
.ScrolledWindow
.SetFont(self
, font
)
2578 self
._normalFont
= font
2579 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2580 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2581 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2586 def GetHyperTextFont(self
):
2587 """Returns the font used to render an hypertext item."""
2589 return self
._hypertextfont
2592 def SetHyperTextFont(self
, font
):
2593 """Sets the font used to render an hypertext item."""
2595 self
._hypertextfont
= font
2599 def SetHyperTextNewColour(self
, colour
):
2600 """Sets the colour used to render a non-visited hypertext item."""
2602 self
._hypertextnewcolour
= colour
2606 def GetHyperTextNewColour(self
):
2607 """Returns the colour used to render a non-visited hypertext item."""
2609 return self
._hypertextnewcolour
2612 def SetHyperTextVisitedColour(self
, colour
):
2613 """Sets the colour used to render a visited hypertext item."""
2615 self
._hypertextvisitedcolour
= colour
2619 def GetHyperTextVisitedColour(self
):
2620 """Returns the colour used to render a visited hypertext item."""
2622 return self
._hypertextvisitedcolour
2625 def SetItemVisited(self
, item
, visited
=True):
2626 """Sets whether an hypertext item was visited."""
2629 raise Exception("\nERROR: Invalid Tree Item. ")
2631 item
.SetVisited(visited
)
2632 self
.RefreshLine(item
)
2635 def GetItemVisited(self
, item
):
2636 """Returns whether an hypertext item was visited."""
2639 raise Exception("\nERROR: Invalid Tree Item. ")
2641 return item
.GetVisited()
2644 def SetHilightFocusColour(self
, colour
):
2646 Sets the colour used to highlight focused selected items.
2647 This is applied only if gradient and Windows Vista styles are disabled.
2650 self
._hilightBrush
= wx
.Brush(colour
)
2651 self
.RefreshSelected()
2654 def SetHilightNonFocusColour(self
, colour
):
2656 Sets the colour used to highlight unfocused selected items.
2657 This is applied only if gradient and Windows Vista styles are disabled.
2660 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2661 self
.RefreshSelected()
2664 def GetHilightFocusColour(self
):
2666 Returns the colour used to highlight focused selected items.
2667 This is applied only if gradient and Windows Vista styles are disabled.
2670 return self
._hilightBrush
.GetColour()
2673 def GetHilightNonFocusColour(self
):
2675 Returns the colour used to highlight unfocused selected items.
2676 This is applied only if gradient and Windows Vista styles are disabled.
2679 return self
._hilightUnfocusedBrush
.GetColour()
2682 def SetFirstGradientColour(self
, colour
=None):
2683 """Sets the first gradient colour."""
2686 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2688 self
._firstcolour
= colour
2689 if self
._usegradients
:
2690 self
.RefreshSelected()
2693 def SetSecondGradientColour(self
, colour
=None):
2694 """Sets the second gradient colour."""
2697 # No colour given, generate a slightly darker from the
2698 # CustomTreeCtrl background colour
2699 color
= self
.GetBackgroundColour()
2700 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2701 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2702 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2704 self
._secondcolour
= colour
2706 if self
._usegradients
:
2707 self
.RefreshSelected()
2710 def GetFirstGradientColour(self
):
2711 """Returns the first gradient colour."""
2713 return self
._firstcolour
2716 def GetSecondGradientColour(self
):
2717 """Returns the second gradient colour."""
2719 return self
._secondcolour
2722 def EnableSelectionGradient(self
, enable
=True):
2723 """Globally enables/disables drawing of gradient selection."""
2725 self
._usegradients
= enable
2726 self
._vistaselection
= False
2727 self
.RefreshSelected()
2730 def SetGradientStyle(self
, vertical
=0):
2732 Sets the gradient style:
2733 0: horizontal gradient
2734 1: vertical gradient
2737 # 0 = Horizontal, 1 = Vertical
2738 self
._gradientstyle
= vertical
2740 if self
._usegradients
:
2741 self
.RefreshSelected()
2744 def GetGradientStyle(self
):
2746 Returns the gradient style:
2747 0: horizontal gradient
2748 1: vertical gradient
2751 return self
._gradientstyle
2754 def EnableSelectionVista(self
, enable
=True):
2755 """Globally enables/disables drawing of Windows Vista selection."""
2757 self
._usegradients
= False
2758 self
._vistaselection
= enable
2759 self
.RefreshSelected()
2762 def SetBorderPen(self
, pen
):
2764 Sets the pen used to draw the selected item border.
2765 The border pen is not used if the Windows Vista style is applied.
2768 self
._borderPen
= pen
2769 self
.RefreshSelected()
2772 def GetBorderPen(self
):
2774 Returns the pen used to draw the selected item border.
2775 The border pen is not used if the Windows Vista style is applied.
2778 return self
._borderPen
2781 def SetConnectionPen(self
, pen
):
2782 """Sets the pen used to draw the connecting lines between items."""
2784 self
._dottedPen
= pen
2788 def GetConnectionPen(self
):
2789 """Returns the pen used to draw the connecting lines between items."""
2791 return self
._dottedPen
2794 def SetBackgroundImage(self
, image
):
2795 """Sets the CustomTreeCtrl background image (can be none)."""
2797 self
._backgroundImage
= image
2801 def GetBackgroundImage(self
):
2802 """Returns the CustomTreeCtrl background image (can be none)."""
2804 return self
._backgroundImage
2807 def GetItemWindow(self
, item
):
2808 """Returns the window associated to the item (if any)."""
2811 raise Exception("\nERROR: Invalid Item")
2813 return item
.GetWindow()
2816 def GetItemWindowEnabled(self
, item
):
2817 """Returns whether the window associated to the item is enabled."""
2820 raise Exception("\nERROR: Invalid Item")
2822 return item
.GetWindowEnabled()
2825 def SetItemWindowEnabled(self
, item
, enable
=True):
2826 """Enables/disables the window associated to the item."""
2829 raise Exception("\nERROR: Invalid Item")
2831 item
.SetWindowEnabled(enable
)
2834 def GetItemType(self
, item
):
2836 Returns the item type:
2843 raise Exception("\nERROR: Invalid Item")
2845 return item
.GetType()
2847 # -----------------------------------------------------------------------------
2848 # item status inquiries
2849 # -----------------------------------------------------------------------------
2851 def IsVisible(self
, item
):
2852 """Returns whether the item is visible or not."""
2855 raise Exception("\nERROR: Invalid Tree Item. ")
2857 # An item is only visible if it's not a descendant of a collapsed item
2858 parent
= item
.GetParent()
2862 if not parent
.IsExpanded():
2865 parent
= parent
.GetParent()
2867 startX
, startY
= self
.GetViewStart()
2868 clientSize
= self
.GetClientSize()
2870 rect
= self
.GetBoundingRect(item
)
2874 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2876 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2878 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2884 def ItemHasChildren(self
, item
):
2885 """Returns whether the item has children or not."""
2888 raise Exception("\nERROR: Invalid Tree Item. ")
2890 # consider that the item does have children if it has the "+" button: it
2891 # might not have them (if it had never been expanded yet) but then it
2892 # could have them as well and it's better to err on this side rather than
2893 # disabling some operations which are restricted to the items with
2894 # children for an item which does have them
2895 return item
.HasPlus()
2898 def IsExpanded(self
, item
):
2899 """Returns whether the item is expanded or not."""
2902 raise Exception("\nERROR: Invalid Tree Item. ")
2904 return item
.IsExpanded()
2907 def IsSelected(self
, item
):
2908 """Returns whether the item is selected or not."""
2911 raise Exception("\nERROR: Invalid Tree Item. ")
2913 return item
.IsSelected()
2916 def IsBold(self
, item
):
2917 """Returns whether the item font is bold or not."""
2920 raise Exception("\nERROR: Invalid Tree Item. ")
2922 return item
.IsBold()
2925 def IsItalic(self
, item
):
2926 """Returns whether the item font is italic or not."""
2929 raise Exception("\nERROR: Invalid Tree Item. ")
2931 return item
.IsItalic()
2934 # -----------------------------------------------------------------------------
2936 # -----------------------------------------------------------------------------
2938 def GetItemParent(self
, item
):
2939 """Gets the item parent."""
2942 raise Exception("\nERROR: Invalid Tree Item. ")
2944 return item
.GetParent()
2947 def GetFirstChild(self
, item
):
2948 """Gets the item first child."""
2951 raise Exception("\nERROR: Invalid Tree Item. ")
2954 return self
.GetNextChild(item
, cookie
)
2957 def GetNextChild(self
, item
, cookie
):
2959 Gets the item next child based on the 'cookie' parameter.
2960 This method has no sense if you do not call GetFirstChild() before.
2964 raise Exception("\nERROR: Invalid Tree Item. ")
2966 children
= item
.GetChildren()
2968 # it's ok to cast cookie to size_t, we never have indices big enough to
2971 if cookie
< len(children
):
2973 return children
[cookie
], cookie
+1
2977 # there are no more of them
2981 def GetLastChild(self
, item
):
2982 """Gets the item last child."""
2985 raise Exception("\nERROR: Invalid Tree Item. ")
2987 children
= item
.GetChildren()
2988 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2991 def GetNextSibling(self
, item
):
2992 """Gets the next sibling of an item."""
2995 raise Exception("\nERROR: Invalid Tree Item. ")
2998 parent
= i
.GetParent()
3002 # root item doesn't have any siblings
3005 siblings
= parent
.GetChildren()
3006 index
= siblings
.index(i
)
3009 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
3012 def GetPrevSibling(self
, item
):
3013 """Gets the previous sibling of an item."""
3016 raise Exception("\nERROR: Invalid Tree Item. ")
3019 parent
= i
.GetParent()
3023 # root item doesn't have any siblings
3026 siblings
= parent
.GetChildren()
3027 index
= siblings
.index(i
)
3029 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3032 def GetNext(self
, item
):
3033 """Gets the next item. Only for internal use right now."""
3036 raise Exception("\nERROR: Invalid Tree Item. ")
3040 # First see if there are any children.
3041 children
= i
.GetChildren()
3042 if len(children
) > 0:
3045 # Try a sibling of this or ancestor instead
3048 while p
and not toFind
:
3049 toFind
= self
.GetNextSibling(p
)
3050 p
= self
.GetItemParent(p
)
3055 def GetFirstVisibleItem(self
):
3056 """Returns the first visible item."""
3058 id = self
.GetRootItem()
3063 if self
.IsVisible(id):
3065 id = self
.GetNext(id)
3070 def GetNextVisible(self
, item
):
3071 """Returns the next visible item."""
3074 raise Exception("\nERROR: Invalid Tree Item. ")
3079 id = self
.GetNext(id)
3080 if id and self
.IsVisible(id):
3086 def GetPrevVisible(self
, item
):
3089 raise Exception("\nERROR: Invalid Tree Item. ")
3091 raise Exception("\nERROR: Not Implemented")
3096 def ResetTextControl(self
):
3097 """Called by TreeTextCtrl when it marks itself for deletion."""
3099 self
._textCtrl
.Destroy()
3100 self
._textCtrl
= None
3103 def FindItem(self
, idParent
, prefixOrig
):
3104 """Finds the first item starting with the given prefix after the given item."""
3106 # match is case insensitive as this is more convenient to the user: having
3107 # to press Shift-letter to go to the item starting with a capital letter
3108 # would be too bothersome
3109 prefix
= prefixOrig
.lower()
3111 # determine the starting point: we shouldn't take the current item (this
3112 # allows to switch between two items starting with the same letter just by
3113 # pressing it) but we shouldn't jump to the next one if the user is
3114 # continuing to type as otherwise he might easily skip the item he wanted
3117 if len(prefix
) == 1:
3118 id = self
.GetNext(id)
3120 # look for the item starting with the given prefix after it
3121 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3123 id = self
.GetNext(id)
3125 # if we haven't found anything...
3128 # ... wrap to the beginning
3129 id = self
.GetRootItem()
3130 if self
.HasFlag(TR_HIDE_ROOT
):
3131 # can't select virtual root
3132 id = self
.GetNext(id)
3134 # and try all the items (stop when we get to the one we started from)
3135 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3136 id = self
.GetNext(id)
3141 # -----------------------------------------------------------------------------
3143 # -----------------------------------------------------------------------------
3145 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3146 """Actually inserts an item in the tree."""
3148 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3149 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3151 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3152 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3154 if ct_type
< 0 or ct_type
> 2:
3155 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3161 # should we give a warning here?
3162 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3164 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3166 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3169 self
._hasWindows
= True
3170 self
._itemWithWindow
.append(item
)
3172 parent
.Insert(item
, previous
)
3177 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3178 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3181 raise Exception("\nERROR: Tree Can Have Only One Root")
3183 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3184 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3186 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3187 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3189 if ct_type
< 0 or ct_type
> 2:
3190 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3192 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3194 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3197 self
._hasWindows
= True
3198 self
._itemWithWindow
.append(self
._anchor
)
3200 if self
.HasFlag(TR_HIDE_ROOT
):
3202 # if root is hidden, make sure we can navigate
3204 self
._anchor
.SetHasPlus()
3205 self
._anchor
.Expand()
3206 self
.CalculatePositions()
3208 if not self
.HasFlag(TR_MULTIPLE
):
3210 self
._current
= self
._key
_current
= self
._anchor
3211 self
._current
.SetHilight(True)
3216 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3217 """Appends an item as a first child of parent."""
3219 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3220 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3222 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3223 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3225 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3228 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3229 """Auxiliary function to cope with the C++ hideous multifunction."""
3231 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3232 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3234 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3235 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3240 # should we give a warning here?
3241 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3247 index
= parent
.GetChildren().index(idPrevious
)
3249 raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
3251 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3254 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3255 """Auxiliary function to cope with the C++ hideous multifunction."""
3257 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3258 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3260 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3261 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3266 # should we give a warning here?
3267 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3269 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3272 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3273 """Inserts an item after the given previous."""
3275 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3276 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3278 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3279 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3281 if type(input) == type(1):
3282 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3284 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3287 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3288 """Appends an item as a last child of its parent."""
3290 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3291 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3293 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3294 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3299 # should we give a warning here?
3300 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3302 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3305 def SendDeleteEvent(self
, item
):
3306 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3308 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3310 event
.SetEventObject(self
)
3311 self
.ProcessEvent(event
)
3314 def IsDescendantOf(self
, parent
, item
):
3315 """Checks if the given item is under another one."""
3321 # item is a descendant of parent
3324 item
= item
.GetParent()
3329 # Don't leave edit or selection on a child which is about to disappear
3330 def ChildrenClosing(self
, item
):
3331 """We are about to destroy the item children."""
3333 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3334 self
._textCtrl
.StopEditing()
3336 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3337 self
._key
_current
= None
3339 if self
.IsDescendantOf(item
, self
._select
_me
):
3340 self
._select
_me
= item
3342 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3343 self
._current
.SetHilight(False)
3344 self
._current
= None
3345 self
._select
_me
= item
3348 def DeleteChildren(self
, item
):
3349 """Delete item children."""
3352 raise Exception("\nERROR: Invalid Tree Item. ")
3354 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3356 self
.ChildrenClosing(item
)
3357 item
.DeleteChildren(self
)
3360 def Delete(self
, item
):
3361 """Delete an item."""
3364 raise Exception("\nERROR: Invalid Tree Item. ")
3366 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3368 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3369 # can't delete the item being edited, cancel editing it first
3370 self
._textCtrl
.StopEditing()
3372 parent
= item
.GetParent()
3374 # don't keep stale pointers around!
3375 if self
.IsDescendantOf(item
, self
._key
_current
):
3377 # Don't silently change the selection:
3378 # do it properly in idle time, so event
3379 # handlers get called.
3381 # self._key_current = parent
3382 self
._key
_current
= None
3384 # self._select_me records whether we need to select
3385 # a different item, in idle time.
3386 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3387 self
._select
_me
= parent
3389 if self
.IsDescendantOf(item
, self
._current
):
3391 # Don't silently change the selection:
3392 # do it properly in idle time, so event
3393 # handlers get called.
3395 # self._current = parent
3396 self
._current
= None
3397 self
._select
_me
= parent
3399 # remove the item from the tree
3402 parent
.GetChildren().remove(item
) # remove by value
3404 else: # deleting the root
3406 # nothing will be left in the tree
3409 # and delete all of its children and the item itself now
3410 item
.DeleteChildren(self
)
3411 self
.SendDeleteEvent(item
)
3413 if item
== self
._select
_me
:
3414 self
._select
_me
= None
3416 # Remove the item with window
3417 if item
in self
._itemWithWindow
:
3418 wnd
= item
.GetWindow()
3422 self
._itemWithWindow
.remove(item
)
3427 def DeleteAllItems(self
):
3428 """Delete all items in the CustomTreeCtrl."""
3431 self
.Delete(self
._anchor
)
3434 def Expand(self
, item
):
3436 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3437 EVT_TREE_ITEM_EXPANDED events.
3441 raise Exception("\nERROR: Invalid Tree Item. ")
3443 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3444 raise Exception("\nERROR: Can't Expand An Hidden Root. ")
3446 if not item
.HasPlus():
3449 if item
.IsExpanded():
3452 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3454 event
.SetEventObject(self
)
3456 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3457 # cancelled by program
3461 self
.CalculatePositions()
3463 self
.RefreshSubtree(item
)
3465 if self
._hasWindows
:
3466 # We hide the associated window here, we may show it after
3469 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3470 self
.ProcessEvent(event
)
3473 def ExpandAllChildren(self
, item
):
3474 """Expands all the items children of the input item."""
3477 raise Exception("\nERROR: Invalid Tree Item. ")
3479 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3481 if not self
.IsExpanded(item
):
3484 child
, cookie
= self
.GetFirstChild(item
)
3487 self
.ExpandAllChildren(child
)
3488 child
, cookie
= self
.GetNextChild(item
, cookie
)
3491 def ExpandAll(self
):
3492 """Expands all CustomTreeCtrl items."""
3495 self
.ExpandAllChildren(self
._anchor
)
3498 def Collapse(self
, item
):
3500 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3501 EVT_TREE_ITEM_COLLAPSED events.
3505 raise Exception("\nERROR: Invalid Tree Item. ")
3507 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3508 raise Exception("\nERROR: Can't Collapse An Hidden Root. ")
3510 if not item
.IsExpanded():
3513 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3515 event
.SetEventObject(self
)
3516 if self
.ProcessEvent(event
) and not event
.IsAllowed():
3517 # cancelled by program
3520 self
.ChildrenClosing(item
)
3523 self
.CalculatePositions()
3524 self
.RefreshSubtree(item
)
3526 if self
._hasWindows
:
3529 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3530 self
.ProcessEvent(event
)
3533 def CollapseAndReset(self
, item
):
3534 """Collapse the given item and deletes its children."""
3537 self
.DeleteChildren(item
)
3540 def Toggle(self
, item
):
3541 """Toggles the item state (collapsed/expanded)."""
3543 if item
.IsExpanded():
3549 def HideWindows(self
):
3550 """Hides the windows associated to the items. Used internally."""
3552 for child
in self
._itemWithWindow
:
3553 if not self
.IsVisible(child
):
3554 wnd
= child
.GetWindow()
3559 """Unselects the current selection."""
3563 self
._current
.SetHilight(False)
3564 self
.RefreshLine(self
._current
)
3566 self
._current
= None
3567 self
._select
_me
= None
3570 def UnselectAllChildren(self
, item
):
3571 """Unselects all the children of the given item."""
3573 if item
.IsSelected():
3575 item
.SetHilight(False)
3576 self
.RefreshLine(item
)
3578 if item
.HasChildren():
3579 for child
in item
.GetChildren():
3580 self
.UnselectAllChildren(child
)
3583 def UnselectAll(self
):
3584 """Unselect all the items."""
3586 rootItem
= self
.GetRootItem()
3588 # the tree might not have the root item at all
3590 self
.UnselectAllChildren(rootItem
)
3594 # Recursive function !
3595 # To stop we must have crt_item<last_item
3597 # Tag all next children, when no more children,
3598 # Move to parent (not to tag)
3599 # Keep going... if we found last_item, we stop.
3601 def TagNextChildren(self
, crt_item
, last_item
, select
):
3602 """Used internally."""
3604 parent
= crt_item
.GetParent()
3606 if parent
== None: # This is root item
3607 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3609 children
= parent
.GetChildren()
3610 index
= children
.index(crt_item
)
3612 count
= len(children
)
3614 for n
in xrange(index
+1, count
):
3615 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3618 return self
.TagNextChildren(parent
, last_item
, select
)
3621 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3622 """Used internally."""
3624 crt_item
.SetHilight(select
)
3625 self
.RefreshLine(crt_item
)
3627 if crt_item
== last_item
:
3630 if crt_item
.HasChildren():
3631 for child
in crt_item
.GetChildren():
3632 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3638 def SelectItemRange(self
, item1
, item2
):
3639 """Selects all the items between item1 and item2."""
3641 self
._select
_me
= None
3643 # item2 is not necessary after item1
3644 # choice first' and 'last' between item1 and item2
3645 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3646 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3648 select
= self
._current
.IsSelected()
3650 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3653 self
.TagNextChildren(first
, last
, select
)
3656 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3657 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3660 raise Exception("\nERROR: Invalid Tree Item. ")
3662 self
._select
_me
= None
3664 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3666 # to keep going anyhow !!!
3668 if item
.IsSelected():
3669 return # nothing to do
3670 unselect_others
= True
3671 extended_select
= False
3673 elif unselect_others
and item
.IsSelected():
3675 # selection change if there is more than one item currently selected
3676 if len(self
.GetSelections()) == 1:
3679 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3681 event
._itemOld
= self
._current
3682 event
.SetEventObject(self
)
3683 # TODO : Here we don't send any selection mode yet !
3685 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3688 parent
= self
.GetItemParent(item
)
3690 if not self
.IsExpanded(parent
):
3693 parent
= self
.GetItemParent(parent
)
3698 self
.Unselect() # to speed up thing
3704 if not self
._current
:
3705 self
._current
= self
._key
_current
= self
.GetRootItem()
3707 # don't change the mark (self._current)
3708 self
.SelectItemRange(self
._current
, item
)
3712 select
= True # the default
3714 # Check if we need to toggle hilight (ctrl mode)
3715 if not unselect_others
:
3716 select
= not item
.IsSelected()
3718 self
._current
= self
._key
_current
= item
3719 self
._current
.SetHilight(select
)
3720 self
.RefreshLine(self
._current
)
3722 # This can cause idle processing to select the root
3723 # if no item is selected, so it must be after the
3725 self
.EnsureVisible(item
)
3727 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3728 self
.GetEventHandler().ProcessEvent(event
)
3730 # Handles hypertext items
3731 if self
.IsItemHyperText(item
):
3732 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3734 self
.GetEventHandler().ProcessEvent(event
)
3737 def SelectItem(self
, item
, select
=True):
3738 """Selects/deselects an item."""
3741 raise Exception("\nERROR: Invalid Tree Item. ")
3745 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3749 item
.SetHilight(False)
3750 self
.RefreshLine(item
)
3753 def FillArray(self
, item
, array
=[]):
3755 Internal function. Used to populate an array of selected items when
3756 the style TR_MULTIPLE is used.
3762 if item
.IsSelected():
3765 if item
.HasChildren() and item
.IsExpanded():
3766 for child
in item
.GetChildren():
3767 array
= self
.FillArray(child
, array
)
3772 def GetSelections(self
):
3774 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3775 the TR_MULTIPLE style set.
3779 idRoot
= self
.GetRootItem()
3781 array
= self
.FillArray(idRoot
, array
)
3783 #else: the tree is empty, so no selections
3788 def EnsureVisible(self
, item
):
3789 """Ensure that an item is visible in CustomTreeCtrl."""
3792 raise Exception("\nERROR: Invalid Tree Item. ")
3794 # first expand all parent branches
3795 parent
= item
.GetParent()
3797 if self
.HasFlag(TR_HIDE_ROOT
):
3798 while parent
and parent
!= self
._anchor
:
3800 parent
= parent
.GetParent()
3804 parent
= parent
.GetParent()
3809 def ScrollTo(self
, item
):
3810 """Scrolls the specified item into view."""
3815 # We have to call this here because the label in
3816 # question might just have been added and no screen
3817 # update taken place.
3819 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3824 # now scroll to the item
3825 item_y
= item
.GetY()
3826 start_x
, start_y
= self
.GetViewStart()
3827 start_y
*= _PIXELS_PER_UNIT
3829 client_w
, client_h
= self
.GetClientSize()
3833 if item_y
< start_y
+3:
3836 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3837 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3838 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3839 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3840 # Item should appear at top
3841 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3843 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3846 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3847 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3848 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3849 item_y
+= _PIXELS_PER_UNIT
+2
3850 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3851 # Item should appear at bottom
3852 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
)
3855 def OnCompareItems(self
, item1
, item2
):
3857 Returns whether 2 items have the same text.
3858 Override this function in the derived class to change the sort order of the items
3859 in the CustomTreeCtrl. The function should return a negative, zero or positive
3860 value if the first item is less than, equal to or greater than the second one.
3862 The base class version compares items alphabetically.
3865 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3868 def SortChildren(self
, item
):
3870 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3871 You should override that method to change the sort order (the default is ascending
3872 case-sensitive alphabetical order).
3876 raise Exception("\nERROR: Invalid Tree Item. ")
3878 children
= item
.GetChildren()
3880 if len(children
) > 1:
3882 children
.sort(self
.OnCompareItems
)
3885 def GetImageList(self
):
3886 """Returns the normal image list."""
3888 return self
._imageListNormal
3891 def GetButtonsImageList(self
):
3892 """Returns the buttons image list (from which application-defined button images are taken)."""
3894 return self
._imageListButtons
3897 def GetStateImageList(self
):
3898 """Returns the state image list (from which application-defined state images are taken)."""
3900 return self
._imageListState
3903 def GetImageListCheck(self
):
3904 """Returns the image list used to build the check/radio buttons."""
3906 return self
._imageListCheck
3909 def CalculateLineHeight(self
):
3910 """Calculates the height of a line."""
3912 dc
= wx
.ClientDC(self
)
3913 self
._lineHeight
= dc
.GetCharHeight()
3915 if self
._imageListNormal
:
3917 # Calculate a self._lineHeight value from the normal Image sizes.
3918 # May be toggle off. Then CustomTreeCtrl will spread when
3919 # necessary (which might look ugly).
3920 n
= self
._imageListNormal
.GetImageCount()
3924 width
, height
= self
._imageListNormal
.GetSize(i
)
3926 if height
> self
._lineHeight
:
3927 self
._lineHeight
= height
3929 if self
._imageListButtons
:
3931 # Calculate a self._lineHeight value from the Button image sizes.
3932 # May be toggle off. Then CustomTreeCtrl will spread when
3933 # necessary (which might look ugly).
3934 n
= self
._imageListButtons
.GetImageCount()
3938 width
, height
= self
._imageListButtons
.GetSize(i
)
3940 if height
> self
._lineHeight
:
3941 self
._lineHeight
= height
3943 if self
._imageListCheck
:
3945 # Calculate a self._lineHeight value from the check/radio image sizes.
3946 # May be toggle off. Then CustomTreeCtrl will spread when
3947 # necessary (which might look ugly).
3948 n
= self
._imageListCheck
.GetImageCount()
3952 width
, height
= self
._imageListCheck
.GetSize(i
)
3954 if height
> self
._lineHeight
:
3955 self
._lineHeight
= height
3957 if self
._lineHeight
< 30:
3958 self
._lineHeight
+= 2 # at least 2 pixels
3960 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3963 def SetImageList(self
, imageList
):
3964 """Sets the normal image list."""
3966 if self
._ownsImageListNormal
:
3967 del self
._imageListNormal
3969 self
._imageListNormal
= imageList
3970 self
._ownsImageListNormal
= False
3973 # Don't do any drawing if we're setting the list to NULL,
3974 # since we may be in the process of deleting the tree control.
3976 self
.CalculateLineHeight()
3978 # We gray out the image list to use the grayed icons with disabled items
3979 sz
= imageList
.GetSize(0)
3980 self
._grayedImageList
= wx
.ImageList(sz
[0], sz
[1], True, 0)
3982 for ii
in xrange(imageList
.GetImageCount()):
3983 bmp
= imageList
.GetBitmap(ii
)
3984 image
= wx
.ImageFromBitmap(bmp
)
3985 image
= GrayOut(image
)
3986 newbmp
= wx
.BitmapFromImage(image
)
3987 self
._grayedImageList
.Add(newbmp
)
3990 def SetStateImageList(self
, imageList
):
3991 """Sets the state image list (from which application-defined state images are taken)."""
3993 if self
._ownsImageListState
:
3994 del self
._imageListState
3996 self
._imageListState
= imageList
3997 self
._ownsImageListState
= False
4000 def SetButtonsImageList(self
, imageList
):
4001 """Sets the buttons image list (from which application-defined button images are taken)."""
4003 if self
._ownsImageListButtons
:
4004 del self
._imageListButtons
4006 self
._imageListButtons
= imageList
4007 self
._ownsImageListButtons
= False
4009 self
.CalculateLineHeight()
4012 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
4013 """Sets the check image list."""
4017 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
4018 self
._imageListCheck
.Add(GetCheckedBitmap())
4019 self
._imageListCheck
.Add(GetNotCheckedBitmap())
4020 self
._imageListCheck
.Add(GetFlaggedBitmap())
4021 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
4025 sizex
, sizey
= imglist
.GetSize(0)
4026 self
._imageListCheck
= imglist
4028 # We gray out the image list to use the grayed icons with disabled items
4029 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
4031 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
4033 bmp
= self
._imageListCheck
.GetBitmap(ii
)
4034 image
= wx
.ImageFromBitmap(bmp
)
4035 image
= GrayOut(image
)
4036 newbmp
= wx
.BitmapFromImage(image
)
4037 self
._grayedCheckList
.Add(newbmp
)
4042 self
.CalculateLineHeight()
4045 def AssignImageList(self
, imageList
):
4046 """Assigns the normal image list."""
4048 self
.SetImageList(imageList
)
4049 self
._ownsImageListNormal
= True
4052 def AssignStateImageList(self
, imageList
):
4053 """Assigns the state image list."""
4055 self
.SetStateImageList(imageList
)
4056 self
._ownsImageListState
= True
4059 def AssignButtonsImageList(self
, imageList
):
4060 """Assigns the button image list."""
4062 self
.SetButtonsImageList(imageList
)
4063 self
._ownsImageListButtons
= True
4066 # -----------------------------------------------------------------------------
4068 # -----------------------------------------------------------------------------
4070 def AdjustMyScrollbars(self
):
4071 """Adjust the wx.ScrolledWindow scrollbars."""
4075 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4076 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4077 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4078 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4079 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4080 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4084 self
.SetScrollbars(0, 0, 0, 0)
4087 def GetLineHeight(self
, item
):
4088 """Returns the line height for the given item."""
4090 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4091 return item
.GetHeight()
4093 return self
._lineHeight
4096 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4097 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4099 oldpen
= dc
.GetPen()
4100 oldbrush
= dc
.GetBrush()
4101 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4103 # calculate gradient coefficients
4105 col2
= self
._secondcolour
4106 col1
= self
._firstcolour
4108 col2
= self
._hilightUnfocusedBrush
.GetColour()
4109 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4111 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4112 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4114 flrect
= float(rect
.height
)
4116 rstep
= float((r2
- r1
)) / flrect
4117 gstep
= float((g2
- g1
)) / flrect
4118 bstep
= float((b2
- b1
)) / flrect
4120 rf
, gf
, bf
= 0, 0, 0
4122 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4123 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4124 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4125 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4131 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4132 dc
.DrawRectangleRect(rect
)
4133 dc
.SetBrush(oldbrush
)
4136 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4137 """Gradient fill from colour 1 to colour 2 from left to right."""
4139 oldpen
= dc
.GetPen()
4140 oldbrush
= dc
.GetBrush()
4141 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4143 # calculate gradient coefficients
4146 col2
= self
._secondcolour
4147 col1
= self
._firstcolour
4149 col2
= self
._hilightUnfocusedBrush
.GetColour()
4150 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4152 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4153 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4155 flrect
= float(rect
.width
)
4157 rstep
= float((r2
- r1
)) / flrect
4158 gstep
= float((g2
- g1
)) / flrect
4159 bstep
= float((b2
- b1
)) / flrect
4161 rf
, gf
, bf
= 0, 0, 0
4163 for x
in xrange(rect
.x
, rect
.x
+ rect
.width
):
4164 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4165 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4166 dc
.DrawRectangle(x
, rect
.y
, 1, rect
.height
)
4172 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4173 dc
.DrawRectangleRect(rect
)
4174 dc
.SetBrush(oldbrush
)
4177 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4178 """Draw the selected item(s) with the Windows Vista style."""
4182 outer
= _rgbSelectOuter
4183 inner
= _rgbSelectInner
4185 bottom
= _rgbSelectBottom
4189 outer
= _rgbNoFocusOuter
4190 inner
= _rgbNoFocusInner
4191 top
= _rgbNoFocusTop
4192 bottom
= _rgbNoFocusBottom
4194 oldpen
= dc
.GetPen()
4195 oldbrush
= dc
.GetBrush()
4197 bdrRect
= wx
.Rect(*rect
.Get())
4198 filRect
= wx
.Rect(*rect
.Get())
4199 filRect
.Deflate(1,1)
4201 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4202 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4204 flrect
= float(filRect
.height
)
4206 rstep
= float((r2
- r1
)) / flrect
4207 gstep
= float((g2
- g1
)) / flrect
4208 bstep
= float((b2
- b1
)) / flrect
4210 rf
, gf
, bf
= 0, 0, 0
4211 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4213 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4214 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4215 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4216 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4221 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4222 dc
.SetPen(wx
.Pen(outer
))
4223 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4224 bdrRect
.Deflate(1, 1)
4225 dc
.SetPen(wx
.Pen(inner
))
4226 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4229 dc
.SetBrush(oldbrush
)
4232 def PaintItem(self
, item
, dc
):
4233 """Actually paint an item."""
4235 attr
= item
.GetAttributes()
4237 if attr
and attr
.HasFont():
4238 dc
.SetFont(attr
.GetFont())
4240 dc
.SetFont(self
._boldFont
)
4241 if item
.IsHyperText():
4242 dc
.SetFont(self
.GetHyperTextFont())
4243 if item
.GetVisited():
4244 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4246 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4248 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4250 image
= item
.GetCurrentImage()
4251 checkimage
= item
.GetCurrentCheckedImage()
4252 image_w
, image_h
= 0, 0
4254 if image
!= _NO_IMAGE
:
4256 if self
._imageListNormal
:
4258 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4265 if item
.GetType() != 0:
4266 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4269 wcheck
, hcheck
= 0, 0
4271 total_h
= self
.GetLineHeight(item
)
4272 drawItemBackground
= False
4274 if item
.IsSelected():
4276 # under mac selections are only a rectangle in case they don't have the focus
4277 if wx
.Platform
== "__WXMAC__":
4278 if not self
._hasFocus
:
4279 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4280 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4282 dc
.SetBrush(self
._hilightBrush
)
4284 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4285 drawItemBackground
= True
4287 if attr
and attr
.HasBackgroundColour():
4288 drawItemBackground
= True
4289 colBg
= attr
.GetBackgroundColour()
4291 colBg
= self
._backgroundColour
4293 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4294 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4296 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4298 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4300 w
, h
= self
.GetClientSize()
4302 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4304 if item
.IsSelected():
4305 if self
._usegradients
:
4306 if self
._gradientstyle
== 0: # Horizontal
4307 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4309 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4310 elif self
._vistaselection
:
4311 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4313 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4314 flags
= wx
.CONTROL_SELECTED
4315 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4316 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4318 dc
.DrawRectangleRect(itemrect
)
4322 if item
.IsSelected():
4324 # If it's selected, and there's an image, then we should
4325 # take care to leave the area under the image painted in the
4326 # background colour.
4328 wnd
= item
.GetWindow()
4331 wndx
, wndy
= item
.GetWindowSize()
4333 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4335 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4338 if self
._usegradients
:
4339 if self
._gradientstyle
== 0: # Horizontal
4340 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4342 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4343 elif self
._vistaselection
:
4344 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4346 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4347 flags
= wx
.CONTROL_SELECTED
4348 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4349 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4351 dc
.DrawRectangleRect(itemrect
)
4353 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4354 # don't allow backgrounds to be customized. Not drawing the background,
4355 # except for custom item backgrounds, works for both kinds of theme.
4356 elif drawItemBackground
:
4358 minusicon
= wcheck
+ image_w
- 2
4359 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4361 item
.GetWidth()-minusicon
,
4364 if self
._usegradients
and self
._hasFocus
:
4365 if self
._gradientstyle
== 0: # Horizontal
4366 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4368 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4370 dc
.DrawRectangleRect(itemrect
)
4372 if image
!= _NO_IMAGE
:
4374 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4375 if item
.IsEnabled():
4376 imglist
= self
._imageListNormal
4378 imglist
= self
._grayedImageList
4380 imglist
.Draw(image
, dc
,
4381 item
.GetX() + wcheck
,
4382 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4383 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4385 dc
.DestroyClippingRegion()
4388 if item
.IsEnabled():
4389 imglist
= self
._imageListCheck
4391 imglist
= self
._grayedCheckList
4393 imglist
.Draw(checkimage
, dc
,
4395 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4396 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4398 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4399 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4401 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4403 if not item
.IsEnabled():
4404 foreground
= dc
.GetTextForeground()
4405 dc
.SetTextForeground(self
._disabledColour
)
4406 dc
.DrawLabel(item
.GetText(), textrect
)
4407 dc
.SetTextForeground(foreground
)
4409 if wx
.Platform
== "__WXMAC__" and item
.IsSelected() and self
._hasFocus
:
4410 dc
.SetTextForeground(wx
.WHITE
)
4411 dc
.DrawLabel(item
.GetText(), textrect
)
4413 wnd
= item
.GetWindow()
4415 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4416 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4418 if not wnd
.IsShown():
4420 if wnd
.GetPosition() != (wndx
, ya
):
4421 wnd
.SetPosition((wndx
, ya
))
4423 # restore normal font
4424 dc
.SetFont(self
._normalFont
)
4427 # Now y stands for the top of the item, whereas it used to stand for middle !
4428 def PaintLevel(self
, item
, dc
, level
, y
):
4429 """Paint a level of CustomTreeCtrl."""
4431 x
= level
*self
._indent
4433 if not self
.HasFlag(TR_HIDE_ROOT
):
4439 # always expand hidden root
4441 children
= item
.GetChildren()
4442 count
= len(children
)
4448 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4451 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4453 # draw line down to last child
4454 origY
+= self
.GetLineHeight(children
[0])>>1
4455 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4456 dc
.DrawLine(3, origY
, 3, oldY
)
4460 item
.SetX(x
+self
._spacing
)
4463 h
= self
.GetLineHeight(item
)
4465 y_mid
= y_top
+ (h
>>1)
4468 exposed_x
= dc
.LogicalToDeviceX(0)
4469 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4471 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4472 if wx
.Platform
== "__WXMAC__":
4473 # don't draw rect outline if we already have the
4474 # background color under Mac
4475 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4477 pen
= self
._borderPen
4479 if item
.IsSelected():
4480 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4481 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4483 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4485 attr
= item
.GetAttributes()
4486 if attr
and attr
.HasTextColour():
4487 colText
= attr
.GetTextColour()
4489 colText
= self
.GetForegroundColour()
4491 if self
._vistaselection
:
4495 dc
.SetTextForeground(colText
)
4500 self
.PaintItem(item
, dc
)
4502 if self
.HasFlag(TR_ROW_LINES
):
4504 # if the background colour is white, choose a
4505 # contrasting color for the lines
4506 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4507 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4508 dc
.DrawLine(0, y_top
, 10000, y_top
)
4509 dc
.DrawLine(0, y
, 10000, y
)
4511 # restore DC objects
4512 dc
.SetBrush(wx
.WHITE_BRUSH
)
4513 dc
.SetTextForeground(wx
.BLACK
)
4515 if not self
.HasFlag(TR_NO_LINES
):
4517 # draw the horizontal line here
4518 dc
.SetPen(self
._dottedPen
)
4520 if x
> self
._indent
:
4521 x_start
-= self
._indent
4522 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4524 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4527 # should the item show a button?
4528 if item
.HasPlus() and self
.HasButtons():
4530 if self
._imageListButtons
:
4532 # draw the image button here
4535 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4536 if item
.IsSelected():
4537 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4539 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4541 yy
= y_mid
- image_h
/2
4543 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4544 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4545 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4546 dc
.DestroyClippingRegion()
4548 else: # no custom buttons
4550 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4551 # We draw something like the Mac twist buttons
4553 dc
.SetPen(wx
.BLACK_PEN
)
4554 dc
.SetBrush(self
._hilightBrush
)
4555 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4557 if item
.IsExpanded():
4559 button
[0].y
= y_mid
- 3
4561 button
[1].y
= button
[0].y
4563 button
[2].y
= button
[0].y
+ 6
4566 button
[0].y
= y_mid
- 5
4567 button
[1].x
= button
[0].x
4568 button
[1].y
= y_mid
+ 5
4569 button
[2].x
= button
[0].x
+ 5
4572 dc
.DrawPolygon(button
)
4575 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4582 if item
.IsExpanded():
4583 flag |
= _CONTROL_EXPANDED
4584 if item
== self
._underMouse
:
4585 flag |
= _CONTROL_CURRENT
4587 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4589 if item
.IsExpanded():
4591 children
= item
.GetChildren()
4592 count
= len(children
)
4601 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4604 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4606 # draw line down to last child
4607 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4608 if self
.HasButtons():
4611 # Only draw the portion of the line that is visible, in case it is huge
4612 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4613 yOrigin
= abs(yOrigin
)
4614 width
, height
= self
.GetClientSize()
4616 # Move end points to the begining/end of the view?
4619 if oldY
> yOrigin
+ height
:
4620 oldY
= yOrigin
+ height
4622 # after the adjustments if y_mid is larger than oldY then the line
4623 # isn't visible at all so don't draw anything
4625 dc
.SetPen(self
._dottedPen
)
4626 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4631 # -----------------------------------------------------------------------------
4632 # wxWidgets callbacks
4633 # -----------------------------------------------------------------------------
4635 def OnPaint(self
, event
):
4636 """Handles the wx.EVT_PAINT event."""
4638 dc
= wx
.PaintDC(self
)
4641 if not self
._anchor
:
4644 dc
.SetFont(self
._normalFont
)
4645 dc
.SetPen(self
._dottedPen
)
4648 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4651 def OnEraseBackground(self
, event
):
4652 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4654 # Can we actually do something here (or in OnPaint()) To Handle
4655 # background images that are stretchable or always centered?
4656 # I tried but I get enormous flickering...
4658 if not self
._backgroundImage
:
4662 if self
._imageStretchStyle
== _StyleTile
:
4666 dc
= wx
.ClientDC(self
)
4667 rect
= self
.GetUpdateRegion().GetBox()
4668 dc
.SetClippingRect(rect
)
4670 self
.TileBackground(dc
)
4673 def TileBackground(self
, dc
):
4674 """Tiles the background image to fill all the available area."""
4676 sz
= self
.GetClientSize()
4677 w
= self
._backgroundImage
.GetWidth()
4678 h
= self
._backgroundImage
.GetHeight()
4685 while y
< sz
.height
:
4686 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4692 def OnSetFocus(self
, event
):
4693 """Handles the wx.EVT_SET_FOCUS event."""
4695 self
._hasFocus
= True
4696 self
.RefreshSelected()
4700 def OnKillFocus(self
, event
):
4701 """Handles the wx.EVT_KILL_FOCUS event."""
4703 self
._hasFocus
= False
4704 self
.RefreshSelected()
4708 def OnKeyDown(self
, event
):
4709 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4711 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4713 te
.SetEventObject(self
)
4715 if self
.GetEventHandler().ProcessEvent(te
):
4716 # intercepted by the user code
4719 if self
._current
is None or self
._key
_current
is None:
4724 # how should the selection work for this event?
4725 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4729 # * : Expand all/Collapse all
4730 # ' ' | return : activate
4731 # up : go up (not last children!)
4733 # left : go to parent
4734 # right : open if parent and go next
4736 # end : go to last item without opening parents
4737 # alnum : start or continue searching for the item with this prefix
4739 keyCode
= event
.GetKeyCode()
4741 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4742 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4743 self
.Expand(self
._current
)
4745 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4746 if not self
.IsExpanded(self
._current
) and self
.IsEnabled(self
._current
):
4748 self
.ExpandAll(self
._current
)
4750 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4751 if self
.IsExpanded(self
._current
):
4752 self
.Collapse(self
._current
)
4754 elif keyCode
== wx
.WXK_MENU
:
4755 # Use the item's bounding rectangle to determine position for the event
4756 itemRect
= self
.GetBoundingRect(self
._current
, True)
4757 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4758 event
._item
= self
._current
4759 # Use the left edge, vertical middle
4760 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4761 event
.SetEventObject(self
)
4762 self
.GetEventHandler().ProcessEvent(event
)
4764 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4766 if not self
.IsEnabled(self
._current
):
4770 if not event
.HasModifiers():
4771 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4772 event
._item
= self
._current
4773 event
.SetEventObject(self
)
4774 self
.GetEventHandler().ProcessEvent(event
)
4776 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4777 checked
= not self
.IsItemChecked(self
._current
)
4778 self
.CheckItem(self
._current
, checked
)
4780 # in any case, also generate the normal key event for this key,
4781 # even if we generated the ACTIVATED event above: this is what
4782 # wxMSW does and it makes sense because you might not want to
4783 # process ACTIVATED event at all and handle Space and Return
4784 # directly (and differently) which would be impossible otherwise
4787 # up goes to the previous sibling or to the last
4788 # of its children if it's expanded
4789 elif keyCode
== wx
.WXK_UP
:
4790 prev
= self
.GetPrevSibling(self
._key
_current
)
4792 prev
= self
.GetItemParent(self
._key
_current
)
4793 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4797 current
= self
._key
_current
4798 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4799 if current
== self
.GetFirstChild(prev
)[0] and self
.IsEnabled(prev
):
4800 # otherwise we return to where we came from
4801 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4802 self
._key
_current
= prev
4805 current
= self
._key
_current
4807 # We are going to another parent node
4808 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4809 child
= self
.GetLastChild(prev
)
4814 # Try to get the previous siblings and see if they are active
4815 while prev
and not self
.IsEnabled(prev
):
4816 prev
= self
.GetPrevSibling(prev
)
4819 # No previous siblings active: go to the parent and up
4820 prev
= self
.GetItemParent(current
)
4821 while prev
and not self
.IsEnabled(prev
):
4822 prev
= self
.GetItemParent(prev
)
4825 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4826 self
._key
_current
= prev
4828 # left arrow goes to the parent
4829 elif keyCode
== wx
.WXK_LEFT
:
4831 prev
= self
.GetItemParent(self
._current
)
4832 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4833 # don't go to root if it is hidden
4834 prev
= self
.GetPrevSibling(self
._current
)
4836 if self
.IsExpanded(self
._current
):
4837 self
.Collapse(self
._current
)
4839 if prev
and self
.IsEnabled(prev
):
4840 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4842 elif keyCode
== wx
.WXK_RIGHT
:
4843 # this works the same as the down arrow except that we
4844 # also expand the item if it wasn't expanded yet
4845 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4846 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4847 if self
.IsEnabled(child
):
4848 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4849 self
._key
_current
= child
4851 self
.Expand(self
._current
)
4854 elif keyCode
== wx
.WXK_DOWN
:
4855 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4857 child
= self
.GetNextActiveItem(self
._key
_current
)
4860 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4861 self
._key
_current
= child
4865 next
= self
.GetNextSibling(self
._key
_current
)
4868 current
= self
._key
_current
4869 while current
and not next
:
4870 current
= self
.GetItemParent(current
)
4872 next
= self
.GetNextSibling(current
)
4873 if not next
or not self
.IsEnabled(next
):
4877 while next
and not self
.IsEnabled(next
):
4878 next
= self
.GetNext(next
)
4881 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4882 self
._key
_current
= next
4885 # <End> selects the last visible tree item
4886 elif keyCode
== wx
.WXK_END
:
4888 last
= self
.GetRootItem()
4890 while last
and self
.IsExpanded(last
):
4892 lastChild
= self
.GetLastChild(last
)
4894 # it may happen if the item was expanded but then all of
4895 # its children have been deleted - so IsExpanded() returned
4896 # true, but GetLastChild() returned invalid item
4902 if last
and self
.IsEnabled(last
):
4904 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4906 # <Home> selects the root item
4907 elif keyCode
== wx
.WXK_HOME
:
4909 prev
= self
.GetRootItem()
4914 if self
.HasFlag(TR_HIDE_ROOT
):
4915 prev
, cookie
= self
.GetFirstChild(prev
)
4919 if self
.IsEnabled(prev
):
4920 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4924 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4925 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4926 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4928 # find the next item starting with the given prefix
4930 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4936 if self
.IsEnabled(id):
4938 self
._findPrefix
+= ch
4940 # also start the timer to reset the current prefix if the user
4941 # doesn't press any more alnum keys soon -- we wouldn't want
4942 # to use this prefix for a new item search
4943 if not self
._findTimer
:
4944 self
._findTimer
= TreeFindTimer(self
)
4946 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4953 def GetNextActiveItem(self
, item
, down
=True):
4954 """Returns the next active item. Used Internally at present. """
4957 sibling
= self
.GetNextSibling
4959 sibling
= self
.GetPrevSibling
4961 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4962 # Is an unchecked radiobutton... all its children are inactive
4963 # try to get the next/previous sibling
4967 child
= sibling(item
)
4968 if (child
and self
.IsEnabled(child
)) or not child
:
4973 # Tha's not a radiobutton... but some of its children can be
4975 child
, cookie
= self
.GetFirstChild(item
)
4976 while child
and not self
.IsEnabled(child
):
4977 child
, cookie
= self
.GetNextChild(item
, cookie
)
4979 if child
and self
.IsEnabled(child
):
4985 def HitTest(self
, point
, flags
=0):
4987 Calculates which (if any) item is under the given point, returning the tree item
4988 at this point plus extra information flags. Flags is a bitlist of the following:
4990 TREE_HITTEST_ABOVE above the client area
4991 TREE_HITTEST_BELOW below the client area
4992 TREE_HITTEST_NOWHERE no item has been hit
4993 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4994 TREE_HITTEST_ONITEMICON on the icon associated to an item
4995 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4996 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4997 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4998 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4999 TREE_HITTEST_TOLEFT on the left of the client area
5000 TREE_HITTEST_TORIGHT on the right of the client area
5001 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
5002 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
5003 TREE_HITTEST_ONITEM anywhere on the item
5005 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
5008 w
, h
= self
.GetSize()
5012 flags |
= TREE_HITTEST_TOLEFT
5014 flags |
= TREE_HITTEST_TORIGHT
5016 flags |
= TREE_HITTEST_ABOVE
5018 flags |
= TREE_HITTEST_BELOW
5023 if self
._anchor
== None:
5024 flags
= TREE_HITTEST_NOWHERE
5027 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5030 flags
= TREE_HITTEST_NOWHERE
5033 if not self
.IsEnabled(hit
):
5039 def GetBoundingRect(self
, item
, textOnly
=False):
5040 """Gets the bounding rectangle of the item."""
5043 raise Exception("\nERROR: Invalid Tree Item. ")
5047 startX
, startY
= self
.GetViewStart()
5050 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5051 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5052 rect
.width
= i
.GetWidth()
5053 rect
.height
= self
.GetLineHeight(i
)
5058 def Edit(self
, item
):
5060 Internal function. Starts the editing of an item label, sending a
5061 EVT_TREE_BEGIN_LABEL_EDIT event.
5064 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5066 te
.SetEventObject(self
)
5067 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5071 # We have to call this here because the label in
5072 # question might just have been added and no screen
5073 # update taken place.
5075 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5080 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5081 self
._textCtrl
.StopEditing()
5083 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5084 self
._textCtrl
.SetFocus()
5087 def GetEditControl(self
):
5089 Returns a pointer to the edit TextCtrl if the item is being edited or
5090 None otherwise (it is assumed that no more than one item may be edited
5094 return self
._textCtrl
5097 def OnRenameAccept(self
, item
, value
):
5099 Called by TreeTextCtrl, to accept the changes and to send the
5100 EVT_TREE_END_LABEL_EDIT event.
5103 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5105 le
.SetEventObject(self
)
5107 le
._editCancelled
= False
5109 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5112 def OnRenameCancelled(self
, item
):
5114 Called by TreeTextCtrl, to cancel the changes and to send the
5115 EVT_TREE_END_LABEL_EDIT event.
5118 # let owner know that the edit was cancelled
5119 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5121 le
.SetEventObject(self
)
5123 le
._editCancelled
= True
5125 self
.GetEventHandler().ProcessEvent(le
)
5128 def OnRenameTimer(self
):
5129 """The timer for renaming has expired. Start editing."""
5131 self
.Edit(self
._current
)
5134 def OnMouse(self
, event
):
5135 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5137 if not self
._anchor
:
5140 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5142 # Is the mouse over a tree item button?
5144 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5145 underMouse
= thisItem
5146 underMouseChanged
= underMouse
!= self
._underMouse
5148 if underMouse
and (flags
& TREE_HITTEST_ONITEM
) and not event
.LeftIsDown() and \
5149 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5150 underMouse
= underMouse
5154 if underMouse
!= self
._underMouse
:
5155 if self
._underMouse
:
5156 # unhighlight old item
5157 self
._underMouse
= None
5159 self
._underMouse
= underMouse
5161 # Determines what item we are hovering over and need a tooltip for
5162 hoverItem
= thisItem
5164 # We do not want a tooltip if we are dragging, or if the rename timer is running
5165 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5167 if hoverItem
is not None:
5168 # Ask the tree control what tooltip (if any) should be shown
5169 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5170 hevent
._item
= hoverItem
5171 hevent
.SetEventObject(self
)
5173 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5174 self
.SetToolTip(hevent
._label
)
5176 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5177 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5178 self
._isonhyperlink
= True
5180 if self
._isonhyperlink
:
5181 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5182 self
._isonhyperlink
= False
5184 # we process left mouse up event (enables in-place edit), right down
5185 # (pass to the user code), left dbl click (activate item) and
5186 # dragging/moving events for items drag-and-drop
5188 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5189 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5195 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5197 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5199 if self
._dragCount
== 0:
5200 self
._dragStart
= pt
5203 self
._dragCount
= self
._dragCount
+ 1
5205 if self
._dragCount
!= 3:
5206 # wait until user drags a bit further...
5209 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5211 nevent
= TreeEvent(command
, self
.GetId())
5212 nevent
._item
= self
._current
5213 nevent
.SetEventObject(self
)
5214 newpt
= self
.CalcScrolledPosition(pt
)
5215 nevent
.SetPoint(newpt
)
5217 # by default the dragging is not supported, the user code must
5218 # explicitly allow the event for it to take place
5221 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5223 # we're going to drag this item
5224 self
._isDragging
= True
5226 # remember the old cursor because we will change it while
5228 self
._oldCursor
= self
._cursor
5230 # in a single selection control, hide the selection temporarily
5231 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5232 self
._oldSelection
= self
.GetSelection()
5234 if self
._oldSelection
:
5236 self
._oldSelection
.SetHilight(False)
5237 self
.RefreshLine(self
._oldSelection
)
5239 selections
= self
.GetSelections()
5240 if len(selections
) == 1:
5241 self
._oldSelection
= selections
[0]
5242 self
._oldSelection
.SetHilight(False)
5243 self
.RefreshLine(self
._oldSelection
)
5248 # Create the custom draw image from the icons and the text of the item
5249 self
._dragImage
= DragImage(self
, self
._current
)
5250 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5251 self
._dragImage
.Show()
5252 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5254 elif event
.Dragging() and self
._isDragging
:
5256 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5258 if self
._countDrag
== 0 and item
:
5259 self
._oldItem
= item
5261 if item
!= self
._dropTarget
:
5263 # unhighlight the previous drop target
5264 if self
._dropTarget
:
5265 self
._dropTarget
.SetHilight(False)
5266 self
.RefreshLine(self
._dropTarget
)
5268 item
.SetHilight(True)
5269 self
.RefreshLine(item
)
5270 self
._countDrag
= self
._countDrag
+ 1
5271 self
._dropTarget
= item
5275 if self
._countDrag
>= 3:
5276 # Here I am trying to avoid ugly repainting problems... hope it works
5277 self
.RefreshLine(self
._oldItem
)
5280 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5283 self
._dragImage
.EndDrag()
5285 if self
._dropTarget
:
5286 self
._dropTarget
.SetHilight(False)
5288 if self
._oldSelection
:
5290 self
._oldSelection
.SetHilight(True)
5291 self
.RefreshLine(self
._oldSelection
)
5292 self
._oldSelection
= None
5294 # generate the drag end event
5295 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5297 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5298 event
.SetEventObject(self
)
5300 self
.GetEventHandler().ProcessEvent(event
)
5302 self
._isDragging
= False
5303 self
._dropTarget
= None
5305 self
.SetCursor(self
._oldCursor
)
5307 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5310 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5315 # If we got to this point, we are not dragging or moving the mouse.
5316 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5317 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5318 # We skip even if we didn't hit an item because we still should
5319 # restore focus to the tree control even if we didn't exactly hit an item.
5320 if event
.LeftDown():
5321 self
._hasFocus
= True
5322 self
.SetFocusIgnoringChildren()
5325 # here we process only the messages which happen on tree items
5330 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5331 self
._textCtrl
.StopEditing()
5332 return # we hit the blank area
5334 if event
.RightDown():
5336 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5337 self
._textCtrl
.StopEditing()
5339 self
._hasFocus
= True
5340 self
.SetFocusIgnoringChildren()
5342 # If the item is already selected, do not update the selection.
5343 # Multi-selections should not be cleared if a selected item is clicked.
5344 if not self
.IsSelected(item
):
5346 self
.DoSelectItem(item
, True, False)
5348 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5350 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5351 nevent
.SetEventObject(self
)
5352 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5354 # Consistent with MSW (for now), send the ITEM_MENU *after*
5355 # the RIGHT_CLICK event. TODO: This behaviour may change.
5356 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5357 nevent2
._item
= item
5358 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5359 nevent2
.SetEventObject(self
)
5360 self
.GetEventHandler().ProcessEvent(nevent2
)
5362 elif event
.LeftUp():
5364 # this facilitates multiple-item drag-and-drop
5366 if self
.HasFlag(TR_MULTIPLE
):
5368 selections
= self
.GetSelections()
5370 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5372 self
.DoSelectItem(item
, True, False)
5374 if self
._lastOnSame
:
5376 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5378 if self
._renameTimer
:
5380 if self
._renameTimer
.IsRunning():
5382 self
._renameTimer
.Stop()
5386 self
._renameTimer
= TreeRenameTimer(self
)
5388 self
._renameTimer
.Start(_DELAY
, True)
5390 self
._lastOnSame
= False
5393 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5395 if not item
or not item
.IsEnabled():
5396 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5397 self
._textCtrl
.StopEditing()
5400 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5401 self
._textCtrl
.StopEditing()
5403 self
._hasFocus
= True
5404 self
.SetFocusIgnoringChildren()
5406 if event
.LeftDown():
5408 self
._lastOnSame
= item
== self
._current
5410 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5412 # only toggle the item for a single click, double click on
5413 # the button doesn't do anything (it toggles the item twice)
5414 if event
.LeftDown():
5418 # don't select the item if the button was clicked
5421 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5423 if event
.LeftDown():
5425 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5429 # clear the previously selected items, if the
5430 # user clicked outside of the present selection.
5431 # otherwise, perform the deselection on mouse-up.
5432 # this allows multiple drag and drop to work.
5433 # but if Cmd is down, toggle selection of the clicked item
5434 if not self
.IsSelected(item
) or event
.CmdDown():
5436 if flags
& TREE_HITTEST_ONITEM
:
5437 # how should the selection work for this event?
5438 if item
.IsHyperText():
5439 self
.SetItemVisited(item
, True)
5441 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5445 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5447 # For some reason, Windows isn't recognizing a left double-click,
5448 # so we need to simulate it here. Allow 200 milliseconds for now.
5449 if event
.LeftDClick():
5451 # double clicking should not start editing the item label
5452 if self
._renameTimer
:
5453 self
._renameTimer
.Stop()
5455 self
._lastOnSame
= False
5457 # send activate event first
5458 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5460 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5461 nevent
.SetEventObject(self
)
5462 if not self
.GetEventHandler().ProcessEvent(nevent
):
5464 # if the user code didn't process the activate event,
5465 # handle it ourselves by toggling the item when it is
5467 ## if item.HasPlus():
5471 def OnInternalIdle(self
):
5472 """Performs operations in idle time (essentially drawing)."""
5474 # Check if we need to select the root item
5475 # because nothing else has been selected.
5476 # Delaying it means that we can invoke event handlers
5477 # as required, when a first item is selected.
5478 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5481 self
.SelectItem(self
._select
_me
)
5482 elif self
.GetRootItem():
5483 self
.SelectItem(self
.GetRootItem())
5485 # after all changes have been done to the tree control,
5486 # we actually redraw the tree when everything is over
5490 if self
._freezeCount
:
5495 self
.CalculatePositions()
5497 self
.AdjustMyScrollbars()
5502 def CalculateSize(self
, item
, dc
):
5503 """Calculates overall position and size of an item."""
5505 attr
= item
.GetAttributes()
5507 if attr
and attr
.HasFont():
5508 dc
.SetFont(attr
.GetFont())
5510 dc
.SetFont(self
._boldFont
)
5512 dc
.SetFont(self
._normalFont
)
5514 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5517 # restore normal font
5518 dc
.SetFont(self
._normalFont
)
5520 image_w
, image_h
= 0, 0
5521 image
= item
.GetCurrentImage()
5523 if image
!= _NO_IMAGE
:
5525 if self
._imageListNormal
:
5527 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5530 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5532 checkimage
= item
.GetCurrentCheckedImage()
5533 if checkimage
is not None:
5534 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5540 total_h
+= 2 # at least 2 pixels
5542 total_h
+= total_h
/10 # otherwise 10% extra spacing
5544 if total_h
> self
._lineHeight
:
5545 self
._lineHeight
= total_h
5547 if not item
.GetWindow():
5548 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5549 item
.SetHeight(total_h
)
5551 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5554 def CalculateLevel(self
, item
, dc
, level
, y
):
5555 """Calculates the level of an item."""
5557 x
= level
*self
._indent
5559 if not self
.HasFlag(TR_HIDE_ROOT
):
5565 # a hidden root is not evaluated, but its
5566 # children are always calculated
5567 children
= item
.GetChildren()
5568 count
= len(children
)
5570 for n
in xrange(count
):
5571 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5575 self
.CalculateSize(item
, dc
)
5578 item
.SetX(x
+self
._spacing
)
5580 y
+= self
.GetLineHeight(item
)
5582 if not item
.IsExpanded():
5583 # we don't need to calculate collapsed branches
5586 children
= item
.GetChildren()
5587 count
= len(children
)
5589 for n
in xrange(count
):
5590 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5595 def CalculatePositions(self
):
5596 """Calculates all the positions of the visible items."""
5598 if not self
._anchor
:
5601 dc
= wx
.ClientDC(self
)
5604 dc
.SetFont(self
._normalFont
)
5605 dc
.SetPen(self
._dottedPen
)
5607 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5610 def RefreshSubtree(self
, item
):
5611 """Refreshes a damaged subtree of an item."""
5615 if self
._freezeCount
:
5618 client
= self
.GetClientSize()
5621 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5622 rect
.width
= client
.x
5623 rect
.height
= client
.y
5625 self
.Refresh(True, rect
)
5626 self
.AdjustMyScrollbars()
5629 def RefreshLine(self
, item
):
5630 """Refreshes a damaged item line."""
5634 if self
._freezeCount
:
5638 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5639 rect
.width
= self
.GetClientSize().x
5640 rect
.height
= self
.GetLineHeight(item
)
5642 self
.Refresh(True, rect
)
5645 def RefreshSelected(self
):
5646 """Refreshes a damaged selected item line."""
5648 if self
._freezeCount
:
5651 # TODO: this is awfully inefficient, we should keep the list of all
5652 # selected items internally, should be much faster
5654 self
.RefreshSelectedUnder(self
._anchor
)
5657 def RefreshSelectedUnder(self
, item
):
5658 """Refreshes the selected items under the given item."""
5660 if self
._freezeCount
:
5663 if item
.IsSelected():
5664 self
.RefreshLine(item
)
5666 children
= item
.GetChildren()
5667 for child
in children
:
5668 self
.RefreshSelectedUnder(child
)
5672 """Freeze CustomTreeCtrl."""
5674 self
._freezeCount
= self
._freezeCount
+ 1
5678 """Thaw CustomTreeCtrl."""
5680 if self
._freezeCount
== 0:
5681 raise Exception("\nERROR: Thawing Unfrozen Tree Control?")
5683 self
._freezeCount
= self
._freezeCount
- 1
5685 if not self
._freezeCount
:
5689 # ----------------------------------------------------------------------------
5690 # changing colours: we need to refresh the tree control
5691 # ----------------------------------------------------------------------------
5693 def SetBackgroundColour(self
, colour
):
5694 """Changes the background colour of CustomTreeCtrl."""
5696 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5699 if self
._freezeCount
:
5707 def SetForegroundColour(self
, colour
):
5708 """Changes the foreground colour of CustomTreeCtrl."""
5710 if not wx
.Window
.SetForegroundColour(self
, colour
):
5713 if self
._freezeCount
:
5721 def OnGetToolTip(self
, event
):
5723 Process the tooltip event, to speed up event processing. Does not actually
5730 def DoGetBestSize(self
):
5731 """Something is better than nothing..."""
5733 # something is better than nothing...
5734 # 100x80 is what the MSW version will get from the default
5735 # wxControl::DoGetBestSize
5737 return wx
.Size(100, 80)
5740 def GetClassDefaultAttributes(self
):
5741 """Gets the class default attributes."""
5743 attr
= wx
.VisualAttributes()
5744 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5745 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5746 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
5749 GetClassDefaultAttributes
= classmethod(GetClassDefaultAttributes
)