1 # --------------------------------------------------------------------------------- #
2 # CUSTOMTREECTRL wxPython IMPLEMENTATION
3 # Inspired By And Heavily Based On wxGenericTreeCtrl.
5 # Andrea Gavana, @ 17 May 2006
6 # Latest Revision: 16 Apr 2007, 11.00 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 @ 16 Apr 2007, 11.00 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?
1172 Returns whether the item is ok or not. Useless on Python, but added for
1173 backward compatibility with the C++ implementation.
1179 def GetChildren(self
):
1180 """Returns the item's children."""
1182 return self
._children
1186 """Returns the item text."""
1191 def GetImage(self
, which
=TreeItemIcon_Normal
):
1192 """Returns the item image for a particular state."""
1194 return self
._images
[which
]
1197 def GetCheckedImage(self
, which
=TreeItemIcon_Checked
):
1198 """Returns the item check image. Meaningful only for radio & check items."""
1200 return self
._checkedimages
[which
]
1204 """Returns the data associated to this item."""
1209 def SetImage(self
, image
, which
):
1210 """Sets the item image."""
1212 self
._images
[which
] = image
1215 def SetData(self
, data
):
1216 """Sets the data associated to this item."""
1221 def SetHasPlus(self
, has
=True):
1222 """Sets whether an item has the 'plus' button."""
1227 def SetBold(self
, bold
):
1228 """Sets the item font bold."""
1233 def SetItalic(self
, italic
):
1234 """Sets the item font italic."""
1236 self
._isItalic
= italic
1240 """Returns the x position on an item in the ScrolledWindow."""
1246 """Returns the y position on an item in the ScrolledWindow."""
1252 """Sets the x position on an item in the ScrolledWindow."""
1258 """Sets the y position on an item in the ScrolledWindow."""
1263 def GetHeight(self
):
1264 """Returns the height of the item."""
1270 """Returns the width of the item."""
1275 def SetHeight(self
, h
):
1276 """Sets the height of the item."""
1281 def SetWidth(self
, w
):
1282 """Sets the width of the item."""
1287 def SetWindow(self
, wnd
):
1288 """Sets the window associated to the item."""
1292 if wnd
.GetSizer(): # the window is a complex one hold by a sizer
1293 size
= wnd
.GetBestSize()
1294 else: # simple window, without sizers
1295 size
= wnd
.GetSize()
1297 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1298 # No other solution to handle the focus changing from an item in
1299 # CustomTreeCtrl and the window associated to an item
1300 # Do better strategies exist?
1301 self
._wnd
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1303 self
._height
= size
.GetHeight() + 2
1304 self
._width
= size
.GetWidth()
1305 self
._windowsize
= size
1307 # We don't show the window if the item is collapsed
1308 if self
._isCollapsed
:
1309 self
._wnd
.Show(False)
1311 # The window is enabled only if the item is enabled
1312 self
._wnd
.Enable(self
._enabled
)
1313 self
._windowenabled
= self
._enabled
1316 def GetWindow(self
):
1317 """Returns the window associated to the item."""
1322 def DeleteWindow(self
):
1323 """Deletes the window associated to the item (if any)."""
1330 def GetWindowEnabled(self
):
1331 """Returns whether the associated window is enabled or not."""
1334 raise Exception("\nERROR: This Item Has No Window Associated")
1336 return self
._windowenabled
1339 def SetWindowEnabled(self
, enable
=True):
1340 """Sets whether the associated window is enabled or not."""
1343 raise Exception("\nERROR: This Item Has No Window Associated")
1345 self
._windowenabled
= enable
1346 self
._wnd
.Enable(enable
)
1349 def GetWindowSize(self
):
1350 """Returns the associated window size."""
1352 return self
._windowsize
1355 def OnSetFocus(self
, event
):
1356 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1358 treectrl
= self
._wnd
.GetParent()
1359 select
= treectrl
.GetSelection()
1361 # If the window is associated to an item that currently is selected
1362 # (has focus) we don't kill the focus. Otherwise we do it.
1364 treectrl
._hasFocus
= False
1366 treectrl
._hasFocus
= True
1373 Returns the item type. It should be one of:
1382 def SetHyperText(self
, hyper
=True):
1383 """Sets whether the item is hypertext or not."""
1385 self
._hypertext
= hyper
1388 def SetVisited(self
, visited
=True):
1389 """Sets whether an hypertext item was visited or not."""
1391 self
._visited
= visited
1394 def GetVisited(self
):
1395 """Returns whether an hypertext item was visited or not."""
1397 return self
._visited
1400 def IsHyperText(self
):
1401 """Returns whether the item is hypetext or not."""
1403 return self
._hypertext
1406 def GetParent(self
):
1407 """Gets the item parent."""
1412 def Insert(self
, child
, index
):
1413 """Inserts an item in the item children."""
1415 self
._children
.insert(index
, child
)
1419 """Expand the item."""
1421 self
._isCollapsed
= False
1425 """Collapse the item."""
1427 self
._isCollapsed
= True
1430 def SetHilight(self
, set=True):
1431 """Sets the item focus/unfocus."""
1433 self
._hasHilight
= set
1436 def HasChildren(self
):
1437 """Returns whether the item has children or not."""
1439 return len(self
._children
) > 0
1442 def IsSelected(self
):
1443 """Returns whether the item is selected or not."""
1445 return self
._hasHilight
!= 0
1448 def IsExpanded(self
):
1449 """Returns whether the item is expanded or not."""
1451 return not self
._isCollapsed
1454 def IsChecked(self
):
1455 """Returns whether the item is checked or not."""
1457 return self
._checked
1460 def Check(self
, checked
=True):
1461 """Check an item. Meaningful only for check and radio items."""
1463 self
._checked
= checked
1467 """Returns whether the item has the plus button or not."""
1469 return self
._hasPlus
or self
.HasChildren()
1473 """Returns whether the item font is bold or not."""
1475 return self
._isBold
!= 0
1479 """Returns whether the item font is italic or not."""
1481 return self
._isItalic
!= 0
1484 def Enable(self
, enable
=True):
1485 """Enables/disables the item."""
1487 self
._enabled
= enable
1490 def IsEnabled(self
):
1491 """Returns whether the item is enabled or not."""
1493 return self
._enabled
1496 def GetAttributes(self
):
1497 """Returns the item attributes (font, colours)."""
1503 """Creates a new attribute (font, colours)."""
1507 self
._attr
= TreeItemAttr()
1508 self
._ownsAttr
= True
1513 def SetAttributes(self
, attr
):
1514 """Sets the item attributes (font, colours)."""
1520 self
._ownsAttr
= False
1523 def AssignAttributes(self
, attr
):
1524 """Assigns the item attributes (font, colours)."""
1526 self
.SetAttributes(attr
)
1527 self
._ownsAttr
= True
1530 def DeleteChildren(self
, tree
):
1531 """Deletes the item children."""
1533 for child
in self
._children
:
1535 tree
.SendDeleteEvent(child
)
1537 child
.DeleteChildren(tree
)
1539 if child
== tree
._select
_me
:
1540 tree
._select
_me
= None
1542 # We have to destroy the associated window
1543 wnd
= child
.GetWindow()
1548 if child
in tree
._itemWithWindow
:
1549 tree
._itemWithWindow
.remove(child
)
1556 def SetText(self
, text
):
1557 """Sets the item text."""
1562 def GetChildrenCount(self
, recursively
=True):
1563 """Gets the number of children."""
1565 count
= len(self
._children
)
1572 for n
in xrange(count
):
1573 total
+= self
._children
[n
].GetChildrenCount()
1578 def GetSize(self
, x
, y
, theButton
):
1579 """Returns the item size."""
1581 bottomY
= self
._y
+ theButton
.GetLineHeight(self
)
1586 width
= self
._x
+ self
._width
1591 if self
.IsExpanded():
1592 for child
in self
._children
:
1593 x
, y
= child
.GetSize(x
, y
, theButton
)
1598 def HitTest(self
, point
, theCtrl
, flags
=0, level
=0):
1600 HitTest method for an item. Called from the main window HitTest.
1601 see the CustomTreeCtrl HitTest method for the flags explanation.
1604 # for a hidden root node, don't evaluate it, but do evaluate children
1605 if not (level
== 0 and theCtrl
.HasFlag(TR_HIDE_ROOT
)):
1608 h
= theCtrl
.GetLineHeight(self
)
1610 if point
.y
> self
._y
and point
.y
< self
._y
+ h
:
1612 y_mid
= self
._y
+ h
/2
1615 flags |
= TREE_HITTEST_ONITEMUPPERPART
1617 flags |
= TREE_HITTEST_ONITEMLOWERPART
1619 xCross
= self
._x
- theCtrl
.GetSpacing()
1621 if wx
.Platform
== "__WXMAC__":
1622 # according to the drawing code the triangels are drawn
1623 # at -4 , -4 from the position up to +10/+10 max
1624 if point
.x
> xCross
-4 and point
.x
< xCross
+10 and point
.y
> y_mid
-4 and \
1625 point
.y
< y_mid
+10 and self
.HasPlus() and theCtrl
.HasButtons():
1627 flags |
= TREE_HITTEST_ONITEMBUTTON
1630 # 5 is the size of the plus sign
1631 if point
.x
> xCross
-6 and point
.x
< xCross
+6 and point
.y
> y_mid
-6 and \
1632 point
.y
< y_mid
+6 and self
.HasPlus() and theCtrl
.HasButtons():
1634 flags |
= TREE_HITTEST_ONITEMBUTTON
1637 if point
.x
>= self
._x
and point
.x
<= self
._x
+ self
._width
:
1642 # assuming every image (normal and selected) has the same size!
1643 if self
.GetImage() != _NO_IMAGE
and theCtrl
._imageListNormal
:
1644 image_w
, image_h
= theCtrl
._imageListNormal
.GetSize(self
.GetImage())
1646 if self
.GetCheckedImage() is not None:
1647 wcheck
, hcheck
= theCtrl
._imageListCheck
.GetSize(self
.GetCheckedImage())
1649 if wcheck
and point
.x
<= self
._x
+ wcheck
+ 1:
1650 flags |
= TREE_HITTEST_ONITEMCHECKICON
1653 if image_w
!= -1 and point
.x
<= self
._x
+ wcheck
+ image_w
+ 1:
1654 flags |
= TREE_HITTEST_ONITEMICON
1656 flags |
= TREE_HITTEST_ONITEMLABEL
1660 if point
.x
< self
._x
:
1661 flags |
= TREE_HITTEST_ONITEMINDENT
1662 if point
.x
> self
._x
+ self
._width
:
1663 flags |
= TREE_HITTEST_ONITEMRIGHT
1667 # if children are expanded, fall through to evaluate them
1668 if self
._isCollapsed
:
1672 for child
in self
._children
:
1673 res
, flags
= child
.HitTest(point
, theCtrl
, flags
, level
+ 1)
1680 def GetCurrentImage(self
):
1681 """Returns the current item image."""
1685 if self
.IsExpanded():
1687 if self
.IsSelected():
1689 image
= self
.GetImage(TreeItemIcon_SelectedExpanded
)
1691 if image
== _NO_IMAGE
:
1693 # we usually fall back to the normal item, but try just the
1694 # expanded one (and not selected) first in this case
1695 image
= self
.GetImage(TreeItemIcon_Expanded
)
1697 else: # not expanded
1699 if self
.IsSelected():
1700 image
= self
.GetImage(TreeItemIcon_Selected
)
1702 # maybe it doesn't have the specific image we want,
1703 # try the default one instead
1704 if image
== _NO_IMAGE
:
1705 image
= self
.GetImage()
1710 def GetCurrentCheckedImage(self
):
1711 """Returns the current item check image."""
1716 if self
.IsChecked():
1717 if self
._type
== 1: # Checkbox
1718 return self
._checkedimages
[TreeItemIcon_Checked
]
1720 return self
._checkedimages
[TreeItemIcon_Flagged
]
1722 if self
._type
== 1: # Checkbox
1723 return self
._checkedimages
[TreeItemIcon_NotChecked
]
1725 return self
._checkedimages
[TreeItemIcon_NotFlagged
]
1728 def EventFlagsToSelType(style
, shiftDown
=False, ctrlDown
=False):
1730 Translate the key or mouse event flag to the type of selection we
1734 is_multiple
= (style
& TR_MULTIPLE
) != 0
1735 extended_select
= shiftDown
and is_multiple
1736 unselect_others
= not (extended_select
or (ctrlDown
and is_multiple
))
1738 return is_multiple
, extended_select
, unselect_others
1741 # -----------------------------------------------------------------------------
1742 # CustomTreeCtrl Main Implementation.
1743 # This Is The Main Class.
1744 # -----------------------------------------------------------------------------
1746 class CustomTreeCtrl(wx
.PyScrolledWindow
):
1748 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1749 style
=TR_DEFAULT_STYLE
, ctstyle
=0, validator
=wx
.DefaultValidator
,
1750 name
="CustomTreeCtrl"):
1752 Default class constructor.
1754 parent: parent window. Must not be none.
1756 id: window identifier. A value of -1 indicates a default value.
1758 pos: window position.
1760 size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
1762 style: the underlying wx.ScrolledWindow style + CustomTreeCtrl window style. This can be one of:
1765 TR_HAS_BUTTONS # draw collapsed/expanded btns
1766 TR_NO_LINES # don't draw lines at all
1767 TR_LINES_AT_ROOT # connect top-level nodes
1768 TR_TWIST_BUTTONS # draw mac-like twist buttons
1769 TR_SINGLE # single selection mode
1770 TR_MULTIPLE # can select multiple items
1771 TR_EXTENDED # todo: allow extended selection
1772 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1773 TR_EDIT_LABELS # can edit item labels
1774 TR_ROW_LINES # put border around items
1775 TR_HIDE_ROOT # don't display root node
1776 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1777 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1778 TR_AUTO_CHECK_PARENT # only meaningful for checkboxes
1779 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1781 ctstyle: kept for backward compatibility.
1783 validator: window validator.
1788 style
= style | ctstyle
1790 self
._current
= self
._key
_current
= self
._anchor
= self
._select
_me
= None
1791 self
._hasFocus
= False
1794 # Default line height: it will soon be changed
1795 self
._lineHeight
= 10
1796 # Item indent wrt parent
1798 # item horizontal spacing between the start and the text
1801 # Brushes for focused/unfocused items (also gradient type)
1802 self
._hilightBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
))
1803 btnshadow
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1804 self
._hilightUnfocusedBrush
= wx
.Brush(btnshadow
)
1805 r
, g
, b
= btnshadow
.Red(), btnshadow
.Green(), btnshadow
.Blue()
1806 backcolour
= (max((r
>> 1) - 20, 0),
1807 max((g
>> 1) - 20, 0),
1808 max((b
>> 1) - 20, 0))
1809 backcolour
= wx
.Colour(backcolour
[0], backcolour
[1], backcolour
[2])
1810 self
._hilightUnfocusedBrush
2 = wx
.Brush(backcolour
)
1812 # image list for icons
1813 self
._imageListNormal
= self
._imageListButtons
= self
._imageListState
= self
._imageListCheck
= None
1814 self
._ownsImageListNormal
= self
._ownsImageListButtons
= self
._ownsImageListState
= False
1816 # Drag and drop initial settings
1819 self
._isDragging
= False
1820 self
._dropTarget
= self
._oldSelection
= None
1821 self
._dragImage
= None
1822 self
._underMouse
= None
1824 # TextCtrl initial settings for editable items
1825 self
._textCtrl
= None
1826 self
._renameTimer
= None
1828 # This one allows us to handle Freeze() and Thaw() calls
1829 self
._freezeCount
= 0
1831 self
._findPrefix
= ""
1832 self
._findTimer
= None
1834 self
._dropEffectAboveItem
= False
1835 self
._lastOnSame
= False
1837 # Default normal and bold fonts for an item
1838 self
._hasFont
= True
1839 self
._normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1840 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1841 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
1842 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1846 self
._hypertextfont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
1847 self
._normalFont
.GetStyle(), wx
.NORMAL
, True,
1848 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
1849 self
._hypertextnewcolour
= wx
.BLUE
1850 self
._hypertextvisitedcolour
= wx
.Colour(200, 47, 200)
1851 self
._isonhyperlink
= False
1853 # Default CustomTreeCtrl background colour.
1854 self
._backgroundColour
= wx
.WHITE
1856 # Background image settings
1857 self
._backgroundImage
= None
1858 self
._imageStretchStyle
= _StyleTile
1860 # Disabled items colour
1861 self
._disabledColour
= wx
.Colour(180, 180, 180)
1863 # Gradient selection colours
1864 self
._firstcolour
= color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
1865 self
._secondcolour
= wx
.WHITE
1866 self
._usegradients
= False
1867 self
._gradientstyle
= 0 # Horizontal Gradient
1869 # Vista Selection Styles
1870 self
._vistaselection
= False
1872 # Connection lines style
1873 if wx
.Platform
!= "__WXMAC__":
1874 self
._dottedPen
= wx
.Pen("grey", 1, wx
.USER_DASH
)
1875 self
._dottedPen
.SetDashes([1,1])
1876 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1878 self
._dottedPen
= wx
.Pen("grey", 1)
1880 # Pen Used To Draw The Border Around Selected Items
1881 self
._borderPen
= wx
.BLACK_PEN
1882 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1884 # For Appended Windows
1885 self
._hasWindows
= False
1886 self
._itemWithWindow
= []
1888 if wx
.Platform
== "__WXMAC__":
1889 style
&= ~TR_LINES_AT_ROOT
1890 style |
= TR_NO_LINES
1892 platform
, major
, minor
= wx
.GetOsVersion()
1894 style |
= TR_ROW_LINES
1896 self
._windowStyle
= style
1898 # Create the default check image list
1899 self
.SetImageListCheck(13, 13)
1901 # A constant to use my translation of RendererNative.DrawTreeItemButton
1902 # if the wxPython version is less than 2.6.2.1.
1903 if wx
.VERSION_STRING
< "2.6.2.1":
1904 self
._drawingfunction
= DrawTreeItemButton
1906 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1908 # Create our container... at last!
1909 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1911 # If the tree display has no buttons, but does have
1912 # connecting lines, we can use a narrower layout.
1913 # It may not be a good idea to force this...
1914 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1918 self
.SetValidator(validator
)
1920 attr
= self
.GetDefaultAttributes()
1921 self
.SetOwnForegroundColour(attr
.colFg
)
1922 self
.SetOwnBackgroundColour(wx
.WHITE
)
1924 if not self
._hasFont
:
1925 self
.SetOwnFont(attr
.font
)
1930 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1931 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1932 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1933 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1934 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1935 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1936 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1937 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1939 # Sets the focus to ourselves: this is useful if you have items
1940 # with associated widgets.
1944 def AcceptsFocus(self
):
1945 # overridden base class method, allows this ctrl to
1946 # participate in the tab-order, etc. It's overridable because
1947 # of deriving this class from wx.PyScrolledWindow...
1951 def OnDestroy(self
, event
):
1952 """Handles the wx.EVT_WINDOW_DESTROY event."""
1954 # Here there may be something I miss... do I have to destroy
1956 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1957 self
._renameTimer
.Stop()
1958 del self
._renameTimer
1960 if self
._findTimer
and self
._findTimer
.IsRunning():
1961 self
._findTimer
.Stop()
1968 """Returns the global number of items in the tree."""
1970 if not self
._anchor
:
1974 count
= self
._anchor
.GetChildrenCount()
1976 if not self
.HasFlag(TR_HIDE_ROOT
):
1977 # take the root itself into account
1983 def GetIndent(self
):
1984 """Returns the item indentation."""
1989 def GetSpacing(self
):
1990 """Returns the spacing between the start and the text."""
1992 return self
._spacing
1995 def GetRootItem(self
):
1996 """Returns the root item."""
2001 def GetSelection(self
):
2002 """Returns the current selection: TR_SINGLE only."""
2004 return self
._current
2007 def ToggleItemSelection(self
, item
):
2008 """Toggles the item selection."""
2011 raise Exception("\nERROR: Invalid Tree Item. ")
2013 self
.SelectItem(item
, not self
.IsSelected(item
))
2016 def EnableChildren(self
, item
, enable
=True):
2017 """Enables/disables item children. Used internally."""
2020 if item
.IsExpanded():
2023 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2024 # We hit a radiobutton item not checked, we don't want to
2025 # enable the children
2028 child
, cookie
= self
.GetFirstChild(item
)
2030 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2032 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2033 self
.EnableChildren(child
, enable
)
2034 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2037 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2038 """Enables/disables an item."""
2041 raise Exception("\nERROR: Invalid Tree Item. ")
2043 if item
.IsEnabled() == enable
:
2046 if not enable
and item
.IsSelected():
2047 self
.SelectItem(item
, False)
2050 wnd
= item
.GetWindow()
2052 # Handles the eventual window associated to the item
2054 wndenable
= item
.GetWindowEnabled()
2062 # We have to refresh the item line
2063 dc
= wx
.ClientDC(self
)
2064 self
.CalculateSize(item
, dc
)
2065 self
.RefreshLine(item
)
2068 def IsItemEnabled(self
, item
):
2069 """Returns whether an item is enabled or disabled."""
2072 raise Exception("\nERROR: Invalid Tree Item. ")
2074 return item
.IsEnabled()
2077 def SetDisabledColour(self
, colour
):
2078 """Sets the items disabled colour."""
2080 self
._disabledColour
= colour
2084 def GetDisabledColour(self
):
2085 """Returns the items disabled colour."""
2087 return self
._disabledColour
2090 def IsItemChecked(self
, item
):
2091 """Returns whether an item is checked or not."""
2094 raise Exception("\nERROR: Invalid Tree Item. ")
2096 return item
.IsChecked()
2099 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2100 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2102 if item
.GetType() == 0:
2108 dc
= wx
.ClientDC(self
)
2109 self
.CalculateSize(item
, dc
)
2110 self
.RefreshLine(item
)
2113 def UnCheckRadioParent(self
, item
, checked
=False):
2114 """Used internally to handle radio node parent correctly."""
2116 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2118 e
.SetEventObject(self
)
2120 if self
.GetEventHandler().ProcessEvent(e
):
2124 self
.RefreshLine(item
)
2125 self
.EnableChildren(item
, checked
)
2126 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2128 e
.SetEventObject(self
)
2129 self
.GetEventHandler().ProcessEvent(e
)
2134 def CheckItem(self
, item
, checked
=True):
2136 Actually checks/uncheks an item, sending (eventually) the two
2137 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2141 raise Exception("\nERROR: Invalid Tree Item. ")
2143 # Should we raise an error here?!?
2144 if item
.GetType() == 0:
2147 if item
.GetType() == 2: # it's a radio button
2148 if not checked
and item
.IsChecked(): # Try To Unckeck?
2149 if item
.HasChildren():
2150 self
.UnCheckRadioParent(item
, checked
)
2153 if not self
.UnCheckRadioParent(item
, checked
):
2156 self
.CheckSameLevel(item
, False)
2159 # Radiobuttons are done, let's handle checkbuttons...
2160 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2162 e
.SetEventObject(self
)
2164 if self
.GetEventHandler().ProcessEvent(e
):
2169 dc
= wx
.ClientDC(self
)
2170 self
.RefreshLine(item
)
2172 if self
._windowStyle
& TR_AUTO_CHECK_CHILD
:
2173 ischeck
= self
.IsItemChecked(item
)
2174 self
.AutoCheckChild(item
, ischeck
)
2175 if self
._windowStyle
& TR_AUTO_CHECK_PARENT
:
2176 ischeck
= self
.IsItemChecked(item
)
2177 self
.AutoCheckParent(item
, ischeck
)
2178 elif self
._windowStyle
& TR_AUTO_TOGGLE_CHILD
:
2179 self
.AutoToggleChild(item
)
2181 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2183 e
.SetEventObject(self
)
2184 self
.GetEventHandler().ProcessEvent(e
)
2187 def AutoToggleChild(self
, item
):
2188 """Transverses the tree and toggles the items. Meaningful only for check items."""
2191 raise Exception("\nERROR: Invalid Tree Item. ")
2193 child
, cookie
= self
.GetFirstChild(item
)
2196 if item
.IsExpanded():
2201 if child
.GetType() == 1 and child
.IsEnabled():
2202 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2203 self
.AutoToggleChild(child
)
2204 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2207 def AutoCheckChild(self
, item
, checked
):
2208 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2211 raise Exception("\nERROR: Invalid Tree Item. ")
2213 (child
, cookie
) = self
.GetFirstChild(item
)
2216 if item
.IsExpanded():
2220 if child
.GetType() == 1 and child
.IsEnabled():
2221 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2222 self
.AutoCheckChild(child
, checked
)
2223 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2226 def AutoCheckParent(self
, item
, checked
):
2227 """Traverses up the tree and checks/unchecks parent items.
2228 Meaningful only for check items."""
2231 raise Exception("\nERROR: Invalid Tree Item. ")
2233 parent
= item
.GetParent()
2234 if not parent
or parent
.GetType() != 1:
2237 (child
, cookie
) = self
.GetFirstChild(parent
)
2239 if child
.GetType() == 1 and child
.IsEnabled():
2240 if checked
!= child
.IsChecked():
2242 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2244 self
.CheckItem2(parent
, checked
, torefresh
=True)
2245 self
.AutoCheckParent(parent
, checked
)
2248 def CheckChilds(self
, item
, checked
=True):
2249 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2252 raise Exception("\nERROR: Invalid Tree Item. ")
2255 self
.AutoToggleChild(item
)
2257 self
.AutoCheckChild(item
, checked
)
2260 def CheckSameLevel(self
, item
, checked
=False):
2262 Uncheck radio items which are on the same level of the checked one.
2266 parent
= item
.GetParent()
2272 if parent
.IsExpanded():
2275 (child
, cookie
) = self
.GetFirstChild(parent
)
2277 if child
.GetType() == 2 and child
!= item
:
2278 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2279 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2280 self
.EnableChildren(child
, checked
)
2281 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2284 def EditLabel(self
, item
):
2285 """Starts editing an item label."""
2288 raise Exception("\nERROR: Invalid Tree Item. ")
2293 def ShouldInheritColours(self
):
2294 """We don't inherit colours from anyone."""
2299 def SetIndent(self
, indent
):
2300 """Sets item indentation."""
2302 self
._indent
= indent
2306 def SetSpacing(self
, spacing
):
2307 """Sets item spacing."""
2309 self
._spacing
= spacing
2313 def HasFlag(self
, flag
):
2314 """Returns whether CustomTreeCtrl has a flag."""
2316 return self
._windowStyle
& flag
2319 def HasChildren(self
, item
):
2320 """Returns whether an item has children or not."""
2323 raise Exception("\nERROR: Invalid Tree Item. ")
2325 return len(item
.GetChildren()) > 0
2328 def GetChildrenCount(self
, item
, recursively
=True):
2329 """Gets the item children count."""
2332 raise Exception("\nERROR: Invalid Tree Item. ")
2334 return item
.GetChildrenCount(recursively
)
2337 def SetTreeStyle(self
, styles
):
2338 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2340 # Do not try to expand the root node if it hasn't been created yet
2341 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2343 # if we will hide the root, make sure children are visible
2344 self
._anchor
.SetHasPlus()
2345 self
._anchor
.Expand()
2346 self
.CalculatePositions()
2348 # right now, just sets the styles. Eventually, we may
2349 # want to update the inherited styles, but right now
2350 # none of the parents has updatable styles
2352 if self
._windowStyle
& TR_MULTIPLE
and not (styles
& TR_MULTIPLE
):
2353 selections
= self
.GetSelections()
2354 for select
in selections
[0:-1]:
2355 self
.SelectItem(select
, False)
2357 self
._windowStyle
= styles
2361 def GetTreeStyle(self
):
2362 """Returns the CustomTreeCtrl style."""
2364 return self
._windowStyle
2367 def HasButtons(self
):
2368 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2370 return self
.HasFlag(TR_HAS_BUTTONS
)
2373 # -----------------------------------------------------------------------------
2374 # functions to work with tree items
2375 # -----------------------------------------------------------------------------
2377 def GetItemText(self
, item
):
2378 """Returns the item text."""
2381 raise Exception("\nERROR: Invalid Tree Item. ")
2383 return item
.GetText()
2386 def GetItemImage(self
, item
, which
=TreeItemIcon_Normal
):
2387 """Returns the item image."""
2390 raise Exception("\nERROR: Invalid Tree Item. ")
2392 return item
.GetImage(which
)
2395 def GetPyData(self
, item
):
2396 """Returns the data associated to an item."""
2399 raise Exception("\nERROR: Invalid Tree Item. ")
2401 return item
.GetData()
2403 GetItemPyData
= GetPyData
2406 def GetItemTextColour(self
, item
):
2407 """Returns the item text colour."""
2410 raise Exception("\nERROR: Invalid Tree Item. ")
2412 return item
.Attr().GetTextColour()
2415 def GetItemBackgroundColour(self
, item
):
2416 """Returns the item background colour."""
2419 raise Exception("\nERROR: Invalid Tree Item. ")
2421 return item
.Attr().GetBackgroundColour()
2424 def GetItemFont(self
, item
):
2425 """Returns the item font."""
2428 raise Exception("\nERROR: Invalid Tree Item. ")
2430 return item
.Attr().GetFont()
2433 def IsItemHyperText(self
, item
):
2434 """Returns whether an item is hypertext or not."""
2437 raise Exception("\nERROR: Invalid Tree Item. ")
2439 return item
.IsHyperText()
2442 def SetItemText(self
, item
, text
):
2443 """Sets the item text."""
2446 raise Exception("\nERROR: Invalid Tree Item. ")
2448 dc
= wx
.ClientDC(self
)
2450 self
.CalculateSize(item
, dc
)
2451 self
.RefreshLine(item
)
2454 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2455 """Sets the item image, depending on the item state."""
2458 raise Exception("\nERROR: Invalid Tree Item. ")
2460 item
.SetImage(image
, which
)
2462 dc
= wx
.ClientDC(self
)
2463 self
.CalculateSize(item
, dc
)
2464 self
.RefreshLine(item
)
2467 def SetPyData(self
, item
, data
):
2468 """Sets the data associated to an item."""
2471 raise Exception("\nERROR: Invalid Tree Item. ")
2475 SetItemPyData
= SetPyData
2478 def SetItemHasChildren(self
, item
, has
=True):
2479 """Forces the appearance of the button next to the item."""
2482 raise Exception("\nERROR: Invalid Tree Item. ")
2484 item
.SetHasPlus(has
)
2485 self
.RefreshLine(item
)
2488 def SetItemBold(self
, item
, bold
=True):
2489 """Sets the item font bold/unbold."""
2492 raise Exception("\nERROR: Invalid Tree Item. ")
2494 # avoid redrawing the tree if no real change
2495 if item
.IsBold() != bold
:
2500 def SetItemItalic(self
, item
, italic
=True):
2501 """Sets the item font italic/non-italic."""
2504 raise Exception("\nERROR: Invalid Tree Item. ")
2506 if item
.IsItalic() != italic
:
2507 itemFont
= self
.GetItemFont(item
)
2508 if itemFont
!= wx
.NullFont
:
2513 item
.SetItalic(italic
)
2514 itemFont
.SetStyle(style
)
2515 self
.SetItemFont(item
, itemFont
)
2519 def SetItemDropHighlight(self
, item
, highlight
=True):
2521 Gives the item the visual feedback for drag and drop operations.
2522 This is useful when something is dragged from outside the CustomTreeCtrl.
2526 raise Exception("\nERROR: Invalid Tree Item. ")
2529 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2530 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2532 item
.Attr().SetTextColour(fg
)
2533 item
.Attr
.SetBackgroundColour(bg
)
2534 self
.RefreshLine(item
)
2537 def SetItemTextColour(self
, item
, col
):
2538 """Sets the item text colour."""
2541 raise Exception("\nERROR: Invalid Tree Item. ")
2543 if self
.GetItemTextColour(item
) == col
:
2546 item
.Attr().SetTextColour(col
)
2547 self
.RefreshLine(item
)
2550 def SetItemBackgroundColour(self
, item
, col
):
2551 """Sets the item background colour."""
2554 raise Exception("\nERROR: Invalid Tree Item. ")
2556 item
.Attr().SetBackgroundColour(col
)
2557 self
.RefreshLine(item
)
2560 def SetItemHyperText(self
, item
, hyper
=True):
2561 """Sets whether the item is hypertext or not."""
2564 raise Exception("\nERROR: Invalid Tree Item. ")
2566 item
.SetHyperText(hyper
)
2567 self
.RefreshLine(item
)
2570 def SetItemFont(self
, item
, font
):
2571 """Sets the item font."""
2574 raise Exception("\nERROR: Invalid Tree Item. ")
2576 if self
.GetItemFont(item
) == font
:
2579 item
.Attr().SetFont(font
)
2583 def SetFont(self
, font
):
2584 """Sets the CustomTreeCtrl font."""
2586 wx
.ScrolledWindow
.SetFont(self
, font
)
2588 self
._normalFont
= font
2589 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2590 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2591 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2596 def GetHyperTextFont(self
):
2597 """Returns the font used to render an hypertext item."""
2599 return self
._hypertextfont
2602 def SetHyperTextFont(self
, font
):
2603 """Sets the font used to render an hypertext item."""
2605 self
._hypertextfont
= font
2609 def SetHyperTextNewColour(self
, colour
):
2610 """Sets the colour used to render a non-visited hypertext item."""
2612 self
._hypertextnewcolour
= colour
2616 def GetHyperTextNewColour(self
):
2617 """Returns the colour used to render a non-visited hypertext item."""
2619 return self
._hypertextnewcolour
2622 def SetHyperTextVisitedColour(self
, colour
):
2623 """Sets the colour used to render a visited hypertext item."""
2625 self
._hypertextvisitedcolour
= colour
2629 def GetHyperTextVisitedColour(self
):
2630 """Returns the colour used to render a visited hypertext item."""
2632 return self
._hypertextvisitedcolour
2635 def SetItemVisited(self
, item
, visited
=True):
2636 """Sets whether an hypertext item was visited."""
2639 raise Exception("\nERROR: Invalid Tree Item. ")
2641 item
.SetVisited(visited
)
2642 self
.RefreshLine(item
)
2645 def GetItemVisited(self
, item
):
2646 """Returns whether an hypertext item was visited."""
2649 raise Exception("\nERROR: Invalid Tree Item. ")
2651 return item
.GetVisited()
2654 def SetHilightFocusColour(self
, colour
):
2656 Sets the colour used to highlight focused selected items.
2657 This is applied only if gradient and Windows Vista styles are disabled.
2660 self
._hilightBrush
= wx
.Brush(colour
)
2661 self
.RefreshSelected()
2664 def SetHilightNonFocusColour(self
, colour
):
2666 Sets the colour used to highlight unfocused selected items.
2667 This is applied only if gradient and Windows Vista styles are disabled.
2670 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2671 self
.RefreshSelected()
2674 def GetHilightFocusColour(self
):
2676 Returns the colour used to highlight focused selected items.
2677 This is applied only if gradient and Windows Vista styles are disabled.
2680 return self
._hilightBrush
.GetColour()
2683 def GetHilightNonFocusColour(self
):
2685 Returns the colour used to highlight unfocused selected items.
2686 This is applied only if gradient and Windows Vista styles are disabled.
2689 return self
._hilightUnfocusedBrush
.GetColour()
2692 def SetFirstGradientColour(self
, colour
=None):
2693 """Sets the first gradient colour."""
2696 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2698 self
._firstcolour
= colour
2699 if self
._usegradients
:
2700 self
.RefreshSelected()
2703 def SetSecondGradientColour(self
, colour
=None):
2704 """Sets the second gradient colour."""
2707 # No colour given, generate a slightly darker from the
2708 # CustomTreeCtrl background colour
2709 color
= self
.GetBackgroundColour()
2710 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2711 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2712 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2714 self
._secondcolour
= colour
2716 if self
._usegradients
:
2717 self
.RefreshSelected()
2720 def GetFirstGradientColour(self
):
2721 """Returns the first gradient colour."""
2723 return self
._firstcolour
2726 def GetSecondGradientColour(self
):
2727 """Returns the second gradient colour."""
2729 return self
._secondcolour
2732 def EnableSelectionGradient(self
, enable
=True):
2733 """Globally enables/disables drawing of gradient selection."""
2735 self
._usegradients
= enable
2736 self
._vistaselection
= False
2737 self
.RefreshSelected()
2740 def SetGradientStyle(self
, vertical
=0):
2742 Sets the gradient style:
2743 0: horizontal gradient
2744 1: vertical gradient
2747 # 0 = Horizontal, 1 = Vertical
2748 self
._gradientstyle
= vertical
2750 if self
._usegradients
:
2751 self
.RefreshSelected()
2754 def GetGradientStyle(self
):
2756 Returns the gradient style:
2757 0: horizontal gradient
2758 1: vertical gradient
2761 return self
._gradientstyle
2764 def EnableSelectionVista(self
, enable
=True):
2765 """Globally enables/disables drawing of Windows Vista selection."""
2767 self
._usegradients
= False
2768 self
._vistaselection
= enable
2769 self
.RefreshSelected()
2772 def SetBorderPen(self
, pen
):
2774 Sets the pen used to draw the selected item border.
2775 The border pen is not used if the Windows Vista style is applied.
2778 self
._borderPen
= pen
2779 self
.RefreshSelected()
2782 def GetBorderPen(self
):
2784 Returns the pen used to draw the selected item border.
2785 The border pen is not used if the Windows Vista style is applied.
2788 return self
._borderPen
2791 def SetConnectionPen(self
, pen
):
2792 """Sets the pen used to draw the connecting lines between items."""
2794 self
._dottedPen
= pen
2798 def GetConnectionPen(self
):
2799 """Returns the pen used to draw the connecting lines between items."""
2801 return self
._dottedPen
2804 def SetBackgroundImage(self
, image
):
2805 """Sets the CustomTreeCtrl background image (can be none)."""
2807 self
._backgroundImage
= image
2811 def GetBackgroundImage(self
):
2812 """Returns the CustomTreeCtrl background image (can be none)."""
2814 return self
._backgroundImage
2817 def GetItemWindow(self
, item
):
2818 """Returns the window associated to the item (if any)."""
2821 raise Exception("\nERROR: Invalid Item")
2823 return item
.GetWindow()
2826 def GetItemWindowEnabled(self
, item
):
2827 """Returns whether the window associated to the item is enabled."""
2830 raise Exception("\nERROR: Invalid Item")
2832 return item
.GetWindowEnabled()
2835 def SetItemWindowEnabled(self
, item
, enable
=True):
2836 """Enables/disables the window associated to the item."""
2839 raise Exception("\nERROR: Invalid Item")
2841 item
.SetWindowEnabled(enable
)
2844 def GetItemType(self
, item
):
2846 Returns the item type:
2853 raise Exception("\nERROR: Invalid Item")
2855 return item
.GetType()
2857 # -----------------------------------------------------------------------------
2858 # item status inquiries
2859 # -----------------------------------------------------------------------------
2861 def IsVisible(self
, item
):
2862 """Returns whether the item is visible or not."""
2865 raise Exception("\nERROR: Invalid Tree Item. ")
2867 # An item is only visible if it's not a descendant of a collapsed item
2868 parent
= item
.GetParent()
2872 if not parent
.IsExpanded():
2875 parent
= parent
.GetParent()
2877 startX
, startY
= self
.GetViewStart()
2878 clientSize
= self
.GetClientSize()
2880 rect
= self
.GetBoundingRect(item
)
2884 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2886 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2888 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2894 def ItemHasChildren(self
, item
):
2895 """Returns whether the item has children or not."""
2898 raise Exception("\nERROR: Invalid Tree Item. ")
2900 # consider that the item does have children if it has the "+" button: it
2901 # might not have them (if it had never been expanded yet) but then it
2902 # could have them as well and it's better to err on this side rather than
2903 # disabling some operations which are restricted to the items with
2904 # children for an item which does have them
2905 return item
.HasPlus()
2908 def IsExpanded(self
, item
):
2909 """Returns whether the item is expanded or not."""
2912 raise Exception("\nERROR: Invalid Tree Item. ")
2914 return item
.IsExpanded()
2917 def IsSelected(self
, item
):
2918 """Returns whether the item is selected or not."""
2921 raise Exception("\nERROR: Invalid Tree Item. ")
2923 return item
.IsSelected()
2926 def IsBold(self
, item
):
2927 """Returns whether the item font is bold or not."""
2930 raise Exception("\nERROR: Invalid Tree Item. ")
2932 return item
.IsBold()
2935 def IsItalic(self
, item
):
2936 """Returns whether the item font is italic or not."""
2939 raise Exception("\nERROR: Invalid Tree Item. ")
2941 return item
.IsItalic()
2944 # -----------------------------------------------------------------------------
2946 # -----------------------------------------------------------------------------
2948 def GetItemParent(self
, item
):
2949 """Gets the item parent."""
2952 raise Exception("\nERROR: Invalid Tree Item. ")
2954 return item
.GetParent()
2957 def GetFirstChild(self
, item
):
2958 """Gets the item first child."""
2961 raise Exception("\nERROR: Invalid Tree Item. ")
2964 return self
.GetNextChild(item
, cookie
)
2967 def GetNextChild(self
, item
, cookie
):
2969 Gets the item next child based on the 'cookie' parameter.
2970 This method has no sense if you do not call GetFirstChild() before.
2974 raise Exception("\nERROR: Invalid Tree Item. ")
2976 children
= item
.GetChildren()
2978 # it's ok to cast cookie to size_t, we never have indices big enough to
2981 if cookie
< len(children
):
2983 return children
[cookie
], cookie
+1
2987 # there are no more of them
2991 def GetLastChild(self
, item
):
2992 """Gets the item last child."""
2995 raise Exception("\nERROR: Invalid Tree Item. ")
2997 children
= item
.GetChildren()
2998 return (len(children
) == 0 and [None] or [children
[-1]])[0]
3001 def GetNextSibling(self
, item
):
3002 """Gets the next sibling of an item."""
3005 raise Exception("\nERROR: Invalid Tree Item. ")
3008 parent
= i
.GetParent()
3012 # root item doesn't have any siblings
3015 siblings
= parent
.GetChildren()
3016 index
= siblings
.index(i
)
3019 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
3022 def GetPrevSibling(self
, item
):
3023 """Gets the previous sibling of an item."""
3026 raise Exception("\nERROR: Invalid Tree Item. ")
3029 parent
= i
.GetParent()
3033 # root item doesn't have any siblings
3036 siblings
= parent
.GetChildren()
3037 index
= siblings
.index(i
)
3039 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3042 def GetNext(self
, item
):
3043 """Gets the next item. Only for internal use right now."""
3046 raise Exception("\nERROR: Invalid Tree Item. ")
3050 # First see if there are any children.
3051 children
= i
.GetChildren()
3052 if len(children
) > 0:
3055 # Try a sibling of this or ancestor instead
3058 while p
and not toFind
:
3059 toFind
= self
.GetNextSibling(p
)
3060 p
= self
.GetItemParent(p
)
3065 def GetFirstVisibleItem(self
):
3066 """Returns the first visible item."""
3068 id = self
.GetRootItem()
3073 if self
.IsVisible(id):
3075 id = self
.GetNext(id)
3080 def GetNextVisible(self
, item
):
3081 """Returns the next visible item."""
3084 raise Exception("\nERROR: Invalid Tree Item. ")
3089 id = self
.GetNext(id)
3090 if id and self
.IsVisible(id):
3096 def GetPrevVisible(self
, item
):
3099 raise Exception("\nERROR: Invalid Tree Item. ")
3101 raise Exception("\nERROR: Not Implemented")
3106 def ResetTextControl(self
):
3107 """Called by TreeTextCtrl when it marks itself for deletion."""
3109 self
._textCtrl
.Destroy()
3110 self
._textCtrl
= None
3113 def FindItem(self
, idParent
, prefixOrig
):
3114 """Finds the first item starting with the given prefix after the given item."""
3116 # match is case insensitive as this is more convenient to the user: having
3117 # to press Shift-letter to go to the item starting with a capital letter
3118 # would be too bothersome
3119 prefix
= prefixOrig
.lower()
3121 # determine the starting point: we shouldn't take the current item (this
3122 # allows to switch between two items starting with the same letter just by
3123 # pressing it) but we shouldn't jump to the next one if the user is
3124 # continuing to type as otherwise he might easily skip the item he wanted
3127 if len(prefix
) == 1:
3128 id = self
.GetNext(id)
3130 # look for the item starting with the given prefix after it
3131 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3133 id = self
.GetNext(id)
3135 # if we haven't found anything...
3138 # ... wrap to the beginning
3139 id = self
.GetRootItem()
3140 if self
.HasFlag(TR_HIDE_ROOT
):
3141 # can't select virtual root
3142 id = self
.GetNext(id)
3144 # and try all the items (stop when we get to the one we started from)
3145 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3146 id = self
.GetNext(id)
3151 # -----------------------------------------------------------------------------
3153 # -----------------------------------------------------------------------------
3155 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3156 """Actually inserts an item in the tree."""
3158 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3159 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3161 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3162 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3164 if ct_type
< 0 or ct_type
> 2:
3165 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3171 # should we give a warning here?
3172 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3174 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3176 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3179 self
._hasWindows
= True
3180 self
._itemWithWindow
.append(item
)
3182 parent
.Insert(item
, previous
)
3187 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3188 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3191 raise Exception("\nERROR: Tree Can Have Only One Root")
3193 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3194 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3196 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3197 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3199 if ct_type
< 0 or ct_type
> 2:
3200 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3202 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3204 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3207 self
._hasWindows
= True
3208 self
._itemWithWindow
.append(self
._anchor
)
3210 if self
.HasFlag(TR_HIDE_ROOT
):
3212 # if root is hidden, make sure we can navigate
3214 self
._anchor
.SetHasPlus()
3215 self
._anchor
.Expand()
3216 self
.CalculatePositions()
3218 if not self
.HasFlag(TR_MULTIPLE
):
3220 self
._current
= self
._key
_current
= self
._anchor
3221 self
._current
.SetHilight(True)
3226 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3227 """Appends an item as a first child of parent."""
3229 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3230 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3232 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3233 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3235 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3238 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3239 """Auxiliary function to cope with the C++ hideous multifunction."""
3241 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3242 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3244 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3245 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3250 # should we give a warning here?
3251 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3257 index
= parent
.GetChildren().index(idPrevious
)
3259 raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
3261 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3264 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3265 """Auxiliary function to cope with the C++ hideous multifunction."""
3267 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3268 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3270 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3271 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3276 # should we give a warning here?
3277 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3279 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3282 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3283 """Inserts an item after the given previous."""
3285 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3286 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3288 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3289 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3291 if type(input) == type(1):
3292 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3294 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3297 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3298 """Appends an item as a last child of its parent."""
3300 if wnd
is not None and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3301 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3303 if text
.find("\n") >= 0 and not (self
._windowStyle
& TR_HAS_VARIABLE_ROW_HEIGHT
):
3304 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3309 # should we give a warning here?
3310 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3312 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3315 def SendDeleteEvent(self
, item
):
3316 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3318 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3320 event
.SetEventObject(self
)
3321 self
.GetEventHandler().ProcessEvent(event
)
3324 def IsDescendantOf(self
, parent
, item
):
3325 """Checks if the given item is under another one."""
3331 # item is a descendant of parent
3334 item
= item
.GetParent()
3339 # Don't leave edit or selection on a child which is about to disappear
3340 def ChildrenClosing(self
, item
):
3341 """We are about to destroy the item children."""
3343 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3344 self
._textCtrl
.StopEditing()
3346 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3347 self
._key
_current
= None
3349 if self
.IsDescendantOf(item
, self
._select
_me
):
3350 self
._select
_me
= item
3352 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3353 self
._current
.SetHilight(False)
3354 self
._current
= None
3355 self
._select
_me
= item
3358 def DeleteChildren(self
, item
):
3359 """Delete item children."""
3362 raise Exception("\nERROR: Invalid Tree Item. ")
3364 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3366 self
.ChildrenClosing(item
)
3367 item
.DeleteChildren(self
)
3370 def Delete(self
, item
):
3371 """Delete an item."""
3374 raise Exception("\nERROR: Invalid Tree Item. ")
3376 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3378 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3379 # can't delete the item being edited, cancel editing it first
3380 self
._textCtrl
.StopEditing()
3382 parent
= item
.GetParent()
3384 # don't keep stale pointers around!
3385 if self
.IsDescendantOf(item
, self
._key
_current
):
3387 # Don't silently change the selection:
3388 # do it properly in idle time, so event
3389 # handlers get called.
3391 # self._key_current = parent
3392 self
._key
_current
= None
3394 # self._select_me records whether we need to select
3395 # a different item, in idle time.
3396 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3397 self
._select
_me
= parent
3399 if self
.IsDescendantOf(item
, self
._current
):
3401 # Don't silently change the selection:
3402 # do it properly in idle time, so event
3403 # handlers get called.
3405 # self._current = parent
3406 self
._current
= None
3407 self
._select
_me
= parent
3409 # remove the item from the tree
3412 parent
.GetChildren().remove(item
) # remove by value
3414 else: # deleting the root
3416 # nothing will be left in the tree
3419 # and delete all of its children and the item itself now
3420 item
.DeleteChildren(self
)
3421 self
.SendDeleteEvent(item
)
3423 if item
== self
._select
_me
:
3424 self
._select
_me
= None
3426 # Remove the item with window
3427 if item
in self
._itemWithWindow
:
3428 wnd
= item
.GetWindow()
3432 self
._itemWithWindow
.remove(item
)
3437 def DeleteAllItems(self
):
3438 """Delete all items in the CustomTreeCtrl."""
3441 self
.Delete(self
._anchor
)
3444 def Expand(self
, item
):
3446 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3447 EVT_TREE_ITEM_EXPANDED events.
3451 raise Exception("\nERROR: Invalid Tree Item. ")
3453 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3454 raise Exception("\nERROR: Can't Expand An Hidden Root. ")
3456 if not item
.HasPlus():
3459 if item
.IsExpanded():
3462 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3464 event
.SetEventObject(self
)
3466 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3467 # cancelled by program
3471 self
.CalculatePositions()
3473 self
.RefreshSubtree(item
)
3475 if self
._hasWindows
:
3476 # We hide the associated window here, we may show it after
3479 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3480 self
.GetEventHandler().ProcessEvent(event
)
3483 def ExpandAllChildren(self
, item
):
3484 """Expands all the items children of the input item."""
3487 raise Exception("\nERROR: Invalid Tree Item. ")
3489 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3491 if not self
.IsExpanded(item
):
3494 child
, cookie
= self
.GetFirstChild(item
)
3497 self
.ExpandAllChildren(child
)
3498 child
, cookie
= self
.GetNextChild(item
, cookie
)
3501 def ExpandAll(self
):
3502 """Expands all CustomTreeCtrl items."""
3505 self
.ExpandAllChildren(self
._anchor
)
3508 def Collapse(self
, item
):
3510 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3511 EVT_TREE_ITEM_COLLAPSED events.
3515 raise Exception("\nERROR: Invalid Tree Item. ")
3517 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3518 raise Exception("\nERROR: Can't Collapse An Hidden Root. ")
3520 if not item
.IsExpanded():
3523 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3525 event
.SetEventObject(self
)
3526 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3527 # cancelled by program
3530 self
.ChildrenClosing(item
)
3533 self
.CalculatePositions()
3534 self
.RefreshSubtree(item
)
3536 if self
._hasWindows
:
3539 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3540 self
.GetEventHandler().ProcessEvent(event
)
3543 def CollapseAndReset(self
, item
):
3544 """Collapse the given item and deletes its children."""
3547 self
.DeleteChildren(item
)
3550 def Toggle(self
, item
):
3551 """Toggles the item state (collapsed/expanded)."""
3553 if item
.IsExpanded():
3559 def HideWindows(self
):
3560 """Hides the windows associated to the items. Used internally."""
3562 for child
in self
._itemWithWindow
:
3563 if not self
.IsVisible(child
):
3564 wnd
= child
.GetWindow()
3569 """Unselects the current selection."""
3573 self
._current
.SetHilight(False)
3574 self
.RefreshLine(self
._current
)
3576 self
._current
= None
3577 self
._select
_me
= None
3580 def UnselectAllChildren(self
, item
):
3581 """Unselects all the children of the given item."""
3583 if item
.IsSelected():
3585 item
.SetHilight(False)
3586 self
.RefreshLine(item
)
3588 if item
.HasChildren():
3589 for child
in item
.GetChildren():
3590 self
.UnselectAllChildren(child
)
3593 def UnselectAll(self
):
3594 """Unselect all the items."""
3596 rootItem
= self
.GetRootItem()
3598 # the tree might not have the root item at all
3600 self
.UnselectAllChildren(rootItem
)
3604 # Recursive function !
3605 # To stop we must have crt_item<last_item
3607 # Tag all next children, when no more children,
3608 # Move to parent (not to tag)
3609 # Keep going... if we found last_item, we stop.
3611 def TagNextChildren(self
, crt_item
, last_item
, select
):
3612 """Used internally."""
3614 parent
= crt_item
.GetParent()
3616 if parent
== None: # This is root item
3617 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3619 children
= parent
.GetChildren()
3620 index
= children
.index(crt_item
)
3622 count
= len(children
)
3624 for n
in xrange(index
+1, count
):
3625 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3628 return self
.TagNextChildren(parent
, last_item
, select
)
3631 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3632 """Used internally."""
3634 crt_item
.SetHilight(select
)
3635 self
.RefreshLine(crt_item
)
3637 if crt_item
== last_item
:
3640 if crt_item
.HasChildren():
3641 for child
in crt_item
.GetChildren():
3642 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3648 def SelectItemRange(self
, item1
, item2
):
3649 """Selects all the items between item1 and item2."""
3651 self
._select
_me
= None
3653 # item2 is not necessary after item1
3654 # choice first' and 'last' between item1 and item2
3655 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3656 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3658 select
= self
._current
.IsSelected()
3660 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3663 self
.TagNextChildren(first
, last
, select
)
3666 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3667 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3670 raise Exception("\nERROR: Invalid Tree Item. ")
3672 self
._select
_me
= None
3674 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3676 # to keep going anyhow !!!
3678 if item
.IsSelected():
3679 return # nothing to do
3680 unselect_others
= True
3681 extended_select
= False
3683 elif unselect_others
and item
.IsSelected():
3685 # selection change if there is more than one item currently selected
3686 if len(self
.GetSelections()) == 1:
3689 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3691 event
._itemOld
= self
._current
3692 event
.SetEventObject(self
)
3693 # TODO : Here we don't send any selection mode yet !
3695 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3698 parent
= self
.GetItemParent(item
)
3700 if not self
.IsExpanded(parent
):
3703 parent
= self
.GetItemParent(parent
)
3708 self
.Unselect() # to speed up thing
3714 if not self
._current
:
3715 self
._current
= self
._key
_current
= self
.GetRootItem()
3717 # don't change the mark (self._current)
3718 self
.SelectItemRange(self
._current
, item
)
3722 select
= True # the default
3724 # Check if we need to toggle hilight (ctrl mode)
3725 if not unselect_others
:
3726 select
= not item
.IsSelected()
3728 self
._current
= self
._key
_current
= item
3729 self
._current
.SetHilight(select
)
3730 self
.RefreshLine(self
._current
)
3732 # This can cause idle processing to select the root
3733 # if no item is selected, so it must be after the
3735 self
.EnsureVisible(item
)
3737 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3738 self
.GetEventHandler().ProcessEvent(event
)
3740 # Handles hypertext items
3741 if self
.IsItemHyperText(item
):
3742 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3744 self
.GetEventHandler().ProcessEvent(event
)
3747 def SelectItem(self
, item
, select
=True):
3748 """Selects/deselects an item."""
3751 raise Exception("\nERROR: Invalid Tree Item. ")
3755 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3759 item
.SetHilight(False)
3760 self
.RefreshLine(item
)
3763 def FillArray(self
, item
, array
=[]):
3765 Internal function. Used to populate an array of selected items when
3766 the style TR_MULTIPLE is used.
3772 if item
.IsSelected():
3775 if item
.HasChildren() and item
.IsExpanded():
3776 for child
in item
.GetChildren():
3777 array
= self
.FillArray(child
, array
)
3782 def GetSelections(self
):
3784 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3785 the TR_MULTIPLE style set.
3789 idRoot
= self
.GetRootItem()
3791 array
= self
.FillArray(idRoot
, array
)
3793 #else: the tree is empty, so no selections
3798 def EnsureVisible(self
, item
):
3799 """Ensure that an item is visible in CustomTreeCtrl."""
3802 raise Exception("\nERROR: Invalid Tree Item. ")
3804 # first expand all parent branches
3805 parent
= item
.GetParent()
3807 if self
.HasFlag(TR_HIDE_ROOT
):
3808 while parent
and parent
!= self
._anchor
:
3810 parent
= parent
.GetParent()
3814 parent
= parent
.GetParent()
3819 def ScrollTo(self
, item
):
3820 """Scrolls the specified item into view."""
3825 # We have to call this here because the label in
3826 # question might just have been added and no screen
3827 # update taken place.
3829 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3834 # now scroll to the item
3835 item_y
= item
.GetY()
3836 start_x
, start_y
= self
.GetViewStart()
3837 start_y
*= _PIXELS_PER_UNIT
3839 client_w
, client_h
= self
.GetClientSize()
3843 if item_y
< start_y
+3:
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 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3850 # Item should appear at top
3851 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3853 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3856 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3857 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3858 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3859 item_y
+= _PIXELS_PER_UNIT
+2
3860 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3861 # Item should appear at bottom
3862 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
)
3865 def OnCompareItems(self
, item1
, item2
):
3867 Returns whether 2 items have the same text.
3868 Override this function in the derived class to change the sort order of the items
3869 in the CustomTreeCtrl. The function should return a negative, zero or positive
3870 value if the first item is less than, equal to or greater than the second one.
3872 The base class version compares items alphabetically.
3875 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3878 def SortChildren(self
, item
):
3880 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3881 You should override that method to change the sort order (the default is ascending
3882 case-sensitive alphabetical order).
3886 raise Exception("\nERROR: Invalid Tree Item. ")
3888 children
= item
.GetChildren()
3890 if len(children
) > 1:
3892 children
.sort(self
.OnCompareItems
)
3895 def GetImageList(self
):
3896 """Returns the normal image list."""
3898 return self
._imageListNormal
3901 def GetButtonsImageList(self
):
3902 """Returns the buttons image list (from which application-defined button images are taken)."""
3904 return self
._imageListButtons
3907 def GetStateImageList(self
):
3908 """Returns the state image list (from which application-defined state images are taken)."""
3910 return self
._imageListState
3913 def GetImageListCheck(self
):
3914 """Returns the image list used to build the check/radio buttons."""
3916 return self
._imageListCheck
3919 def CalculateLineHeight(self
):
3920 """Calculates the height of a line."""
3922 dc
= wx
.ClientDC(self
)
3923 self
._lineHeight
= dc
.GetCharHeight()
3925 if self
._imageListNormal
:
3927 # Calculate a self._lineHeight value from the normal Image sizes.
3928 # May be toggle off. Then CustomTreeCtrl will spread when
3929 # necessary (which might look ugly).
3930 n
= self
._imageListNormal
.GetImageCount()
3934 width
, height
= self
._imageListNormal
.GetSize(i
)
3936 if height
> self
._lineHeight
:
3937 self
._lineHeight
= height
3939 if self
._imageListButtons
:
3941 # Calculate a self._lineHeight value from the Button image sizes.
3942 # May be toggle off. Then CustomTreeCtrl will spread when
3943 # necessary (which might look ugly).
3944 n
= self
._imageListButtons
.GetImageCount()
3948 width
, height
= self
._imageListButtons
.GetSize(i
)
3950 if height
> self
._lineHeight
:
3951 self
._lineHeight
= height
3953 if self
._imageListCheck
:
3955 # Calculate a self._lineHeight value from the check/radio image sizes.
3956 # May be toggle off. Then CustomTreeCtrl will spread when
3957 # necessary (which might look ugly).
3958 n
= self
._imageListCheck
.GetImageCount()
3962 width
, height
= self
._imageListCheck
.GetSize(i
)
3964 if height
> self
._lineHeight
:
3965 self
._lineHeight
= height
3967 if self
._lineHeight
< 30:
3968 self
._lineHeight
+= 2 # at least 2 pixels
3970 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3973 def SetImageList(self
, imageList
):
3974 """Sets the normal image list."""
3976 if self
._ownsImageListNormal
:
3977 del self
._imageListNormal
3979 self
._imageListNormal
= imageList
3980 self
._ownsImageListNormal
= False
3983 # Don't do any drawing if we're setting the list to NULL,
3984 # since we may be in the process of deleting the tree control.
3986 self
.CalculateLineHeight()
3988 # We gray out the image list to use the grayed icons with disabled items
3989 sz
= imageList
.GetSize(0)
3990 self
._grayedImageList
= wx
.ImageList(sz
[0], sz
[1], True, 0)
3992 for ii
in xrange(imageList
.GetImageCount()):
3993 bmp
= imageList
.GetBitmap(ii
)
3994 image
= wx
.ImageFromBitmap(bmp
)
3995 image
= GrayOut(image
)
3996 newbmp
= wx
.BitmapFromImage(image
)
3997 self
._grayedImageList
.Add(newbmp
)
4000 def SetStateImageList(self
, imageList
):
4001 """Sets the state image list (from which application-defined state images are taken)."""
4003 if self
._ownsImageListState
:
4004 del self
._imageListState
4006 self
._imageListState
= imageList
4007 self
._ownsImageListState
= False
4010 def SetButtonsImageList(self
, imageList
):
4011 """Sets the buttons image list (from which application-defined button images are taken)."""
4013 if self
._ownsImageListButtons
:
4014 del self
._imageListButtons
4016 self
._imageListButtons
= imageList
4017 self
._ownsImageListButtons
= False
4019 self
.CalculateLineHeight()
4022 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
4023 """Sets the check image list."""
4027 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
4028 self
._imageListCheck
.Add(GetCheckedBitmap())
4029 self
._imageListCheck
.Add(GetNotCheckedBitmap())
4030 self
._imageListCheck
.Add(GetFlaggedBitmap())
4031 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
4035 sizex
, sizey
= imglist
.GetSize(0)
4036 self
._imageListCheck
= imglist
4038 # We gray out the image list to use the grayed icons with disabled items
4039 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
4041 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
4043 bmp
= self
._imageListCheck
.GetBitmap(ii
)
4044 image
= wx
.ImageFromBitmap(bmp
)
4045 image
= GrayOut(image
)
4046 newbmp
= wx
.BitmapFromImage(image
)
4047 self
._grayedCheckList
.Add(newbmp
)
4052 self
.CalculateLineHeight()
4055 def AssignImageList(self
, imageList
):
4056 """Assigns the normal image list."""
4058 self
.SetImageList(imageList
)
4059 self
._ownsImageListNormal
= True
4062 def AssignStateImageList(self
, imageList
):
4063 """Assigns the state image list."""
4065 self
.SetStateImageList(imageList
)
4066 self
._ownsImageListState
= True
4069 def AssignButtonsImageList(self
, imageList
):
4070 """Assigns the button image list."""
4072 self
.SetButtonsImageList(imageList
)
4073 self
._ownsImageListButtons
= True
4076 # -----------------------------------------------------------------------------
4078 # -----------------------------------------------------------------------------
4080 def AdjustMyScrollbars(self
):
4081 """Adjust the wx.ScrolledWindow scrollbars."""
4085 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4086 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4087 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4088 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4089 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4090 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4094 self
.SetScrollbars(0, 0, 0, 0)
4097 def GetLineHeight(self
, item
):
4098 """Returns the line height for the given item."""
4100 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4101 return item
.GetHeight()
4103 return self
._lineHeight
4106 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4107 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4109 oldpen
= dc
.GetPen()
4110 oldbrush
= dc
.GetBrush()
4111 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4113 # calculate gradient coefficients
4115 col2
= self
._secondcolour
4116 col1
= self
._firstcolour
4118 col2
= self
._hilightUnfocusedBrush
.GetColour()
4119 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4121 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4122 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4124 flrect
= float(rect
.height
)
4126 rstep
= float((r2
- r1
)) / flrect
4127 gstep
= float((g2
- g1
)) / flrect
4128 bstep
= float((b2
- b1
)) / flrect
4130 rf
, gf
, bf
= 0, 0, 0
4132 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4133 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4134 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4135 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4141 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4142 dc
.DrawRectangleRect(rect
)
4143 dc
.SetBrush(oldbrush
)
4146 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4147 """Gradient fill from colour 1 to colour 2 from left to right."""
4149 oldpen
= dc
.GetPen()
4150 oldbrush
= dc
.GetBrush()
4151 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4153 # calculate gradient coefficients
4156 col2
= self
._secondcolour
4157 col1
= self
._firstcolour
4159 col2
= self
._hilightUnfocusedBrush
.GetColour()
4160 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4162 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4163 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4165 flrect
= float(rect
.width
)
4167 rstep
= float((r2
- r1
)) / flrect
4168 gstep
= float((g2
- g1
)) / flrect
4169 bstep
= float((b2
- b1
)) / flrect
4171 rf
, gf
, bf
= 0, 0, 0
4173 for x
in xrange(rect
.x
, rect
.x
+ rect
.width
):
4174 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4175 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4176 dc
.DrawRectangle(x
, rect
.y
, 1, rect
.height
)
4182 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4183 dc
.DrawRectangleRect(rect
)
4184 dc
.SetBrush(oldbrush
)
4187 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4188 """Draw the selected item(s) with the Windows Vista style."""
4192 outer
= _rgbSelectOuter
4193 inner
= _rgbSelectInner
4195 bottom
= _rgbSelectBottom
4199 outer
= _rgbNoFocusOuter
4200 inner
= _rgbNoFocusInner
4201 top
= _rgbNoFocusTop
4202 bottom
= _rgbNoFocusBottom
4204 oldpen
= dc
.GetPen()
4205 oldbrush
= dc
.GetBrush()
4207 bdrRect
= wx
.Rect(*rect
.Get())
4208 filRect
= wx
.Rect(*rect
.Get())
4209 filRect
.Deflate(1,1)
4211 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4212 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4214 flrect
= float(filRect
.height
)
4216 flrect
= self
._lineHeight
4218 rstep
= float((r2
- r1
)) / flrect
4219 gstep
= float((g2
- g1
)) / flrect
4220 bstep
= float((b2
- b1
)) / flrect
4222 rf
, gf
, bf
= 0, 0, 0
4223 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4225 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4226 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4227 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4228 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4233 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4234 dc
.SetPen(wx
.Pen(outer
))
4235 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4236 bdrRect
.Deflate(1, 1)
4237 dc
.SetPen(wx
.Pen(inner
))
4238 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4241 dc
.SetBrush(oldbrush
)
4244 def PaintItem(self
, item
, dc
):
4245 """Actually paint an item."""
4247 attr
= item
.GetAttributes()
4249 if attr
and attr
.HasFont():
4250 dc
.SetFont(attr
.GetFont())
4252 dc
.SetFont(self
._boldFont
)
4253 if item
.IsHyperText():
4254 dc
.SetFont(self
.GetHyperTextFont())
4255 if item
.GetVisited():
4256 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4258 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4260 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4262 image
= item
.GetCurrentImage()
4263 checkimage
= item
.GetCurrentCheckedImage()
4264 image_w
, image_h
= 0, 0
4266 if image
!= _NO_IMAGE
:
4268 if self
._imageListNormal
:
4270 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4277 if item
.GetType() != 0:
4278 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4281 wcheck
, hcheck
= 0, 0
4283 total_h
= self
.GetLineHeight(item
)
4284 drawItemBackground
= False
4286 if item
.IsSelected():
4288 # under mac selections are only a rectangle in case they don't have the focus
4289 if wx
.Platform
== "__WXMAC__":
4290 if not self
._hasFocus
:
4291 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4292 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4294 dc
.SetBrush(self
._hilightBrush
)
4296 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4297 drawItemBackground
= True
4299 if attr
and attr
.HasBackgroundColour():
4300 drawItemBackground
= True
4301 colBg
= attr
.GetBackgroundColour()
4303 colBg
= self
._backgroundColour
4305 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4306 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4308 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4310 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4312 w
, h
= self
.GetClientSize()
4314 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4316 if item
.IsSelected():
4317 if self
._usegradients
:
4318 if self
._gradientstyle
== 0: # Horizontal
4319 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4321 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4322 elif self
._vistaselection
:
4323 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4325 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4326 flags
= wx
.CONTROL_SELECTED
4327 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4328 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4330 dc
.DrawRectangleRect(itemrect
)
4334 if item
.IsSelected():
4336 # If it's selected, and there's an image, then we should
4337 # take care to leave the area under the image painted in the
4338 # background colour.
4340 wnd
= item
.GetWindow()
4343 wndx
, wndy
= item
.GetWindowSize()
4345 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4347 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4350 if self
._usegradients
:
4351 if self
._gradientstyle
== 0: # Horizontal
4352 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4354 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4355 elif self
._vistaselection
:
4356 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4358 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4359 flags
= wx
.CONTROL_SELECTED
4360 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4361 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4363 dc
.DrawRectangleRect(itemrect
)
4365 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4366 # don't allow backgrounds to be customized. Not drawing the background,
4367 # except for custom item backgrounds, works for both kinds of theme.
4368 elif drawItemBackground
:
4370 minusicon
= wcheck
+ image_w
- 2
4371 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4373 item
.GetWidth()-minusicon
,
4376 if self
._usegradients
and self
._hasFocus
:
4377 if self
._gradientstyle
== 0: # Horizontal
4378 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4380 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4382 dc
.DrawRectangleRect(itemrect
)
4384 if image
!= _NO_IMAGE
:
4386 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4387 if item
.IsEnabled():
4388 imglist
= self
._imageListNormal
4390 imglist
= self
._grayedImageList
4392 imglist
.Draw(image
, dc
,
4393 item
.GetX() + wcheck
,
4394 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4395 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4397 dc
.DestroyClippingRegion()
4400 if item
.IsEnabled():
4401 imglist
= self
._imageListCheck
4403 imglist
= self
._grayedCheckList
4405 imglist
.Draw(checkimage
, dc
,
4407 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4408 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4410 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4411 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4413 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4415 if not item
.IsEnabled():
4416 foreground
= dc
.GetTextForeground()
4417 dc
.SetTextForeground(self
._disabledColour
)
4418 dc
.DrawLabel(item
.GetText(), textrect
)
4419 dc
.SetTextForeground(foreground
)
4421 if wx
.Platform
== "__WXMAC__" and item
.IsSelected() and self
._hasFocus
:
4422 dc
.SetTextForeground(wx
.WHITE
)
4423 dc
.DrawLabel(item
.GetText(), textrect
)
4425 wnd
= item
.GetWindow()
4427 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4428 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4430 if item
.GetHeight() > item
.GetWindowSize()[1]:
4431 ya
+= (item
.GetHeight() - item
.GetWindowSize()[1])/2
4433 if not wnd
.IsShown():
4435 if wnd
.GetPosition() != (wndx
, ya
):
4436 wnd
.SetPosition((wndx
, ya
))
4438 # restore normal font
4439 dc
.SetFont(self
._normalFont
)
4442 # Now y stands for the top of the item, whereas it used to stand for middle !
4443 def PaintLevel(self
, item
, dc
, level
, y
):
4444 """Paint a level of CustomTreeCtrl."""
4446 x
= level
*self
._indent
4448 if not self
.HasFlag(TR_HIDE_ROOT
):
4454 # always expand hidden root
4456 children
= item
.GetChildren()
4457 count
= len(children
)
4463 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4466 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4468 # draw line down to last child
4469 origY
+= self
.GetLineHeight(children
[0])>>1
4470 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4471 oldPen
= dc
.GetPen()
4472 dc
.SetPen(self
._dottedPen
)
4473 dc
.DrawLine(3, origY
, 3, oldY
)
4478 item
.SetX(x
+self
._spacing
)
4481 h
= self
.GetLineHeight(item
)
4483 y_mid
= y_top
+ (h
>>1)
4486 exposed_x
= dc
.LogicalToDeviceX(0)
4487 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4489 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4490 if wx
.Platform
== "__WXMAC__":
4491 # don't draw rect outline if we already have the
4492 # background color under Mac
4493 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4495 pen
= self
._borderPen
4497 if item
.IsSelected():
4498 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4499 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4501 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4503 attr
= item
.GetAttributes()
4504 if attr
and attr
.HasTextColour():
4505 colText
= attr
.GetTextColour()
4507 colText
= self
.GetForegroundColour()
4509 if self
._vistaselection
:
4513 dc
.SetTextForeground(colText
)
4518 self
.PaintItem(item
, dc
)
4520 if self
.HasFlag(TR_ROW_LINES
):
4522 # if the background colour is white, choose a
4523 # contrasting color for the lines
4524 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4525 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4526 dc
.DrawLine(0, y_top
, 10000, y_top
)
4527 dc
.DrawLine(0, y
, 10000, y
)
4529 # restore DC objects
4530 dc
.SetBrush(wx
.WHITE_BRUSH
)
4531 dc
.SetTextForeground(wx
.BLACK
)
4533 if not self
.HasFlag(TR_NO_LINES
):
4535 # draw the horizontal line here
4536 dc
.SetPen(self
._dottedPen
)
4538 if x
> self
._indent
:
4539 x_start
-= self
._indent
4540 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4542 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4545 # should the item show a button?
4546 if item
.HasPlus() and self
.HasButtons():
4548 if self
._imageListButtons
:
4550 # draw the image button here
4553 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4554 if item
.IsSelected():
4555 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4557 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4559 yy
= y_mid
- image_h
/2
4561 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4562 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4563 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4564 dc
.DestroyClippingRegion()
4566 else: # no custom buttons
4568 if self
._windowStyle
& TR_TWIST_BUTTONS
:
4569 # We draw something like the Mac twist buttons
4571 dc
.SetPen(wx
.BLACK_PEN
)
4572 dc
.SetBrush(self
._hilightBrush
)
4573 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4575 if item
.IsExpanded():
4577 button
[0].y
= y_mid
- 3
4579 button
[1].y
= button
[0].y
4581 button
[2].y
= button
[0].y
+ 6
4584 button
[0].y
= y_mid
- 5
4585 button
[1].x
= button
[0].x
4586 button
[1].y
= y_mid
+ 5
4587 button
[2].x
= button
[0].x
+ 5
4590 dc
.DrawPolygon(button
)
4593 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4600 if item
.IsExpanded():
4601 flag |
= _CONTROL_EXPANDED
4602 if item
== self
._underMouse
:
4603 flag |
= _CONTROL_CURRENT
4605 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4607 if item
.IsExpanded():
4609 children
= item
.GetChildren()
4610 count
= len(children
)
4619 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4622 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4624 # draw line down to last child
4625 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4626 if self
.HasButtons():
4629 # Only draw the portion of the line that is visible, in case it is huge
4630 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4631 yOrigin
= abs(yOrigin
)
4632 width
, height
= self
.GetClientSize()
4634 # Move end points to the begining/end of the view?
4637 if oldY
> yOrigin
+ height
:
4638 oldY
= yOrigin
+ height
4640 # after the adjustments if y_mid is larger than oldY then the line
4641 # isn't visible at all so don't draw anything
4643 dc
.SetPen(self
._dottedPen
)
4644 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4649 # -----------------------------------------------------------------------------
4650 # wxWidgets callbacks
4651 # -----------------------------------------------------------------------------
4653 def OnPaint(self
, event
):
4654 """Handles the wx.EVT_PAINT event."""
4656 dc
= wx
.PaintDC(self
)
4659 if not self
._anchor
:
4662 dc
.SetFont(self
._normalFont
)
4663 dc
.SetPen(self
._dottedPen
)
4666 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4669 def OnEraseBackground(self
, event
):
4670 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4672 # Can we actually do something here (or in OnPaint()) To Handle
4673 # background images that are stretchable or always centered?
4674 # I tried but I get enormous flickering...
4676 if not self
._backgroundImage
:
4680 if self
._imageStretchStyle
== _StyleTile
:
4684 dc
= wx
.ClientDC(self
)
4685 rect
= self
.GetUpdateRegion().GetBox()
4686 dc
.SetClippingRect(rect
)
4688 self
.TileBackground(dc
)
4691 def TileBackground(self
, dc
):
4692 """Tiles the background image to fill all the available area."""
4694 sz
= self
.GetClientSize()
4695 w
= self
._backgroundImage
.GetWidth()
4696 h
= self
._backgroundImage
.GetHeight()
4703 while y
< sz
.height
:
4704 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4710 def OnSetFocus(self
, event
):
4711 """Handles the wx.EVT_SET_FOCUS event."""
4713 self
._hasFocus
= True
4714 self
.RefreshSelected()
4718 def OnKillFocus(self
, event
):
4719 """Handles the wx.EVT_KILL_FOCUS event."""
4721 self
._hasFocus
= False
4722 self
.RefreshSelected()
4726 def OnKeyDown(self
, event
):
4727 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4729 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4731 te
.SetEventObject(self
)
4733 if self
.GetEventHandler().ProcessEvent(te
):
4734 # intercepted by the user code
4737 if self
._current
is None or self
._key
_current
is None:
4742 # how should the selection work for this event?
4743 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4747 # * : Expand all/Collapse all
4748 # ' ' | return : activate
4749 # up : go up (not last children!)
4751 # left : go to parent
4752 # right : open if parent and go next
4754 # end : go to last item without opening parents
4755 # alnum : start or continue searching for the item with this prefix
4757 keyCode
= event
.GetKeyCode()
4759 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4760 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsItemEnabled(self
._current
):
4761 self
.Expand(self
._current
)
4763 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4764 if not self
.IsExpanded(self
._current
) and self
.IsItemEnabled(self
._current
):
4766 self
.ExpandAll(self
._current
)
4768 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4769 if self
.IsExpanded(self
._current
):
4770 self
.Collapse(self
._current
)
4772 elif keyCode
== wx
.WXK_MENU
:
4773 # Use the item's bounding rectangle to determine position for the event
4774 itemRect
= self
.GetBoundingRect(self
._current
, True)
4775 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4776 event
._item
= self
._current
4777 # Use the left edge, vertical middle
4778 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4779 event
.SetEventObject(self
)
4780 self
.GetEventHandler().ProcessEvent(event
)
4782 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4784 if not self
.IsItemEnabled(self
._current
):
4788 if not event
.HasModifiers():
4789 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4790 event
._item
= self
._current
4791 event
.SetEventObject(self
)
4792 self
.GetEventHandler().ProcessEvent(event
)
4794 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4795 checked
= not self
.IsItemChecked(self
._current
)
4796 self
.CheckItem(self
._current
, checked
)
4798 # in any case, also generate the normal key event for this key,
4799 # even if we generated the ACTIVATED event above: this is what
4800 # wxMSW does and it makes sense because you might not want to
4801 # process ACTIVATED event at all and handle Space and Return
4802 # directly (and differently) which would be impossible otherwise
4805 # up goes to the previous sibling or to the last
4806 # of its children if it's expanded
4807 elif keyCode
== wx
.WXK_UP
:
4808 prev
= self
.GetPrevSibling(self
._key
_current
)
4810 prev
= self
.GetItemParent(self
._key
_current
)
4811 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4815 current
= self
._key
_current
4816 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4817 if current
== self
.GetFirstChild(prev
)[0] and self
.IsItemEnabled(prev
):
4818 # otherwise we return to where we came from
4819 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4820 self
._key
_current
= prev
4823 current
= self
._key
_current
4825 # We are going to another parent node
4826 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4827 child
= self
.GetLastChild(prev
)
4832 # Try to get the previous siblings and see if they are active
4833 while prev
and not self
.IsItemEnabled(prev
):
4834 prev
= self
.GetPrevSibling(prev
)
4837 # No previous siblings active: go to the parent and up
4838 prev
= self
.GetItemParent(current
)
4839 while prev
and not self
.IsItemEnabled(prev
):
4840 prev
= self
.GetItemParent(prev
)
4843 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4844 self
._key
_current
= prev
4846 # left arrow goes to the parent
4847 elif keyCode
== wx
.WXK_LEFT
:
4849 prev
= self
.GetItemParent(self
._current
)
4850 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4851 # don't go to root if it is hidden
4852 prev
= self
.GetPrevSibling(self
._current
)
4854 if self
.IsExpanded(self
._current
):
4855 self
.Collapse(self
._current
)
4857 if prev
and self
.IsItemEnabled(prev
):
4858 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4860 elif keyCode
== wx
.WXK_RIGHT
:
4861 # this works the same as the down arrow except that we
4862 # also expand the item if it wasn't expanded yet
4863 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4864 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4865 if self
.IsItemEnabled(child
):
4866 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4867 self
._key
_current
= child
4869 self
.Expand(self
._current
)
4872 elif keyCode
== wx
.WXK_DOWN
:
4873 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4875 child
= self
.GetNextActiveItem(self
._key
_current
)
4878 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4879 self
._key
_current
= child
4883 next
= self
.GetNextSibling(self
._key
_current
)
4886 current
= self
._key
_current
4887 while current
and not next
:
4888 current
= self
.GetItemParent(current
)
4890 next
= self
.GetNextSibling(current
)
4891 if not next
or not self
.IsItemEnabled(next
):
4895 while next
and not self
.IsItemEnabled(next
):
4896 next
= self
.GetNext(next
)
4899 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4900 self
._key
_current
= next
4903 # <End> selects the last visible tree item
4904 elif keyCode
== wx
.WXK_END
:
4906 last
= self
.GetRootItem()
4908 while last
and self
.IsExpanded(last
):
4910 lastChild
= self
.GetLastChild(last
)
4912 # it may happen if the item was expanded but then all of
4913 # its children have been deleted - so IsExpanded() returned
4914 # true, but GetLastChild() returned invalid item
4920 if last
and self
.IsItemEnabled(last
):
4922 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4924 # <Home> selects the root item
4925 elif keyCode
== wx
.WXK_HOME
:
4927 prev
= self
.GetRootItem()
4932 if self
.HasFlag(TR_HIDE_ROOT
):
4933 prev
, cookie
= self
.GetFirstChild(prev
)
4937 if self
.IsItemEnabled(prev
):
4938 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4942 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4943 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4944 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4946 # find the next item starting with the given prefix
4948 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4954 if self
.IsItemEnabled(id):
4956 self
._findPrefix
+= ch
4958 # also start the timer to reset the current prefix if the user
4959 # doesn't press any more alnum keys soon -- we wouldn't want
4960 # to use this prefix for a new item search
4961 if not self
._findTimer
:
4962 self
._findTimer
= TreeFindTimer(self
)
4964 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4971 def GetNextActiveItem(self
, item
, down
=True):
4972 """Returns the next active item. Used Internally at present. """
4975 sibling
= self
.GetNextSibling
4977 sibling
= self
.GetPrevSibling
4979 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4980 # Is an unchecked radiobutton... all its children are inactive
4981 # try to get the next/previous sibling
4985 child
= sibling(item
)
4986 if (child
and self
.IsItemEnabled(child
)) or not child
:
4991 # Tha's not a radiobutton... but some of its children can be
4993 child
, cookie
= self
.GetFirstChild(item
)
4994 while child
and not self
.IsItemEnabled(child
):
4995 child
, cookie
= self
.GetNextChild(item
, cookie
)
4997 if child
and self
.IsItemEnabled(child
):
5003 def HitTest(self
, point
, flags
=0):
5005 Calculates which (if any) item is under the given point, returning the tree item
5006 at this point plus extra information flags. Flags is a bitlist of the following:
5008 TREE_HITTEST_ABOVE above the client area
5009 TREE_HITTEST_BELOW below the client area
5010 TREE_HITTEST_NOWHERE no item has been hit
5011 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
5012 TREE_HITTEST_ONITEMICON on the icon associated to an item
5013 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
5014 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
5015 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
5016 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
5017 TREE_HITTEST_TOLEFT on the left of the client area
5018 TREE_HITTEST_TORIGHT on the right of the client area
5019 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
5020 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
5021 TREE_HITTEST_ONITEM anywhere on the item
5023 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
5026 w
, h
= self
.GetSize()
5030 flags |
= TREE_HITTEST_TOLEFT
5032 flags |
= TREE_HITTEST_TORIGHT
5034 flags |
= TREE_HITTEST_ABOVE
5036 flags |
= TREE_HITTEST_BELOW
5041 if self
._anchor
== None:
5042 flags
= TREE_HITTEST_NOWHERE
5045 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5048 flags
= TREE_HITTEST_NOWHERE
5051 if not self
.IsItemEnabled(hit
):
5057 def GetBoundingRect(self
, item
, textOnly
=False):
5058 """Gets the bounding rectangle of the item."""
5061 raise Exception("\nERROR: Invalid Tree Item. ")
5065 startX
, startY
= self
.GetViewStart()
5068 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5069 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5070 rect
.width
= i
.GetWidth()
5071 rect
.height
= self
.GetLineHeight(i
)
5076 def Edit(self
, item
):
5078 Internal function. Starts the editing of an item label, sending a
5079 EVT_TREE_BEGIN_LABEL_EDIT event.
5082 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5084 te
.SetEventObject(self
)
5085 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5089 # We have to call this here because the label in
5090 # question might just have been added and no screen
5091 # update taken place.
5093 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5098 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5099 self
._textCtrl
.StopEditing()
5101 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5102 self
._textCtrl
.SetFocus()
5105 def GetEditControl(self
):
5107 Returns a pointer to the edit TextCtrl if the item is being edited or
5108 None otherwise (it is assumed that no more than one item may be edited
5112 return self
._textCtrl
5115 def OnRenameAccept(self
, item
, value
):
5117 Called by TreeTextCtrl, to accept the changes and to send the
5118 EVT_TREE_END_LABEL_EDIT event.
5121 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5123 le
.SetEventObject(self
)
5125 le
._editCancelled
= False
5127 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5130 def OnRenameCancelled(self
, item
):
5132 Called by TreeTextCtrl, to cancel the changes and to send the
5133 EVT_TREE_END_LABEL_EDIT event.
5136 # let owner know that the edit was cancelled
5137 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5139 le
.SetEventObject(self
)
5141 le
._editCancelled
= True
5143 self
.GetEventHandler().ProcessEvent(le
)
5146 def OnRenameTimer(self
):
5147 """The timer for renaming has expired. Start editing."""
5149 self
.Edit(self
._current
)
5152 def OnMouse(self
, event
):
5153 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5155 if not self
._anchor
:
5158 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5160 # Is the mouse over a tree item button?
5162 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5163 underMouse
= thisItem
5164 underMouseChanged
= underMouse
!= self
._underMouse
5166 if underMouse
and (flags
& TREE_HITTEST_ONITEM
) and not event
.LeftIsDown() and \
5167 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5168 underMouse
= underMouse
5172 if underMouse
!= self
._underMouse
:
5173 if self
._underMouse
:
5174 # unhighlight old item
5175 self
._underMouse
= None
5177 self
._underMouse
= underMouse
5179 # Determines what item we are hovering over and need a tooltip for
5180 hoverItem
= thisItem
5182 # We do not want a tooltip if we are dragging, or if the rename timer is running
5183 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5185 if hoverItem
is not None:
5186 # Ask the tree control what tooltip (if any) should be shown
5187 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5188 hevent
._item
= hoverItem
5189 hevent
.SetEventObject(self
)
5191 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5192 self
.SetToolTip(hevent
._label
)
5194 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5195 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5196 self
._isonhyperlink
= True
5198 if self
._isonhyperlink
:
5199 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5200 self
._isonhyperlink
= False
5202 # we process left mouse up event (enables in-place edit), right down
5203 # (pass to the user code), left dbl click (activate item) and
5204 # dragging/moving events for items drag-and-drop
5206 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5207 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5213 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5215 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5217 if self
._dragCount
== 0:
5218 self
._dragStart
= pt
5221 self
._dragCount
= self
._dragCount
+ 1
5223 if self
._dragCount
!= 3:
5224 # wait until user drags a bit further...
5227 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5229 nevent
= TreeEvent(command
, self
.GetId())
5230 nevent
._item
= self
._current
5231 nevent
.SetEventObject(self
)
5232 newpt
= self
.CalcScrolledPosition(pt
)
5233 nevent
.SetPoint(newpt
)
5235 # by default the dragging is not supported, the user code must
5236 # explicitly allow the event for it to take place
5239 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5241 # we're going to drag this item
5242 self
._isDragging
= True
5244 # remember the old cursor because we will change it while
5246 self
._oldCursor
= self
._cursor
5248 # in a single selection control, hide the selection temporarily
5249 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5250 self
._oldSelection
= self
.GetSelection()
5252 if self
._oldSelection
:
5254 self
._oldSelection
.SetHilight(False)
5255 self
.RefreshLine(self
._oldSelection
)
5257 selections
= self
.GetSelections()
5258 if len(selections
) == 1:
5259 self
._oldSelection
= selections
[0]
5260 self
._oldSelection
.SetHilight(False)
5261 self
.RefreshLine(self
._oldSelection
)
5266 # Create the custom draw image from the icons and the text of the item
5267 self
._dragImage
= DragImage(self
, self
._current
)
5268 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5269 self
._dragImage
.Show()
5270 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5272 elif event
.Dragging() and self
._isDragging
:
5274 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5276 if self
._countDrag
== 0 and item
:
5277 self
._oldItem
= item
5279 if item
!= self
._dropTarget
:
5281 # unhighlight the previous drop target
5282 if self
._dropTarget
:
5283 self
._dropTarget
.SetHilight(False)
5284 self
.RefreshLine(self
._dropTarget
)
5286 item
.SetHilight(True)
5287 self
.RefreshLine(item
)
5288 self
._countDrag
= self
._countDrag
+ 1
5289 self
._dropTarget
= item
5293 if self
._countDrag
>= 3:
5294 # Here I am trying to avoid ugly repainting problems... hope it works
5295 self
.RefreshLine(self
._oldItem
)
5298 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5301 self
._dragImage
.EndDrag()
5303 if self
._dropTarget
:
5304 self
._dropTarget
.SetHilight(False)
5306 if self
._oldSelection
:
5308 self
._oldSelection
.SetHilight(True)
5309 self
.RefreshLine(self
._oldSelection
)
5310 self
._oldSelection
= None
5312 # generate the drag end event
5313 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5315 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5316 event
.SetEventObject(self
)
5318 self
.GetEventHandler().ProcessEvent(event
)
5320 self
._isDragging
= False
5321 self
._dropTarget
= None
5323 self
.SetCursor(self
._oldCursor
)
5325 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5328 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5333 # If we got to this point, we are not dragging or moving the mouse.
5334 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5335 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5336 # We skip even if we didn't hit an item because we still should
5337 # restore focus to the tree control even if we didn't exactly hit an item.
5338 if event
.LeftDown():
5339 self
._hasFocus
= True
5340 self
.SetFocusIgnoringChildren()
5343 # here we process only the messages which happen on tree items
5348 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5349 self
._textCtrl
.StopEditing()
5350 return # we hit the blank area
5352 if event
.RightDown():
5354 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5355 self
._textCtrl
.StopEditing()
5357 self
._hasFocus
= True
5358 self
.SetFocusIgnoringChildren()
5360 # If the item is already selected, do not update the selection.
5361 # Multi-selections should not be cleared if a selected item is clicked.
5362 if not self
.IsSelected(item
):
5364 self
.DoSelectItem(item
, True, False)
5366 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5368 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5369 nevent
.SetEventObject(self
)
5370 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5372 # Consistent with MSW (for now), send the ITEM_MENU *after*
5373 # the RIGHT_CLICK event. TODO: This behaviour may change.
5374 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5375 nevent2
._item
= item
5376 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5377 nevent2
.SetEventObject(self
)
5378 self
.GetEventHandler().ProcessEvent(nevent2
)
5380 elif event
.LeftUp():
5382 # this facilitates multiple-item drag-and-drop
5384 if self
.HasFlag(TR_MULTIPLE
):
5386 selections
= self
.GetSelections()
5388 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5390 self
.DoSelectItem(item
, True, False)
5392 if self
._lastOnSame
:
5394 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5396 if self
._renameTimer
:
5398 if self
._renameTimer
.IsRunning():
5400 self
._renameTimer
.Stop()
5404 self
._renameTimer
= TreeRenameTimer(self
)
5406 self
._renameTimer
.Start(_DELAY
, True)
5408 self
._lastOnSame
= False
5411 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5413 if not item
or not item
.IsEnabled():
5414 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5415 self
._textCtrl
.StopEditing()
5418 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5419 self
._textCtrl
.StopEditing()
5421 self
._hasFocus
= True
5422 self
.SetFocusIgnoringChildren()
5424 if event
.LeftDown():
5426 self
._lastOnSame
= item
== self
._current
5428 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5430 # only toggle the item for a single click, double click on
5431 # the button doesn't do anything (it toggles the item twice)
5432 if event
.LeftDown():
5436 # don't select the item if the button was clicked
5439 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5441 if event
.LeftDown():
5443 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5447 # clear the previously selected items, if the
5448 # user clicked outside of the present selection.
5449 # otherwise, perform the deselection on mouse-up.
5450 # this allows multiple drag and drop to work.
5451 # but if Cmd is down, toggle selection of the clicked item
5452 if not self
.IsSelected(item
) or event
.CmdDown():
5454 if flags
& TREE_HITTEST_ONITEM
:
5455 # how should the selection work for this event?
5456 if item
.IsHyperText():
5457 self
.SetItemVisited(item
, True)
5459 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5463 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5465 # For some reason, Windows isn't recognizing a left double-click,
5466 # so we need to simulate it here. Allow 200 milliseconds for now.
5467 if event
.LeftDClick():
5469 # double clicking should not start editing the item label
5470 if self
._renameTimer
:
5471 self
._renameTimer
.Stop()
5473 self
._lastOnSame
= False
5475 # send activate event first
5476 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5478 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5479 nevent
.SetEventObject(self
)
5480 if not self
.GetEventHandler().ProcessEvent(nevent
):
5482 # if the user code didn't process the activate event,
5483 # handle it ourselves by toggling the item when it is
5485 ## if item.HasPlus():
5489 def OnInternalIdle(self
):
5490 """Performs operations in idle time (essentially drawing)."""
5492 # Check if we need to select the root item
5493 # because nothing else has been selected.
5494 # Delaying it means that we can invoke event handlers
5495 # as required, when a first item is selected.
5496 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5499 self
.SelectItem(self
._select
_me
)
5500 elif self
.GetRootItem():
5501 self
.SelectItem(self
.GetRootItem())
5503 # after all changes have been done to the tree control,
5504 # we actually redraw the tree when everything is over
5508 if self
._freezeCount
:
5513 self
.CalculatePositions()
5515 self
.AdjustMyScrollbars()
5520 def CalculateSize(self
, item
, dc
):
5521 """Calculates overall position and size of an item."""
5523 attr
= item
.GetAttributes()
5525 if attr
and attr
.HasFont():
5526 dc
.SetFont(attr
.GetFont())
5528 dc
.SetFont(self
._boldFont
)
5530 dc
.SetFont(self
._normalFont
)
5532 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5535 # restore normal font
5536 dc
.SetFont(self
._normalFont
)
5538 image_w
, image_h
= 0, 0
5539 image
= item
.GetCurrentImage()
5541 if image
!= _NO_IMAGE
:
5543 if self
._imageListNormal
:
5545 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5548 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5550 checkimage
= item
.GetCurrentCheckedImage()
5551 if checkimage
is not None:
5552 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5558 total_h
+= 2 # at least 2 pixels
5560 total_h
+= total_h
/10 # otherwise 10% extra spacing
5562 if total_h
> self
._lineHeight
:
5563 self
._lineHeight
= total_h
5565 if not item
.GetWindow():
5566 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5567 item
.SetHeight(total_h
)
5569 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5570 item
.SetHeight(max(total_h
, item
.GetWindowSize()[1]))
5573 def CalculateLevel(self
, item
, dc
, level
, y
):
5574 """Calculates the level of an item."""
5576 x
= level
*self
._indent
5578 if not self
.HasFlag(TR_HIDE_ROOT
):
5584 # a hidden root is not evaluated, but its
5585 # children are always calculated
5586 children
= item
.GetChildren()
5587 count
= len(children
)
5589 for n
in xrange(count
):
5590 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5594 self
.CalculateSize(item
, dc
)
5597 item
.SetX(x
+self
._spacing
)
5599 y
+= self
.GetLineHeight(item
)
5601 if not item
.IsExpanded():
5602 # we don't need to calculate collapsed branches
5605 children
= item
.GetChildren()
5606 count
= len(children
)
5608 for n
in xrange(count
):
5609 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5614 def CalculatePositions(self
):
5615 """Calculates all the positions of the visible items."""
5617 if not self
._anchor
:
5620 dc
= wx
.ClientDC(self
)
5623 dc
.SetFont(self
._normalFont
)
5624 dc
.SetPen(self
._dottedPen
)
5626 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5629 def RefreshSubtree(self
, item
):
5630 """Refreshes a damaged subtree of an item."""
5634 if self
._freezeCount
:
5637 client
= self
.GetClientSize()
5640 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5641 rect
.width
= client
.x
5642 rect
.height
= client
.y
5644 self
.Refresh(True, rect
)
5645 self
.AdjustMyScrollbars()
5648 def RefreshLine(self
, item
):
5649 """Refreshes a damaged item line."""
5653 if self
._freezeCount
:
5657 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5658 rect
.width
= self
.GetClientSize().x
5659 rect
.height
= self
.GetLineHeight(item
)
5661 self
.Refresh(True, rect
)
5664 def RefreshSelected(self
):
5665 """Refreshes a damaged selected item line."""
5667 if self
._freezeCount
:
5670 # TODO: this is awfully inefficient, we should keep the list of all
5671 # selected items internally, should be much faster
5673 self
.RefreshSelectedUnder(self
._anchor
)
5676 def RefreshSelectedUnder(self
, item
):
5677 """Refreshes the selected items under the given item."""
5679 if self
._freezeCount
:
5682 if item
.IsSelected():
5683 self
.RefreshLine(item
)
5685 children
= item
.GetChildren()
5686 for child
in children
:
5687 self
.RefreshSelectedUnder(child
)
5691 """Freeze CustomTreeCtrl."""
5693 self
._freezeCount
= self
._freezeCount
+ 1
5697 """Thaw CustomTreeCtrl."""
5699 if self
._freezeCount
== 0:
5700 raise Exception("\nERROR: Thawing Unfrozen Tree Control?")
5702 self
._freezeCount
= self
._freezeCount
- 1
5704 if not self
._freezeCount
:
5708 # ----------------------------------------------------------------------------
5709 # changing colours: we need to refresh the tree control
5710 # ----------------------------------------------------------------------------
5712 def SetBackgroundColour(self
, colour
):
5713 """Changes the background colour of CustomTreeCtrl."""
5715 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5718 if self
._freezeCount
:
5726 def SetForegroundColour(self
, colour
):
5727 """Changes the foreground colour of CustomTreeCtrl."""
5729 if not wx
.Window
.SetForegroundColour(self
, colour
):
5732 if self
._freezeCount
:
5740 def OnGetToolTip(self
, event
):
5742 Process the tooltip event, to speed up event processing. Does not actually
5749 def DoGetBestSize(self
):
5750 """Something is better than nothing..."""
5752 # something is better than nothing...
5753 # 100x80 is what the MSW version will get from the default
5754 # wxControl::DoGetBestSize
5756 return wx
.Size(100, 80)
5759 def GetClassDefaultAttributes(self
):
5760 """Gets the class default attributes."""
5762 attr
= wx
.VisualAttributes()
5763 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5764 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5765 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
5768 GetClassDefaultAttributes
= classmethod(GetClassDefaultAttributes
)