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 grey
= (160,160,160)
1874 if wx
.Platform
!= "__WXMAC__":
1875 self
._dottedPen
= wx
.Pen(grey
, 1, wx
.USER_DASH
)
1876 self
._dottedPen
.SetDashes([1,1])
1877 self
._dottedPen
.SetCap(wx
.CAP_BUTT
)
1879 self
._dottedPen
= wx
.Pen(grey
, 1)
1881 # Pen Used To Draw The Border Around Selected Items
1882 self
._borderPen
= wx
.BLACK_PEN
1883 self
._cursor
= wx
.StockCursor(wx
.CURSOR_ARROW
)
1885 # For Appended Windows
1886 self
._hasWindows
= False
1887 self
._itemWithWindow
= []
1889 if wx
.Platform
== "__WXMAC__":
1890 style
&= ~TR_LINES_AT_ROOT
1891 style |
= TR_NO_LINES
1893 platform
, major
, minor
= wx
.GetOsVersion()
1895 style |
= TR_ROW_LINES
1897 # Create the default check image list
1898 self
.SetImageListCheck(13, 13)
1900 # A constant to use my translation of RendererNative.DrawTreeItemButton
1901 # if the wxPython version is less than 2.6.2.1.
1902 if wx
.VERSION_STRING
< "2.6.2.1":
1903 self
._drawingfunction
= DrawTreeItemButton
1905 self
._drawingfunction
= wx
.RendererNative
.Get().DrawTreeItemButton
1907 # Create our container... at last!
1908 wx
.PyScrolledWindow
.__init
__(self
, parent
, id, pos
, size
, style|wx
.HSCROLL|wx
.VSCROLL
, name
)
1910 # If the tree display has no buttons, but does have
1911 # connecting lines, we can use a narrower layout.
1912 # It may not be a good idea to force this...
1913 if not self
.HasButtons() and not self
.HasFlag(TR_NO_LINES
):
1917 self
.SetValidator(validator
)
1919 attr
= self
.GetDefaultAttributes()
1920 self
.SetOwnForegroundColour(attr
.colFg
)
1921 self
.SetOwnBackgroundColour(wx
.WHITE
)
1923 if not self
._hasFont
:
1924 self
.SetOwnFont(attr
.font
)
1929 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1930 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1931 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouse
)
1932 self
.Bind(wx
.EVT_KEY_DOWN
, self
.OnKeyDown
)
1933 self
.Bind(wx
.EVT_SET_FOCUS
, self
.OnSetFocus
)
1934 self
.Bind(wx
.EVT_KILL_FOCUS
, self
.OnKillFocus
)
1935 self
.Bind(EVT_TREE_ITEM_GETTOOLTIP
, self
.OnGetToolTip
)
1936 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.OnDestroy
)
1938 # Sets the focus to ourselves: this is useful if you have items
1939 # with associated widgets.
1943 def AcceptsFocus(self
):
1944 # overridden base class method, allows this ctrl to
1945 # participate in the tab-order, etc. It's overridable because
1946 # of deriving this class from wx.PyScrolledWindow...
1950 def OnDestroy(self
, event
):
1951 """Handles the wx.EVT_WINDOW_DESTROY event."""
1953 # Here there may be something I miss... do I have to destroy
1955 if self
._renameTimer
and self
._renameTimer
.IsRunning():
1956 self
._renameTimer
.Stop()
1957 del self
._renameTimer
1959 if self
._findTimer
and self
._findTimer
.IsRunning():
1960 self
._findTimer
.Stop()
1967 """Returns the global number of items in the tree."""
1969 if not self
._anchor
:
1973 count
= self
._anchor
.GetChildrenCount()
1975 if not self
.HasFlag(TR_HIDE_ROOT
):
1976 # take the root itself into account
1982 def GetIndent(self
):
1983 """Returns the item indentation."""
1988 def GetSpacing(self
):
1989 """Returns the spacing between the start and the text."""
1991 return self
._spacing
1994 def GetRootItem(self
):
1995 """Returns the root item."""
2000 def GetSelection(self
):
2001 """Returns the current selection: TR_SINGLE only."""
2003 return self
._current
2006 def ToggleItemSelection(self
, item
):
2007 """Toggles the item selection."""
2010 raise Exception("\nERROR: Invalid Tree Item. ")
2012 self
.SelectItem(item
, not self
.IsSelected(item
))
2015 def EnableChildren(self
, item
, enable
=True):
2016 """Enables/disables item children. Used internally."""
2019 if item
.IsExpanded():
2022 if item
.GetType() == 2 and enable
and not item
.IsChecked():
2023 # We hit a radiobutton item not checked, we don't want to
2024 # enable the children
2027 child
, cookie
= self
.GetFirstChild(item
)
2029 self
.EnableItem(child
, enable
, torefresh
=torefresh
)
2031 if child
.GetType
!= 2 or (child
.GetType() == 2 and item
.IsChecked()):
2032 self
.EnableChildren(child
, enable
)
2033 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2036 def EnableItem(self
, item
, enable
=True, torefresh
=True):
2037 """Enables/disables an item."""
2040 raise Exception("\nERROR: Invalid Tree Item. ")
2042 if item
.IsEnabled() == enable
:
2045 if not enable
and item
.IsSelected():
2046 self
.SelectItem(item
, False)
2049 wnd
= item
.GetWindow()
2051 # Handles the eventual window associated to the item
2053 wndenable
= item
.GetWindowEnabled()
2061 # We have to refresh the item line
2062 dc
= wx
.ClientDC(self
)
2063 self
.CalculateSize(item
, dc
)
2064 self
.RefreshLine(item
)
2067 def IsItemEnabled(self
, item
):
2068 """Returns whether an item is enabled or disabled."""
2071 raise Exception("\nERROR: Invalid Tree Item. ")
2073 return item
.IsEnabled()
2076 def SetDisabledColour(self
, colour
):
2077 """Sets the items disabled colour."""
2079 self
._disabledColour
= colour
2083 def GetDisabledColour(self
):
2084 """Returns the items disabled colour."""
2086 return self
._disabledColour
2089 def IsItemChecked(self
, item
):
2090 """Returns whether an item is checked or not."""
2093 raise Exception("\nERROR: Invalid Tree Item. ")
2095 return item
.IsChecked()
2098 def CheckItem2(self
, item
, checked
=True, torefresh
=False):
2099 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2101 if item
.GetType() == 0:
2107 dc
= wx
.ClientDC(self
)
2108 self
.CalculateSize(item
, dc
)
2109 self
.RefreshLine(item
)
2112 def UnCheckRadioParent(self
, item
, checked
=False):
2113 """Used internally to handle radio node parent correctly."""
2115 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2117 e
.SetEventObject(self
)
2119 if self
.GetEventHandler().ProcessEvent(e
):
2123 self
.RefreshLine(item
)
2124 self
.EnableChildren(item
, checked
)
2125 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2127 e
.SetEventObject(self
)
2128 self
.GetEventHandler().ProcessEvent(e
)
2133 def CheckItem(self
, item
, checked
=True):
2135 Actually checks/uncheks an item, sending (eventually) the two
2136 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2140 raise Exception("\nERROR: Invalid Tree Item. ")
2142 # Should we raise an error here?!?
2143 if item
.GetType() == 0:
2146 if item
.GetType() == 2: # it's a radio button
2147 if not checked
and item
.IsChecked(): # Try To Unckeck?
2148 if item
.HasChildren():
2149 self
.UnCheckRadioParent(item
, checked
)
2152 if not self
.UnCheckRadioParent(item
, checked
):
2155 self
.CheckSameLevel(item
, False)
2158 # Radiobuttons are done, let's handle checkbuttons...
2159 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKING
, self
.GetId())
2161 e
.SetEventObject(self
)
2163 if self
.GetEventHandler().ProcessEvent(e
):
2168 dc
= wx
.ClientDC(self
)
2169 self
.RefreshLine(item
)
2171 if self
.HasFlag(TR_AUTO_CHECK_CHILD
):
2172 ischeck
= self
.IsItemChecked(item
)
2173 self
.AutoCheckChild(item
, ischeck
)
2174 if self
.HasFlag(TR_AUTO_CHECK_PARENT
):
2175 ischeck
= self
.IsItemChecked(item
)
2176 self
.AutoCheckParent(item
, ischeck
)
2177 elif self
.HasFlag(TR_AUTO_TOGGLE_CHILD
):
2178 self
.AutoToggleChild(item
)
2180 e
= TreeEvent(wxEVT_TREE_ITEM_CHECKED
, self
.GetId())
2182 e
.SetEventObject(self
)
2183 self
.GetEventHandler().ProcessEvent(e
)
2186 def AutoToggleChild(self
, item
):
2187 """Transverses the tree and toggles the items. Meaningful only for check items."""
2190 raise Exception("\nERROR: Invalid Tree Item. ")
2192 child
, cookie
= self
.GetFirstChild(item
)
2195 if item
.IsExpanded():
2200 if child
.GetType() == 1 and child
.IsEnabled():
2201 self
.CheckItem2(child
, not child
.IsChecked(), torefresh
=torefresh
)
2202 self
.AutoToggleChild(child
)
2203 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2206 def AutoCheckChild(self
, item
, checked
):
2207 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2210 raise Exception("\nERROR: Invalid Tree Item. ")
2212 (child
, cookie
) = self
.GetFirstChild(item
)
2215 if item
.IsExpanded():
2219 if child
.GetType() == 1 and child
.IsEnabled():
2220 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2221 self
.AutoCheckChild(child
, checked
)
2222 (child
, cookie
) = self
.GetNextChild(item
, cookie
)
2225 def AutoCheckParent(self
, item
, checked
):
2226 """Traverses up the tree and checks/unchecks parent items.
2227 Meaningful only for check items."""
2230 raise Exception("\nERROR: Invalid Tree Item. ")
2232 parent
= item
.GetParent()
2233 if not parent
or parent
.GetType() != 1:
2236 (child
, cookie
) = self
.GetFirstChild(parent
)
2238 if child
.GetType() == 1 and child
.IsEnabled():
2239 if checked
!= child
.IsChecked():
2241 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2243 self
.CheckItem2(parent
, checked
, torefresh
=True)
2244 self
.AutoCheckParent(parent
, checked
)
2247 def CheckChilds(self
, item
, checked
=True):
2248 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2251 raise Exception("\nERROR: Invalid Tree Item. ")
2254 self
.AutoToggleChild(item
)
2256 self
.AutoCheckChild(item
, checked
)
2259 def CheckSameLevel(self
, item
, checked
=False):
2261 Uncheck radio items which are on the same level of the checked one.
2265 parent
= item
.GetParent()
2271 if parent
.IsExpanded():
2274 (child
, cookie
) = self
.GetFirstChild(parent
)
2276 if child
.GetType() == 2 and child
!= item
:
2277 self
.CheckItem2(child
, checked
, torefresh
=torefresh
)
2278 if child
.GetType
!= 2 or (child
.GetType() == 2 and child
.IsChecked()):
2279 self
.EnableChildren(child
, checked
)
2280 (child
, cookie
) = self
.GetNextChild(parent
, cookie
)
2283 def EditLabel(self
, item
):
2284 """Starts editing an item label."""
2287 raise Exception("\nERROR: Invalid Tree Item. ")
2292 def ShouldInheritColours(self
):
2293 """We don't inherit colours from anyone."""
2298 def SetIndent(self
, indent
):
2299 """Sets item indentation."""
2301 self
._indent
= indent
2305 def SetSpacing(self
, spacing
):
2306 """Sets item spacing."""
2308 self
._spacing
= spacing
2312 def HasChildren(self
, item
):
2313 """Returns whether an item has children or not."""
2316 raise Exception("\nERROR: Invalid Tree Item. ")
2318 return len(item
.GetChildren()) > 0
2321 def GetChildrenCount(self
, item
, recursively
=True):
2322 """Gets the item children count."""
2325 raise Exception("\nERROR: Invalid Tree Item. ")
2327 return item
.GetChildrenCount(recursively
)
2330 def SetTreeStyle(self
, styles
):
2331 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2333 # Do not try to expand the root node if it hasn't been created yet
2334 if self
._anchor
and not self
.HasFlag(TR_HIDE_ROOT
) and styles
& TR_HIDE_ROOT
:
2336 # if we will hide the root, make sure children are visible
2337 self
._anchor
.SetHasPlus()
2338 self
._anchor
.Expand()
2339 self
.CalculatePositions()
2341 # right now, just sets the styles. Eventually, we may
2342 # want to update the inherited styles, but right now
2343 # none of the parents has updatable styles
2345 if self
.HasFlag(TR_MULTIPLE
) and not (styles
& TR_MULTIPLE
):
2346 selections
= self
.GetSelections()
2347 for select
in selections
[0:-1]:
2348 self
.SelectItem(select
, False)
2350 self
.SetWindowStyle(styles
)
2354 def GetTreeStyle(self
):
2355 """Returns the CustomTreeCtrl style."""
2357 return self
.GetWindowStyle()
2360 def HasButtons(self
):
2361 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2363 return self
.HasFlag(TR_HAS_BUTTONS
)
2366 # -----------------------------------------------------------------------------
2367 # functions to work with tree items
2368 # -----------------------------------------------------------------------------
2370 def GetItemText(self
, item
):
2371 """Returns the item text."""
2374 raise Exception("\nERROR: Invalid Tree Item. ")
2376 return item
.GetText()
2379 def GetItemImage(self
, item
, which
=TreeItemIcon_Normal
):
2380 """Returns the item image."""
2383 raise Exception("\nERROR: Invalid Tree Item. ")
2385 return item
.GetImage(which
)
2388 def GetPyData(self
, item
):
2389 """Returns the data associated to an item."""
2392 raise Exception("\nERROR: Invalid Tree Item. ")
2394 return item
.GetData()
2396 GetItemPyData
= GetPyData
2399 def GetItemTextColour(self
, item
):
2400 """Returns the item text colour."""
2403 raise Exception("\nERROR: Invalid Tree Item. ")
2405 return item
.Attr().GetTextColour()
2408 def GetItemBackgroundColour(self
, item
):
2409 """Returns the item background colour."""
2412 raise Exception("\nERROR: Invalid Tree Item. ")
2414 return item
.Attr().GetBackgroundColour()
2417 def GetItemFont(self
, item
):
2418 """Returns the item font."""
2421 raise Exception("\nERROR: Invalid Tree Item. ")
2423 return item
.Attr().GetFont()
2426 def IsItemHyperText(self
, item
):
2427 """Returns whether an item is hypertext or not."""
2430 raise Exception("\nERROR: Invalid Tree Item. ")
2432 return item
.IsHyperText()
2435 def SetItemText(self
, item
, text
):
2436 """Sets the item text."""
2439 raise Exception("\nERROR: Invalid Tree Item. ")
2441 dc
= wx
.ClientDC(self
)
2443 self
.CalculateSize(item
, dc
)
2444 self
.RefreshLine(item
)
2447 def SetItemImage(self
, item
, image
, which
=TreeItemIcon_Normal
):
2448 """Sets the item image, depending on the item state."""
2451 raise Exception("\nERROR: Invalid Tree Item. ")
2453 item
.SetImage(image
, which
)
2455 dc
= wx
.ClientDC(self
)
2456 self
.CalculateSize(item
, dc
)
2457 self
.RefreshLine(item
)
2460 def SetPyData(self
, item
, data
):
2461 """Sets the data associated to an item."""
2464 raise Exception("\nERROR: Invalid Tree Item. ")
2468 SetItemPyData
= SetPyData
2471 def SetItemHasChildren(self
, item
, has
=True):
2472 """Forces the appearance of the button next to the item."""
2475 raise Exception("\nERROR: Invalid Tree Item. ")
2477 item
.SetHasPlus(has
)
2478 self
.RefreshLine(item
)
2481 def SetItemBold(self
, item
, bold
=True):
2482 """Sets the item font bold/unbold."""
2485 raise Exception("\nERROR: Invalid Tree Item. ")
2487 # avoid redrawing the tree if no real change
2488 if item
.IsBold() != bold
:
2493 def SetItemItalic(self
, item
, italic
=True):
2494 """Sets the item font italic/non-italic."""
2497 raise Exception("\nERROR: Invalid Tree Item. ")
2499 if item
.IsItalic() != italic
:
2500 itemFont
= self
.GetItemFont(item
)
2501 if itemFont
!= wx
.NullFont
:
2506 item
.SetItalic(italic
)
2507 itemFont
.SetStyle(style
)
2508 self
.SetItemFont(item
, itemFont
)
2512 def SetItemDropHighlight(self
, item
, highlight
=True):
2514 Gives the item the visual feedback for drag and drop operations.
2515 This is useful when something is dragged from outside the CustomTreeCtrl.
2519 raise Exception("\nERROR: Invalid Tree Item. ")
2522 bg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2523 fg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
2525 item
.Attr().SetTextColour(fg
)
2526 item
.Attr
.SetBackgroundColour(bg
)
2527 self
.RefreshLine(item
)
2530 def SetItemTextColour(self
, item
, col
):
2531 """Sets the item text colour."""
2534 raise Exception("\nERROR: Invalid Tree Item. ")
2536 if self
.GetItemTextColour(item
) == col
:
2539 item
.Attr().SetTextColour(col
)
2540 self
.RefreshLine(item
)
2543 def SetItemBackgroundColour(self
, item
, col
):
2544 """Sets the item background colour."""
2547 raise Exception("\nERROR: Invalid Tree Item. ")
2549 item
.Attr().SetBackgroundColour(col
)
2550 self
.RefreshLine(item
)
2553 def SetItemHyperText(self
, item
, hyper
=True):
2554 """Sets whether the item is hypertext or not."""
2557 raise Exception("\nERROR: Invalid Tree Item. ")
2559 item
.SetHyperText(hyper
)
2560 self
.RefreshLine(item
)
2563 def SetItemFont(self
, item
, font
):
2564 """Sets the item font."""
2567 raise Exception("\nERROR: Invalid Tree Item. ")
2569 if self
.GetItemFont(item
) == font
:
2572 item
.Attr().SetFont(font
)
2576 def SetFont(self
, font
):
2577 """Sets the CustomTreeCtrl font."""
2579 wx
.ScrolledWindow
.SetFont(self
, font
)
2581 self
._normalFont
= font
2582 self
._boldFont
= wx
.Font(self
._normalFont
.GetPointSize(), self
._normalFont
.GetFamily(),
2583 self
._normalFont
.GetStyle(), wx
.BOLD
, self
._normalFont
.GetUnderlined(),
2584 self
._normalFont
.GetFaceName(), self
._normalFont
.GetEncoding())
2589 def GetHyperTextFont(self
):
2590 """Returns the font used to render an hypertext item."""
2592 return self
._hypertextfont
2595 def SetHyperTextFont(self
, font
):
2596 """Sets the font used to render an hypertext item."""
2598 self
._hypertextfont
= font
2602 def SetHyperTextNewColour(self
, colour
):
2603 """Sets the colour used to render a non-visited hypertext item."""
2605 self
._hypertextnewcolour
= colour
2609 def GetHyperTextNewColour(self
):
2610 """Returns the colour used to render a non-visited hypertext item."""
2612 return self
._hypertextnewcolour
2615 def SetHyperTextVisitedColour(self
, colour
):
2616 """Sets the colour used to render a visited hypertext item."""
2618 self
._hypertextvisitedcolour
= colour
2622 def GetHyperTextVisitedColour(self
):
2623 """Returns the colour used to render a visited hypertext item."""
2625 return self
._hypertextvisitedcolour
2628 def SetItemVisited(self
, item
, visited
=True):
2629 """Sets whether an hypertext item was visited."""
2632 raise Exception("\nERROR: Invalid Tree Item. ")
2634 item
.SetVisited(visited
)
2635 self
.RefreshLine(item
)
2638 def GetItemVisited(self
, item
):
2639 """Returns whether an hypertext item was visited."""
2642 raise Exception("\nERROR: Invalid Tree Item. ")
2644 return item
.GetVisited()
2647 def SetHilightFocusColour(self
, colour
):
2649 Sets the colour used to highlight focused selected items.
2650 This is applied only if gradient and Windows Vista styles are disabled.
2653 self
._hilightBrush
= wx
.Brush(colour
)
2654 self
.RefreshSelected()
2657 def SetHilightNonFocusColour(self
, colour
):
2659 Sets the colour used to highlight unfocused selected items.
2660 This is applied only if gradient and Windows Vista styles are disabled.
2663 self
._hilightUnfocusedBrush
= wx
.Brush(colour
)
2664 self
.RefreshSelected()
2667 def GetHilightFocusColour(self
):
2669 Returns the colour used to highlight focused selected items.
2670 This is applied only if gradient and Windows Vista styles are disabled.
2673 return self
._hilightBrush
.GetColour()
2676 def GetHilightNonFocusColour(self
):
2678 Returns the colour used to highlight unfocused selected items.
2679 This is applied only if gradient and Windows Vista styles are disabled.
2682 return self
._hilightUnfocusedBrush
.GetColour()
2685 def SetFirstGradientColour(self
, colour
=None):
2686 """Sets the first gradient colour."""
2689 colour
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
)
2691 self
._firstcolour
= colour
2692 if self
._usegradients
:
2693 self
.RefreshSelected()
2696 def SetSecondGradientColour(self
, colour
=None):
2697 """Sets the second gradient colour."""
2700 # No colour given, generate a slightly darker from the
2701 # CustomTreeCtrl background colour
2702 color
= self
.GetBackgroundColour()
2703 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
2704 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
2705 colour
= wx
.Colour(color
[0], color
[1], color
[2])
2707 self
._secondcolour
= colour
2709 if self
._usegradients
:
2710 self
.RefreshSelected()
2713 def GetFirstGradientColour(self
):
2714 """Returns the first gradient colour."""
2716 return self
._firstcolour
2719 def GetSecondGradientColour(self
):
2720 """Returns the second gradient colour."""
2722 return self
._secondcolour
2725 def EnableSelectionGradient(self
, enable
=True):
2726 """Globally enables/disables drawing of gradient selection."""
2728 self
._usegradients
= enable
2729 self
._vistaselection
= False
2730 self
.RefreshSelected()
2733 def SetGradientStyle(self
, vertical
=0):
2735 Sets the gradient style:
2736 0: horizontal gradient
2737 1: vertical gradient
2740 # 0 = Horizontal, 1 = Vertical
2741 self
._gradientstyle
= vertical
2743 if self
._usegradients
:
2744 self
.RefreshSelected()
2747 def GetGradientStyle(self
):
2749 Returns the gradient style:
2750 0: horizontal gradient
2751 1: vertical gradient
2754 return self
._gradientstyle
2757 def EnableSelectionVista(self
, enable
=True):
2758 """Globally enables/disables drawing of Windows Vista selection."""
2760 self
._usegradients
= False
2761 self
._vistaselection
= enable
2762 self
.RefreshSelected()
2765 def SetBorderPen(self
, pen
):
2767 Sets the pen used to draw the selected item border.
2768 The border pen is not used if the Windows Vista style is applied.
2771 self
._borderPen
= pen
2772 self
.RefreshSelected()
2775 def GetBorderPen(self
):
2777 Returns the pen used to draw the selected item border.
2778 The border pen is not used if the Windows Vista style is applied.
2781 return self
._borderPen
2784 def SetConnectionPen(self
, pen
):
2785 """Sets the pen used to draw the connecting lines between items."""
2787 self
._dottedPen
= pen
2791 def GetConnectionPen(self
):
2792 """Returns the pen used to draw the connecting lines between items."""
2794 return self
._dottedPen
2797 def SetBackgroundImage(self
, image
):
2798 """Sets the CustomTreeCtrl background image (can be none)."""
2800 self
._backgroundImage
= image
2804 def GetBackgroundImage(self
):
2805 """Returns the CustomTreeCtrl background image (can be none)."""
2807 return self
._backgroundImage
2810 def GetItemWindow(self
, item
):
2811 """Returns the window associated to the item (if any)."""
2814 raise Exception("\nERROR: Invalid Item")
2816 return item
.GetWindow()
2819 def GetItemWindowEnabled(self
, item
):
2820 """Returns whether the window associated to the item is enabled."""
2823 raise Exception("\nERROR: Invalid Item")
2825 return item
.GetWindowEnabled()
2828 def SetItemWindowEnabled(self
, item
, enable
=True):
2829 """Enables/disables the window associated to the item."""
2832 raise Exception("\nERROR: Invalid Item")
2834 item
.SetWindowEnabled(enable
)
2837 def GetItemType(self
, item
):
2839 Returns the item type:
2846 raise Exception("\nERROR: Invalid Item")
2848 return item
.GetType()
2850 # -----------------------------------------------------------------------------
2851 # item status inquiries
2852 # -----------------------------------------------------------------------------
2854 def IsVisible(self
, item
):
2855 """Returns whether the item is visible or not."""
2858 raise Exception("\nERROR: Invalid Tree Item. ")
2860 # An item is only visible if it's not a descendant of a collapsed item
2861 parent
= item
.GetParent()
2865 if not parent
.IsExpanded():
2868 parent
= parent
.GetParent()
2870 startX
, startY
= self
.GetViewStart()
2871 clientSize
= self
.GetClientSize()
2873 rect
= self
.GetBoundingRect(item
)
2877 if rect
.GetWidth() == 0 or rect
.GetHeight() == 0:
2879 if rect
.GetBottom() < 0 or rect
.GetTop() > clientSize
.y
:
2881 if rect
.GetRight() < 0 or rect
.GetLeft() > clientSize
.x
:
2887 def ItemHasChildren(self
, item
):
2888 """Returns whether the item has children or not."""
2891 raise Exception("\nERROR: Invalid Tree Item. ")
2893 # consider that the item does have children if it has the "+" button: it
2894 # might not have them (if it had never been expanded yet) but then it
2895 # could have them as well and it's better to err on this side rather than
2896 # disabling some operations which are restricted to the items with
2897 # children for an item which does have them
2898 return item
.HasPlus()
2901 def IsExpanded(self
, item
):
2902 """Returns whether the item is expanded or not."""
2905 raise Exception("\nERROR: Invalid Tree Item. ")
2907 return item
.IsExpanded()
2910 def IsSelected(self
, item
):
2911 """Returns whether the item is selected or not."""
2914 raise Exception("\nERROR: Invalid Tree Item. ")
2916 return item
.IsSelected()
2919 def IsBold(self
, item
):
2920 """Returns whether the item font is bold or not."""
2923 raise Exception("\nERROR: Invalid Tree Item. ")
2925 return item
.IsBold()
2928 def IsItalic(self
, item
):
2929 """Returns whether the item font is italic or not."""
2932 raise Exception("\nERROR: Invalid Tree Item. ")
2934 return item
.IsItalic()
2937 # -----------------------------------------------------------------------------
2939 # -----------------------------------------------------------------------------
2941 def GetItemParent(self
, item
):
2942 """Gets the item parent."""
2945 raise Exception("\nERROR: Invalid Tree Item. ")
2947 return item
.GetParent()
2950 def GetFirstChild(self
, item
):
2951 """Gets the item first child."""
2954 raise Exception("\nERROR: Invalid Tree Item. ")
2957 return self
.GetNextChild(item
, cookie
)
2960 def GetNextChild(self
, item
, cookie
):
2962 Gets the item next child based on the 'cookie' parameter.
2963 This method has no sense if you do not call GetFirstChild() before.
2967 raise Exception("\nERROR: Invalid Tree Item. ")
2969 children
= item
.GetChildren()
2971 # it's ok to cast cookie to size_t, we never have indices big enough to
2974 if cookie
< len(children
):
2976 return children
[cookie
], cookie
+1
2980 # there are no more of them
2984 def GetLastChild(self
, item
):
2985 """Gets the item last child."""
2988 raise Exception("\nERROR: Invalid Tree Item. ")
2990 children
= item
.GetChildren()
2991 return (len(children
) == 0 and [None] or [children
[-1]])[0]
2994 def GetNextSibling(self
, item
):
2995 """Gets the next sibling of an item."""
2998 raise Exception("\nERROR: Invalid Tree Item. ")
3001 parent
= i
.GetParent()
3005 # root item doesn't have any siblings
3008 siblings
= parent
.GetChildren()
3009 index
= siblings
.index(i
)
3012 return (n
== len(siblings
) and [None] or [siblings
[n
]])[0]
3015 def GetPrevSibling(self
, item
):
3016 """Gets the previous sibling of an item."""
3019 raise Exception("\nERROR: Invalid Tree Item. ")
3022 parent
= i
.GetParent()
3026 # root item doesn't have any siblings
3029 siblings
= parent
.GetChildren()
3030 index
= siblings
.index(i
)
3032 return (index
== 0 and [None] or [siblings
[index
-1]])[0]
3035 def GetNext(self
, item
):
3036 """Gets the next item. Only for internal use right now."""
3039 raise Exception("\nERROR: Invalid Tree Item. ")
3043 # First see if there are any children.
3044 children
= i
.GetChildren()
3045 if len(children
) > 0:
3048 # Try a sibling of this or ancestor instead
3051 while p
and not toFind
:
3052 toFind
= self
.GetNextSibling(p
)
3053 p
= self
.GetItemParent(p
)
3058 def GetFirstVisibleItem(self
):
3059 """Returns the first visible item."""
3061 id = self
.GetRootItem()
3066 if self
.IsVisible(id):
3068 id = self
.GetNext(id)
3073 def GetNextVisible(self
, item
):
3074 """Returns the next visible item."""
3077 raise Exception("\nERROR: Invalid Tree Item. ")
3082 id = self
.GetNext(id)
3083 if id and self
.IsVisible(id):
3089 def GetPrevVisible(self
, item
):
3092 raise Exception("\nERROR: Invalid Tree Item. ")
3094 raise Exception("\nERROR: Not Implemented")
3099 def ResetTextControl(self
):
3100 """Called by TreeTextCtrl when it marks itself for deletion."""
3102 self
._textCtrl
.Destroy()
3103 self
._textCtrl
= None
3106 def FindItem(self
, idParent
, prefixOrig
):
3107 """Finds the first item starting with the given prefix after the given item."""
3109 # match is case insensitive as this is more convenient to the user: having
3110 # to press Shift-letter to go to the item starting with a capital letter
3111 # would be too bothersome
3112 prefix
= prefixOrig
.lower()
3114 # determine the starting point: we shouldn't take the current item (this
3115 # allows to switch between two items starting with the same letter just by
3116 # pressing it) but we shouldn't jump to the next one if the user is
3117 # continuing to type as otherwise he might easily skip the item he wanted
3120 if len(prefix
) == 1:
3121 id = self
.GetNext(id)
3123 # look for the item starting with the given prefix after it
3124 while id and not self
.GetItemText(id).lower().startswith(prefix
):
3126 id = self
.GetNext(id)
3128 # if we haven't found anything...
3131 # ... wrap to the beginning
3132 id = self
.GetRootItem()
3133 if self
.HasFlag(TR_HIDE_ROOT
):
3134 # can't select virtual root
3135 id = self
.GetNext(id)
3137 # and try all the items (stop when we get to the one we started from)
3138 while id != idParent
and not self
.GetItemText(id).lower().startswith(prefix
):
3139 id = self
.GetNext(id)
3144 # -----------------------------------------------------------------------------
3146 # -----------------------------------------------------------------------------
3148 def DoInsertItem(self
, parentId
, previous
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3149 """Actually inserts an item in the tree."""
3151 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3152 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3154 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3155 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3157 if ct_type
< 0 or ct_type
> 2:
3158 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3164 # should we give a warning here?
3165 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3167 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3169 item
= GenericTreeItem(parent
, text
, ct_type
, wnd
, image
, selImage
, data
)
3172 self
._hasWindows
= True
3173 self
._itemWithWindow
.append(item
)
3175 parent
.Insert(item
, previous
)
3180 def AddRoot(self
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3181 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3184 raise Exception("\nERROR: Tree Can Have Only One Root")
3186 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3187 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3189 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3190 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3192 if ct_type
< 0 or ct_type
> 2:
3193 raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
3195 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3197 self
._anchor
= GenericTreeItem(None, text
, ct_type
, wnd
, image
, selImage
, data
)
3200 self
._hasWindows
= True
3201 self
._itemWithWindow
.append(self
._anchor
)
3203 if self
.HasFlag(TR_HIDE_ROOT
):
3205 # if root is hidden, make sure we can navigate
3207 self
._anchor
.SetHasPlus()
3208 self
._anchor
.Expand()
3209 self
.CalculatePositions()
3211 if not self
.HasFlag(TR_MULTIPLE
):
3213 self
._current
= self
._key
_current
= self
._anchor
3214 self
._current
.SetHilight(True)
3219 def PrependItem(self
, parent
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3220 """Appends an item as a first child of parent."""
3222 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3223 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3225 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3226 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3228 return self
.DoInsertItem(parent
, 0, text
, ct_type
, wnd
, image
, selImage
, data
)
3231 def InsertItemByItem(self
, parentId
, idPrevious
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3232 """Auxiliary function to cope with the C++ hideous multifunction."""
3234 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3235 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3237 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3238 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3243 # should we give a warning here?
3244 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3250 index
= parent
.GetChildren().index(idPrevious
)
3252 raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
3254 return self
.DoInsertItem(parentId
, index
+1, text
, ct_type
, wnd
, image
, selImage
, data
)
3257 def InsertItemByIndex(self
, parentId
, before
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3258 """Auxiliary function to cope with the C++ hideous multifunction."""
3260 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3261 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3263 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3264 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3269 # should we give a warning here?
3270 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3272 return self
.DoInsertItem(parentId
, before
, text
, ct_type
, wnd
, image
, selImage
, data
)
3275 def InsertItem(self
, parentId
, input, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3276 """Inserts an item after the given previous."""
3278 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3279 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3281 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3282 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3284 if type(input) == type(1):
3285 return self
.InsertItemByIndex(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3287 return self
.InsertItemByItem(parentId
, input, text
, ct_type
, wnd
, image
, selImage
, data
)
3290 def AppendItem(self
, parentId
, text
, ct_type
=0, wnd
=None, image
=-1, selImage
=-1, data
=None):
3291 """Appends an item as a last child of its parent."""
3293 if wnd
is not None and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3294 raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3296 if text
.find("\n") >= 0 and not self
.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT
):
3297 raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
3302 # should we give a warning here?
3303 return self
.AddRoot(text
, ct_type
, wnd
, image
, selImage
, data
)
3305 return self
.DoInsertItem(parent
, len(parent
.GetChildren()), text
, ct_type
, wnd
, image
, selImage
, data
)
3308 def SendDeleteEvent(self
, item
):
3309 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3311 event
= TreeEvent(wxEVT_TREE_DELETE_ITEM
, self
.GetId())
3313 event
.SetEventObject(self
)
3314 self
.GetEventHandler().ProcessEvent(event
)
3317 def IsDescendantOf(self
, parent
, item
):
3318 """Checks if the given item is under another one."""
3324 # item is a descendant of parent
3327 item
= item
.GetParent()
3332 # Don't leave edit or selection on a child which is about to disappear
3333 def ChildrenClosing(self
, item
):
3334 """We are about to destroy the item children."""
3336 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item() and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3337 self
._textCtrl
.StopEditing()
3339 if item
!= self
._key
_current
and self
.IsDescendantOf(item
, self
._key
_current
):
3340 self
._key
_current
= None
3342 if self
.IsDescendantOf(item
, self
._select
_me
):
3343 self
._select
_me
= item
3345 if item
!= self
._current
and self
.IsDescendantOf(item
, self
._current
):
3346 self
._current
.SetHilight(False)
3347 self
._current
= None
3348 self
._select
_me
= item
3351 def DeleteChildren(self
, item
):
3352 """Delete item children."""
3355 raise Exception("\nERROR: Invalid Tree Item. ")
3357 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3359 self
.ChildrenClosing(item
)
3360 item
.DeleteChildren(self
)
3363 def Delete(self
, item
):
3364 """Delete an item."""
3367 raise Exception("\nERROR: Invalid Tree Item. ")
3369 self
._dirty
= True # do this first so stuff below doesn't cause flicker
3371 if self
._textCtrl
!= None and self
.IsDescendantOf(item
, self
._textCtrl
.item()):
3372 # can't delete the item being edited, cancel editing it first
3373 self
._textCtrl
.StopEditing()
3375 parent
= item
.GetParent()
3377 # don't keep stale pointers around!
3378 if self
.IsDescendantOf(item
, self
._key
_current
):
3380 # Don't silently change the selection:
3381 # do it properly in idle time, so event
3382 # handlers get called.
3384 # self._key_current = parent
3385 self
._key
_current
= None
3387 # self._select_me records whether we need to select
3388 # a different item, in idle time.
3389 if self
._select
_me
and self
.IsDescendantOf(item
, self
._select
_me
):
3390 self
._select
_me
= parent
3392 if self
.IsDescendantOf(item
, self
._current
):
3394 # Don't silently change the selection:
3395 # do it properly in idle time, so event
3396 # handlers get called.
3398 # self._current = parent
3399 self
._current
= None
3400 self
._select
_me
= parent
3402 # remove the item from the tree
3405 parent
.GetChildren().remove(item
) # remove by value
3407 else: # deleting the root
3409 # nothing will be left in the tree
3412 # and delete all of its children and the item itself now
3413 item
.DeleteChildren(self
)
3414 self
.SendDeleteEvent(item
)
3416 if item
== self
._select
_me
:
3417 self
._select
_me
= None
3419 # Remove the item with window
3420 if item
in self
._itemWithWindow
:
3421 wnd
= item
.GetWindow()
3425 self
._itemWithWindow
.remove(item
)
3430 def DeleteAllItems(self
):
3431 """Delete all items in the CustomTreeCtrl."""
3434 self
.Delete(self
._anchor
)
3437 def Expand(self
, item
):
3439 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3440 EVT_TREE_ITEM_EXPANDED events.
3444 raise Exception("\nERROR: Invalid Tree Item. ")
3446 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3447 raise Exception("\nERROR: Can't Expand An Hidden Root. ")
3449 if not item
.HasPlus():
3452 if item
.IsExpanded():
3455 event
= TreeEvent(wxEVT_TREE_ITEM_EXPANDING
, self
.GetId())
3457 event
.SetEventObject(self
)
3459 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3460 # cancelled by program
3464 self
.CalculatePositions()
3466 self
.RefreshSubtree(item
)
3468 if self
._hasWindows
:
3469 # We hide the associated window here, we may show it after
3472 event
.SetEventType(wxEVT_TREE_ITEM_EXPANDED
)
3473 self
.GetEventHandler().ProcessEvent(event
)
3476 def ExpandAllChildren(self
, item
):
3477 """Expands all the items children of the input item."""
3480 raise Exception("\nERROR: Invalid Tree Item. ")
3482 if not self
.HasFlag(TR_HIDE_ROOT
) or item
!= self
.GetRootItem():
3484 if not self
.IsExpanded(item
):
3487 child
, cookie
= self
.GetFirstChild(item
)
3490 self
.ExpandAllChildren(child
)
3491 child
, cookie
= self
.GetNextChild(item
, cookie
)
3494 def ExpandAll(self
):
3495 """Expands all CustomTreeCtrl items."""
3498 self
.ExpandAllChildren(self
._anchor
)
3501 def Collapse(self
, item
):
3503 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3504 EVT_TREE_ITEM_COLLAPSED events.
3508 raise Exception("\nERROR: Invalid Tree Item. ")
3510 if self
.HasFlag(TR_HIDE_ROOT
) and item
== self
.GetRootItem():
3511 raise Exception("\nERROR: Can't Collapse An Hidden Root. ")
3513 if not item
.IsExpanded():
3516 event
= TreeEvent(wxEVT_TREE_ITEM_COLLAPSING
, self
.GetId())
3518 event
.SetEventObject(self
)
3519 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3520 # cancelled by program
3523 self
.ChildrenClosing(item
)
3526 self
.CalculatePositions()
3527 self
.RefreshSubtree(item
)
3529 if self
._hasWindows
:
3532 event
.SetEventType(wxEVT_TREE_ITEM_COLLAPSED
)
3533 self
.GetEventHandler().ProcessEvent(event
)
3536 def CollapseAndReset(self
, item
):
3537 """Collapse the given item and deletes its children."""
3540 self
.DeleteChildren(item
)
3543 def Toggle(self
, item
):
3544 """Toggles the item state (collapsed/expanded)."""
3546 if item
.IsExpanded():
3552 def HideWindows(self
):
3553 """Hides the windows associated to the items. Used internally."""
3555 for child
in self
._itemWithWindow
:
3556 if not self
.IsVisible(child
):
3557 wnd
= child
.GetWindow()
3563 """Unselects the current selection."""
3567 self
._current
.SetHilight(False)
3568 self
.RefreshLine(self
._current
)
3570 self
._current
= None
3571 self
._select
_me
= None
3574 def UnselectAllChildren(self
, item
):
3575 """Unselects all the children of the given item."""
3577 if item
.IsSelected():
3579 item
.SetHilight(False)
3580 self
.RefreshLine(item
)
3582 if item
.HasChildren():
3583 for child
in item
.GetChildren():
3584 self
.UnselectAllChildren(child
)
3587 def UnselectAll(self
):
3588 """Unselect all the items."""
3590 rootItem
= self
.GetRootItem()
3592 # the tree might not have the root item at all
3594 self
.UnselectAllChildren(rootItem
)
3598 # Recursive function !
3599 # To stop we must have crt_item<last_item
3601 # Tag all next children, when no more children,
3602 # Move to parent (not to tag)
3603 # Keep going... if we found last_item, we stop.
3605 def TagNextChildren(self
, crt_item
, last_item
, select
):
3606 """Used internally."""
3608 parent
= crt_item
.GetParent()
3610 if parent
== None: # This is root item
3611 return self
.TagAllChildrenUntilLast(crt_item
, last_item
, select
)
3613 children
= parent
.GetChildren()
3614 index
= children
.index(crt_item
)
3616 count
= len(children
)
3618 for n
in xrange(index
+1, count
):
3619 if self
.TagAllChildrenUntilLast(children
[n
], last_item
, select
):
3622 return self
.TagNextChildren(parent
, last_item
, select
)
3625 def TagAllChildrenUntilLast(self
, crt_item
, last_item
, select
):
3626 """Used internally."""
3628 crt_item
.SetHilight(select
)
3629 self
.RefreshLine(crt_item
)
3631 if crt_item
== last_item
:
3634 if crt_item
.HasChildren():
3635 for child
in crt_item
.GetChildren():
3636 if self
.TagAllChildrenUntilLast(child
, last_item
, select
):
3642 def SelectItemRange(self
, item1
, item2
):
3643 """Selects all the items between item1 and item2."""
3645 self
._select
_me
= None
3647 # item2 is not necessary after item1
3648 # choice first' and 'last' between item1 and item2
3649 first
= (item1
.GetY() < item2
.GetY() and [item1
] or [item2
])[0]
3650 last
= (item1
.GetY() < item2
.GetY() and [item2
] or [item1
])[0]
3652 select
= self
._current
.IsSelected()
3654 if self
.TagAllChildrenUntilLast(first
, last
, select
):
3657 self
.TagNextChildren(first
, last
, select
)
3660 def DoSelectItem(self
, item
, unselect_others
=True, extended_select
=False):
3661 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3664 raise Exception("\nERROR: Invalid Tree Item. ")
3666 self
._select
_me
= None
3668 is_single
= not (self
.GetTreeStyle() & TR_MULTIPLE
)
3670 # to keep going anyhow !!!
3672 if item
.IsSelected():
3673 return # nothing to do
3674 unselect_others
= True
3675 extended_select
= False
3677 elif unselect_others
and item
.IsSelected():
3679 # selection change if there is more than one item currently selected
3680 if len(self
.GetSelections()) == 1:
3683 event
= TreeEvent(wxEVT_TREE_SEL_CHANGING
, self
.GetId())
3685 event
._itemOld
= self
._current
3686 event
.SetEventObject(self
)
3687 # TODO : Here we don't send any selection mode yet !
3689 if self
.GetEventHandler().ProcessEvent(event
) and not event
.IsAllowed():
3692 parent
= self
.GetItemParent(item
)
3694 if not self
.IsExpanded(parent
):
3697 parent
= self
.GetItemParent(parent
)
3702 self
.Unselect() # to speed up thing
3708 if not self
._current
:
3709 self
._current
= self
._key
_current
= self
.GetRootItem()
3711 # don't change the mark (self._current)
3712 self
.SelectItemRange(self
._current
, item
)
3716 select
= True # the default
3718 # Check if we need to toggle hilight (ctrl mode)
3719 if not unselect_others
:
3720 select
= not item
.IsSelected()
3722 self
._current
= self
._key
_current
= item
3723 self
._current
.SetHilight(select
)
3724 self
.RefreshLine(self
._current
)
3726 # This can cause idle processing to select the root
3727 # if no item is selected, so it must be after the
3729 self
.EnsureVisible(item
)
3731 event
.SetEventType(wxEVT_TREE_SEL_CHANGED
)
3732 self
.GetEventHandler().ProcessEvent(event
)
3734 # Handles hypertext items
3735 if self
.IsItemHyperText(item
):
3736 event
= TreeEvent(wxEVT_TREE_ITEM_HYPERLINK
, self
.GetId())
3738 self
.GetEventHandler().ProcessEvent(event
)
3741 def SelectItem(self
, item
, select
=True):
3742 """Selects/deselects an item."""
3745 raise Exception("\nERROR: Invalid Tree Item. ")
3749 self
.DoSelectItem(item
, not self
.HasFlag(TR_MULTIPLE
))
3753 item
.SetHilight(False)
3754 self
.RefreshLine(item
)
3757 def FillArray(self
, item
, array
=[]):
3759 Internal function. Used to populate an array of selected items when
3760 the style TR_MULTIPLE is used.
3766 if item
.IsSelected():
3769 if item
.HasChildren() and item
.IsExpanded():
3770 for child
in item
.GetChildren():
3771 array
= self
.FillArray(child
, array
)
3776 def GetSelections(self
):
3778 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3779 the TR_MULTIPLE style set.
3783 idRoot
= self
.GetRootItem()
3785 array
= self
.FillArray(idRoot
, array
)
3787 #else: the tree is empty, so no selections
3792 def EnsureVisible(self
, item
):
3793 """Ensure that an item is visible in CustomTreeCtrl."""
3796 raise Exception("\nERROR: Invalid Tree Item. ")
3798 # first expand all parent branches
3799 parent
= item
.GetParent()
3801 if self
.HasFlag(TR_HIDE_ROOT
):
3802 while parent
and parent
!= self
._anchor
:
3804 parent
= parent
.GetParent()
3808 parent
= parent
.GetParent()
3813 def ScrollTo(self
, item
):
3814 """Scrolls the specified item into view."""
3819 # We have to call this here because the label in
3820 # question might just have been added and no screen
3821 # update taken place.
3823 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
3828 # now scroll to the item
3829 item_y
= item
.GetY()
3830 start_x
, start_y
= self
.GetViewStart()
3831 start_y
*= _PIXELS_PER_UNIT
3833 client_w
, client_h
= self
.GetClientSize()
3837 if item_y
< start_y
+3:
3840 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3841 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3842 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3843 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3844 # Item should appear at top
3845 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, item_y
/_PIXELS_PER_UNIT
)
3847 elif item_y
+self
.GetLineHeight(item
) > start_y
+client_h
:
3850 x
, y
= self
._anchor
.GetSize(x
, y
, self
)
3851 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3852 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
3853 item_y
+= _PIXELS_PER_UNIT
+2
3854 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
3855 # Item should appear at bottom
3856 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
)
3859 def OnCompareItems(self
, item1
, item2
):
3861 Returns whether 2 items have the same text.
3862 Override this function in the derived class to change the sort order of the items
3863 in the CustomTreeCtrl. The function should return a negative, zero or positive
3864 value if the first item is less than, equal to or greater than the second one.
3866 The base class version compares items alphabetically.
3869 return self
.GetItemText(item1
) == self
.GetItemText(item2
)
3872 def SortChildren(self
, item
):
3874 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3875 You should override that method to change the sort order (the default is ascending
3876 case-sensitive alphabetical order).
3880 raise Exception("\nERROR: Invalid Tree Item. ")
3882 children
= item
.GetChildren()
3884 if len(children
) > 1:
3886 children
.sort(self
.OnCompareItems
)
3889 def GetImageList(self
):
3890 """Returns the normal image list."""
3892 return self
._imageListNormal
3895 def GetButtonsImageList(self
):
3896 """Returns the buttons image list (from which application-defined button images are taken)."""
3898 return self
._imageListButtons
3901 def GetStateImageList(self
):
3902 """Returns the state image list (from which application-defined state images are taken)."""
3904 return self
._imageListState
3907 def GetImageListCheck(self
):
3908 """Returns the image list used to build the check/radio buttons."""
3910 return self
._imageListCheck
3913 def CalculateLineHeight(self
):
3914 """Calculates the height of a line."""
3916 dc
= wx
.ClientDC(self
)
3917 self
._lineHeight
= dc
.GetCharHeight()
3919 if self
._imageListNormal
:
3921 # Calculate a self._lineHeight value from the normal Image sizes.
3922 # May be toggle off. Then CustomTreeCtrl will spread when
3923 # necessary (which might look ugly).
3924 n
= self
._imageListNormal
.GetImageCount()
3928 width
, height
= self
._imageListNormal
.GetSize(i
)
3930 if height
> self
._lineHeight
:
3931 self
._lineHeight
= height
3933 if self
._imageListButtons
:
3935 # Calculate a self._lineHeight value from the Button image sizes.
3936 # May be toggle off. Then CustomTreeCtrl will spread when
3937 # necessary (which might look ugly).
3938 n
= self
._imageListButtons
.GetImageCount()
3942 width
, height
= self
._imageListButtons
.GetSize(i
)
3944 if height
> self
._lineHeight
:
3945 self
._lineHeight
= height
3947 if self
._imageListCheck
:
3949 # Calculate a self._lineHeight value from the check/radio image sizes.
3950 # May be toggle off. Then CustomTreeCtrl will spread when
3951 # necessary (which might look ugly).
3952 n
= self
._imageListCheck
.GetImageCount()
3956 width
, height
= self
._imageListCheck
.GetSize(i
)
3958 if height
> self
._lineHeight
:
3959 self
._lineHeight
= height
3961 if self
._lineHeight
< 30:
3962 self
._lineHeight
+= 2 # at least 2 pixels
3964 self
._lineHeight
+= self
._lineHeight
/10 # otherwise 10% extra spacing
3967 def SetImageList(self
, imageList
):
3968 """Sets the normal image list."""
3970 if self
._ownsImageListNormal
:
3971 del self
._imageListNormal
3973 self
._imageListNormal
= imageList
3974 self
._ownsImageListNormal
= False
3977 # Don't do any drawing if we're setting the list to NULL,
3978 # since we may be in the process of deleting the tree control.
3980 self
.CalculateLineHeight()
3982 # We gray out the image list to use the grayed icons with disabled items
3983 sz
= imageList
.GetSize(0)
3984 self
._grayedImageList
= wx
.ImageList(sz
[0], sz
[1], True, 0)
3986 for ii
in xrange(imageList
.GetImageCount()):
3987 bmp
= imageList
.GetBitmap(ii
)
3988 image
= wx
.ImageFromBitmap(bmp
)
3989 image
= GrayOut(image
)
3990 newbmp
= wx
.BitmapFromImage(image
)
3991 self
._grayedImageList
.Add(newbmp
)
3994 def SetStateImageList(self
, imageList
):
3995 """Sets the state image list (from which application-defined state images are taken)."""
3997 if self
._ownsImageListState
:
3998 del self
._imageListState
4000 self
._imageListState
= imageList
4001 self
._ownsImageListState
= False
4004 def SetButtonsImageList(self
, imageList
):
4005 """Sets the buttons image list (from which application-defined button images are taken)."""
4007 if self
._ownsImageListButtons
:
4008 del self
._imageListButtons
4010 self
._imageListButtons
= imageList
4011 self
._ownsImageListButtons
= False
4013 self
.CalculateLineHeight()
4016 def SetImageListCheck(self
, sizex
, sizey
, imglist
=None):
4017 """Sets the check image list."""
4021 self
._imageListCheck
= wx
.ImageList(sizex
, sizey
)
4022 self
._imageListCheck
.Add(GetCheckedBitmap())
4023 self
._imageListCheck
.Add(GetNotCheckedBitmap())
4024 self
._imageListCheck
.Add(GetFlaggedBitmap())
4025 self
._imageListCheck
.Add(GetNotFlaggedBitmap())
4029 sizex
, sizey
= imglist
.GetSize(0)
4030 self
._imageListCheck
= imglist
4032 # We gray out the image list to use the grayed icons with disabled items
4033 self
._grayedCheckList
= wx
.ImageList(sizex
, sizey
, True, 0)
4035 for ii
in xrange(self
._imageListCheck
.GetImageCount()):
4037 bmp
= self
._imageListCheck
.GetBitmap(ii
)
4038 image
= wx
.ImageFromBitmap(bmp
)
4039 image
= GrayOut(image
)
4040 newbmp
= wx
.BitmapFromImage(image
)
4041 self
._grayedCheckList
.Add(newbmp
)
4046 self
.CalculateLineHeight()
4049 def AssignImageList(self
, imageList
):
4050 """Assigns the normal image list."""
4052 self
.SetImageList(imageList
)
4053 self
._ownsImageListNormal
= True
4056 def AssignStateImageList(self
, imageList
):
4057 """Assigns the state image list."""
4059 self
.SetStateImageList(imageList
)
4060 self
._ownsImageListState
= True
4063 def AssignButtonsImageList(self
, imageList
):
4064 """Assigns the button image list."""
4066 self
.SetButtonsImageList(imageList
)
4067 self
._ownsImageListButtons
= True
4070 # -----------------------------------------------------------------------------
4072 # -----------------------------------------------------------------------------
4074 def AdjustMyScrollbars(self
):
4075 """Adjust the wx.ScrolledWindow scrollbars."""
4079 x
, y
= self
._anchor
.GetSize(0, 0, self
)
4080 y
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4081 x
+= _PIXELS_PER_UNIT
+ 2 # one more scrollbar unit + 2 pixels
4082 x_pos
= self
.GetScrollPos(wx
.HORIZONTAL
)
4083 y_pos
= self
.GetScrollPos(wx
.VERTICAL
)
4084 self
.SetScrollbars(_PIXELS_PER_UNIT
, _PIXELS_PER_UNIT
, x
/_PIXELS_PER_UNIT
, y
/_PIXELS_PER_UNIT
, x_pos
, y_pos
)
4088 self
.SetScrollbars(0, 0, 0, 0)
4091 def GetLineHeight(self
, item
):
4092 """Returns the line height for the given item."""
4094 if self
.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT
:
4095 return item
.GetHeight()
4097 return self
._lineHeight
4100 def DrawVerticalGradient(self
, dc
, rect
, hasfocus
):
4101 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4103 oldpen
= dc
.GetPen()
4104 oldbrush
= dc
.GetBrush()
4105 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4107 # calculate gradient coefficients
4109 col2
= self
._secondcolour
4110 col1
= self
._firstcolour
4112 col2
= self
._hilightUnfocusedBrush
.GetColour()
4113 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4115 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4116 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4118 flrect
= float(rect
.height
)
4120 rstep
= float((r2
- r1
)) / flrect
4121 gstep
= float((g2
- g1
)) / flrect
4122 bstep
= float((b2
- b1
)) / flrect
4124 rf
, gf
, bf
= 0, 0, 0
4126 for y
in xrange(rect
.y
, rect
.y
+ rect
.height
):
4127 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4128 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4129 dc
.DrawRectangle(rect
.x
, y
, rect
.width
, 1)
4135 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4136 dc
.DrawRectangleRect(rect
)
4137 dc
.SetBrush(oldbrush
)
4140 def DrawHorizontalGradient(self
, dc
, rect
, hasfocus
):
4141 """Gradient fill from colour 1 to colour 2 from left to right."""
4143 oldpen
= dc
.GetPen()
4144 oldbrush
= dc
.GetBrush()
4145 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4147 # calculate gradient coefficients
4150 col2
= self
._secondcolour
4151 col1
= self
._firstcolour
4153 col2
= self
._hilightUnfocusedBrush
.GetColour()
4154 col1
= self
._hilightUnfocusedBrush
2.GetColour()
4156 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
4157 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
4159 flrect
= float(rect
.width
)
4161 rstep
= float((r2
- r1
)) / flrect
4162 gstep
= float((g2
- g1
)) / flrect
4163 bstep
= float((b2
- b1
)) / flrect
4165 rf
, gf
, bf
= 0, 0, 0
4167 for x
in xrange(rect
.x
, rect
.x
+ rect
.width
):
4168 currCol
= (int(r1
+ rf
), int(g1
+ gf
), int(b1
+ bf
))
4169 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4170 dc
.DrawRectangle(x
, rect
.y
, 1, rect
.height
)
4176 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4177 dc
.DrawRectangleRect(rect
)
4178 dc
.SetBrush(oldbrush
)
4181 def DrawVistaRectangle(self
, dc
, rect
, hasfocus
):
4182 """Draw the selected item(s) with the Windows Vista style."""
4186 outer
= _rgbSelectOuter
4187 inner
= _rgbSelectInner
4189 bottom
= _rgbSelectBottom
4193 outer
= _rgbNoFocusOuter
4194 inner
= _rgbNoFocusInner
4195 top
= _rgbNoFocusTop
4196 bottom
= _rgbNoFocusBottom
4198 oldpen
= dc
.GetPen()
4199 oldbrush
= dc
.GetBrush()
4201 bdrRect
= wx
.Rect(*rect
.Get())
4202 filRect
= wx
.Rect(*rect
.Get())
4203 filRect
.Deflate(1,1)
4205 r1
, g1
, b1
= int(top
.Red()), int(top
.Green()), int(top
.Blue())
4206 r2
, g2
, b2
= int(bottom
.Red()), int(bottom
.Green()), int(bottom
.Blue())
4208 flrect
= float(filRect
.height
)
4210 flrect
= self
._lineHeight
4212 rstep
= float((r2
- r1
)) / flrect
4213 gstep
= float((g2
- g1
)) / flrect
4214 bstep
= float((b2
- b1
)) / flrect
4216 rf
, gf
, bf
= 0, 0, 0
4217 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4219 for y
in xrange(filRect
.y
, filRect
.y
+ filRect
.height
):
4220 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
4221 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
4222 dc
.DrawRectangle(filRect
.x
, y
, filRect
.width
, 1)
4227 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4228 dc
.SetPen(wx
.Pen(outer
))
4229 dc
.DrawRoundedRectangleRect(bdrRect
, 3)
4230 bdrRect
.Deflate(1, 1)
4231 dc
.SetPen(wx
.Pen(inner
))
4232 dc
.DrawRoundedRectangleRect(bdrRect
, 2)
4235 dc
.SetBrush(oldbrush
)
4238 def PaintItem(self
, item
, dc
):
4239 """Actually paint an item."""
4241 attr
= item
.GetAttributes()
4243 if attr
and attr
.HasFont():
4244 dc
.SetFont(attr
.GetFont())
4246 dc
.SetFont(self
._boldFont
)
4247 if item
.IsHyperText():
4248 dc
.SetFont(self
.GetHyperTextFont())
4249 if item
.GetVisited():
4250 dc
.SetTextForeground(self
.GetHyperTextVisitedColour())
4252 dc
.SetTextForeground(self
.GetHyperTextNewColour())
4254 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
4256 image
= item
.GetCurrentImage()
4257 checkimage
= item
.GetCurrentCheckedImage()
4258 image_w
, image_h
= 0, 0
4260 if image
!= _NO_IMAGE
:
4262 if self
._imageListNormal
:
4264 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
4271 if item
.GetType() != 0:
4272 wcheck
, hcheck
= self
._imageListCheck
.GetSize(item
.GetType())
4275 wcheck
, hcheck
= 0, 0
4277 total_h
= self
.GetLineHeight(item
)
4278 drawItemBackground
= False
4280 if item
.IsSelected():
4282 # under mac selections are only a rectangle in case they don't have the focus
4283 if wx
.Platform
== "__WXMAC__":
4284 if not self
._hasFocus
:
4285 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
4286 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHT
), 1, wx
.SOLID
))
4288 dc
.SetBrush(self
._hilightBrush
)
4290 dc
.SetBrush((self
._hasFocus
and [self
._hilightBrush
] or [self
._hilightUnfocusedBrush
])[0])
4291 drawItemBackground
= True
4293 if attr
and attr
.HasBackgroundColour():
4294 drawItemBackground
= True
4295 colBg
= attr
.GetBackgroundColour()
4297 colBg
= self
._backgroundColour
4299 dc
.SetBrush(wx
.Brush(colBg
, wx
.SOLID
))
4300 dc
.SetPen(wx
.TRANSPARENT_PEN
)
4302 offset
= (self
.HasFlag(TR_ROW_LINES
) and [1] or [0])[0]
4304 if self
.HasFlag(TR_FULL_ROW_HIGHLIGHT
):
4306 w
, h
= self
.GetClientSize()
4308 itemrect
= wx
.Rect(x
, item
.GetY()+offset
, w
, total_h
-offset
)
4310 if item
.IsSelected():
4311 if self
._usegradients
:
4312 if self
._gradientstyle
== 0: # Horizontal
4313 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4315 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4316 elif self
._vistaselection
:
4317 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4319 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4320 flags
= wx
.CONTROL_SELECTED
4321 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4322 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4324 dc
.DrawRectangleRect(itemrect
)
4328 if item
.IsSelected():
4330 # If it's selected, and there's an image, then we should
4331 # take care to leave the area under the image painted in the
4332 # background colour.
4334 wnd
= item
.GetWindow()
4337 wndx
, wndy
= item
.GetWindowSize()
4339 itemrect
= wx
.Rect(item
.GetX() + wcheck
+ image_w
- 2,
4341 item
.GetWidth() - image_w
- wcheck
+ 2 - wndx
,
4344 if self
._usegradients
:
4345 if self
._gradientstyle
== 0: # Horizontal
4346 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4348 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4349 elif self
._vistaselection
:
4350 self
.DrawVistaRectangle(dc
, itemrect
, self
._hasFocus
)
4352 if wx
.Platform
in ["__WXGTK2__", "__WXMAC__"]:
4353 flags
= wx
.CONTROL_SELECTED
4354 if self
._hasFocus
: flags
= flags | wx
.CONTROL_FOCUSED
4355 wx
.RendererNative
.Get().DrawItemSelectionRect(self
, dc
, itemrect
, flags
)
4357 dc
.DrawRectangleRect(itemrect
)
4359 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4360 # don't allow backgrounds to be customized. Not drawing the background,
4361 # except for custom item backgrounds, works for both kinds of theme.
4362 elif drawItemBackground
:
4364 minusicon
= wcheck
+ image_w
- 2
4365 itemrect
= wx
.Rect(item
.GetX()+minusicon
,
4367 item
.GetWidth()-minusicon
,
4370 if self
._usegradients
and self
._hasFocus
:
4371 if self
._gradientstyle
== 0: # Horizontal
4372 self
.DrawHorizontalGradient(dc
, itemrect
, self
._hasFocus
)
4374 self
.DrawVerticalGradient(dc
, itemrect
, self
._hasFocus
)
4376 dc
.DrawRectangleRect(itemrect
)
4378 if image
!= _NO_IMAGE
:
4380 dc
.SetClippingRegion(item
.GetX(), item
.GetY(), wcheck
+image_w
-2, total_h
)
4381 if item
.IsEnabled():
4382 imglist
= self
._imageListNormal
4384 imglist
= self
._grayedImageList
4386 imglist
.Draw(image
, dc
,
4387 item
.GetX() + wcheck
,
4388 item
.GetY() + ((total_h
> image_h
) and [(total_h
-image_h
)/2] or [0])[0],
4389 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4391 dc
.DestroyClippingRegion()
4394 if item
.IsEnabled():
4395 imglist
= self
._imageListCheck
4397 imglist
= self
._grayedCheckList
4399 imglist
.Draw(checkimage
, dc
,
4401 item
.GetY() + ((total_h
> hcheck
) and [(total_h
-hcheck
)/2] or [0])[0],
4402 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4404 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
4405 extraH
= ((total_h
> text_h
) and [(total_h
- text_h
)/2] or [0])[0]
4407 textrect
= wx
.Rect(wcheck
+ image_w
+ item
.GetX(), item
.GetY() + extraH
, text_w
, text_h
)
4409 if not item
.IsEnabled():
4410 foreground
= dc
.GetTextForeground()
4411 dc
.SetTextForeground(self
._disabledColour
)
4412 dc
.DrawLabel(item
.GetText(), textrect
)
4413 dc
.SetTextForeground(foreground
)
4415 if wx
.Platform
== "__WXMAC__" and item
.IsSelected() and self
._hasFocus
:
4416 dc
.SetTextForeground(wx
.WHITE
)
4417 dc
.DrawLabel(item
.GetText(), textrect
)
4419 wnd
= item
.GetWindow()
4421 wndx
= wcheck
+ image_w
+ item
.GetX() + text_w
+ 4
4422 xa
, ya
= self
.CalcScrolledPosition((0, item
.GetY()))
4424 if item
.GetHeight() > item
.GetWindowSize()[1]:
4425 ya
+= (item
.GetHeight() - item
.GetWindowSize()[1])/2
4427 if not wnd
.IsShown():
4429 if wnd
.GetPosition() != (wndx
, ya
):
4430 wnd
.SetPosition((wndx
, ya
))
4432 # restore normal font
4433 dc
.SetFont(self
._normalFont
)
4436 # Now y stands for the top of the item, whereas it used to stand for middle !
4437 def PaintLevel(self
, item
, dc
, level
, y
):
4438 """Paint a level of CustomTreeCtrl."""
4440 x
= level
*self
._indent
4442 if not self
.HasFlag(TR_HIDE_ROOT
):
4448 # always expand hidden root
4450 children
= item
.GetChildren()
4451 count
= len(children
)
4457 y
= self
.PaintLevel(children
[n
], dc
, 1, y
)
4460 if not self
.HasFlag(TR_NO_LINES
) and self
.HasFlag(TR_LINES_AT_ROOT
) and count
> 0:
4462 # draw line down to last child
4463 origY
+= self
.GetLineHeight(children
[0])>>1
4464 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4465 oldPen
= dc
.GetPen()
4466 dc
.SetPen(self
._dottedPen
)
4467 dc
.DrawLine(3, origY
, 3, oldY
)
4472 item
.SetX(x
+self
._spacing
)
4475 h
= self
.GetLineHeight(item
)
4477 y_mid
= y_top
+ (h
>>1)
4480 exposed_x
= dc
.LogicalToDeviceX(0)
4481 exposed_y
= dc
.LogicalToDeviceY(y_top
)
4483 if self
.IsExposed(exposed_x
, exposed_y
, 10000, h
): # 10000 = very much
4484 if wx
.Platform
== "__WXMAC__":
4485 # don't draw rect outline if we already have the
4486 # background color under Mac
4487 pen
= ((item
.IsSelected() and self
._hasFocus
) and [self
._borderPen
] or [wx
.TRANSPARENT_PEN
])[0]
4489 pen
= self
._borderPen
4491 if item
.IsSelected():
4492 if (wx
.Platform
== "__WXMAC__" and self
._hasFocus
):
4493 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4495 colText
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_HIGHLIGHTTEXT
)
4497 attr
= item
.GetAttributes()
4498 if attr
and attr
.HasTextColour():
4499 colText
= attr
.GetTextColour()
4501 colText
= self
.GetForegroundColour()
4503 if self
._vistaselection
:
4507 dc
.SetTextForeground(colText
)
4512 self
.PaintItem(item
, dc
)
4514 if self
.HasFlag(TR_ROW_LINES
):
4516 # if the background colour is white, choose a
4517 # contrasting color for the lines
4518 medium_grey
= wx
.Pen(wx
.Colour(200, 200, 200))
4519 dc
.SetPen(((self
.GetBackgroundColour() == wx
.WHITE
) and [medium_grey
] or [wx
.WHITE_PEN
])[0])
4520 dc
.DrawLine(0, y_top
, 10000, y_top
)
4521 dc
.DrawLine(0, y
, 10000, y
)
4523 # restore DC objects
4524 dc
.SetBrush(wx
.WHITE_BRUSH
)
4525 dc
.SetTextForeground(wx
.BLACK
)
4527 if not self
.HasFlag(TR_NO_LINES
):
4529 # draw the horizontal line here
4530 dc
.SetPen(self
._dottedPen
)
4532 if x
> self
._indent
:
4533 x_start
-= self
._indent
4534 elif self
.HasFlag(TR_LINES_AT_ROOT
):
4536 dc
.DrawLine(x_start
, y_mid
, x
+ self
._spacing
, y_mid
)
4539 # should the item show a button?
4540 if item
.HasPlus() and self
.HasButtons():
4542 if self
._imageListButtons
:
4544 # draw the image button here
4547 image
= (item
.IsExpanded() and [TreeItemIcon_Expanded
] or [TreeItemIcon_Normal
])[0]
4548 if item
.IsSelected():
4549 image
+= TreeItemIcon_Selected
- TreeItemIcon_Normal
4551 image_w
, image_h
= self
._imageListButtons
.GetSize(image
)
4553 yy
= y_mid
- image_h
/2
4555 dc
.SetClippingRegion(xx
, yy
, image_w
, image_h
)
4556 self
._imageListButtons
.Draw(image
, dc
, xx
, yy
,
4557 wx
.IMAGELIST_DRAW_TRANSPARENT
)
4558 dc
.DestroyClippingRegion()
4560 else: # no custom buttons
4562 if self
.HasFlag(TR_TWIST_BUTTONS
):
4563 # We draw something like the Mac twist buttons
4565 dc
.SetPen(wx
.BLACK_PEN
)
4566 dc
.SetBrush(self
._hilightBrush
)
4567 button
= [wx
.Point(), wx
.Point(), wx
.Point()]
4569 if item
.IsExpanded():
4571 button
[0].y
= y_mid
- 3
4573 button
[1].y
= button
[0].y
4575 button
[2].y
= button
[0].y
+ 6
4578 button
[0].y
= y_mid
- 5
4579 button
[1].x
= button
[0].x
4580 button
[1].y
= y_mid
+ 5
4581 button
[2].x
= button
[0].x
+ 5
4584 dc
.DrawPolygon(button
)
4587 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4594 if item
.IsExpanded():
4595 flag |
= _CONTROL_EXPANDED
4596 if item
== self
._underMouse
:
4597 flag |
= _CONTROL_CURRENT
4599 self
._drawingfunction
(self
, dc
, wx
.Rect(x
- wImage
/2, y_mid
- hImage
/2,wImage
, hImage
), flag
)
4601 if item
.IsExpanded():
4603 children
= item
.GetChildren()
4604 count
= len(children
)
4613 y
= self
.PaintLevel(children
[n
], dc
, level
, y
)
4616 if not self
.HasFlag(TR_NO_LINES
) and count
> 0:
4618 # draw line down to last child
4619 oldY
+= self
.GetLineHeight(children
[n
-1])>>1
4620 if self
.HasButtons():
4623 # Only draw the portion of the line that is visible, in case it is huge
4624 xOrigin
, yOrigin
= dc
.GetDeviceOrigin()
4625 yOrigin
= abs(yOrigin
)
4626 width
, height
= self
.GetClientSize()
4628 # Move end points to the begining/end of the view?
4631 if oldY
> yOrigin
+ height
:
4632 oldY
= yOrigin
+ height
4634 # after the adjustments if y_mid is larger than oldY then the line
4635 # isn't visible at all so don't draw anything
4637 dc
.SetPen(self
._dottedPen
)
4638 dc
.DrawLine(x
, y_mid
, x
, oldY
)
4643 # -----------------------------------------------------------------------------
4644 # wxWidgets callbacks
4645 # -----------------------------------------------------------------------------
4647 def OnPaint(self
, event
):
4648 """Handles the wx.EVT_PAINT event."""
4650 dc
= wx
.PaintDC(self
)
4653 if not self
._anchor
:
4656 dc
.SetFont(self
._normalFont
)
4657 dc
.SetPen(self
._dottedPen
)
4660 self
.PaintLevel(self
._anchor
, dc
, 0, y
)
4663 def OnEraseBackground(self
, event
):
4664 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4666 # Can we actually do something here (or in OnPaint()) To Handle
4667 # background images that are stretchable or always centered?
4668 # I tried but I get enormous flickering...
4670 if not self
._backgroundImage
:
4674 if self
._imageStretchStyle
== _StyleTile
:
4678 dc
= wx
.ClientDC(self
)
4679 rect
= self
.GetUpdateRegion().GetBox()
4680 dc
.SetClippingRect(rect
)
4682 self
.TileBackground(dc
)
4685 def TileBackground(self
, dc
):
4686 """Tiles the background image to fill all the available area."""
4688 sz
= self
.GetClientSize()
4689 w
= self
._backgroundImage
.GetWidth()
4690 h
= self
._backgroundImage
.GetHeight()
4697 while y
< sz
.height
:
4698 dc
.DrawBitmap(self
._backgroundImage
, x
, y
, True)
4704 def OnSetFocus(self
, event
):
4705 """Handles the wx.EVT_SET_FOCUS event."""
4707 self
._hasFocus
= True
4708 self
.RefreshSelected()
4712 def OnKillFocus(self
, event
):
4713 """Handles the wx.EVT_KILL_FOCUS event."""
4715 self
._hasFocus
= False
4716 self
.RefreshSelected()
4720 def OnKeyDown(self
, event
):
4721 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4723 te
= TreeEvent(wxEVT_TREE_KEY_DOWN
, self
.GetId())
4725 te
.SetEventObject(self
)
4727 if self
.GetEventHandler().ProcessEvent(te
):
4728 # intercepted by the user code
4731 if self
._current
is None or self
._key
_current
is None:
4736 # how should the selection work for this event?
4737 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(), event
.ShiftDown(), event
.CmdDown())
4741 # * : Expand all/Collapse all
4742 # ' ' | return : activate
4743 # up : go up (not last children!)
4745 # left : go to parent
4746 # right : open if parent and go next
4748 # end : go to last item without opening parents
4749 # alnum : start or continue searching for the item with this prefix
4751 keyCode
= event
.GetKeyCode()
4753 if keyCode
in [ord("+"), wx
.WXK_ADD
]: # "+"
4754 if self
._current
.HasPlus() and not self
.IsExpanded(self
._current
) and self
.IsItemEnabled(self
._current
):
4755 self
.Expand(self
._current
)
4757 elif keyCode
in [ord("*"), wx
.WXK_MULTIPLY
]: # "*"
4758 if not self
.IsExpanded(self
._current
) and self
.IsItemEnabled(self
._current
):
4760 self
.ExpandAll(self
._current
)
4762 elif keyCode
in [ord("-"), wx
.WXK_SUBTRACT
]: # "-"
4763 if self
.IsExpanded(self
._current
):
4764 self
.Collapse(self
._current
)
4766 elif keyCode
== wx
.WXK_MENU
:
4767 # Use the item's bounding rectangle to determine position for the event
4768 itemRect
= self
.GetBoundingRect(self
._current
, True)
4769 event
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
4770 event
._item
= self
._current
4771 # Use the left edge, vertical middle
4772 event
._pointDrag
= wx
.Point(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight()/2)
4773 event
.SetEventObject(self
)
4774 self
.GetEventHandler().ProcessEvent(event
)
4776 elif keyCode
in [wx
.WXK_RETURN
, wx
.WXK_SPACE
]:
4778 if not self
.IsItemEnabled(self
._current
):
4782 if not event
.HasModifiers():
4783 event
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
4784 event
._item
= self
._current
4785 event
.SetEventObject(self
)
4786 self
.GetEventHandler().ProcessEvent(event
)
4788 if keyCode
== wx
.WXK_SPACE
and self
.GetItemType(self
._current
) > 0:
4789 checked
= not self
.IsItemChecked(self
._current
)
4790 self
.CheckItem(self
._current
, checked
)
4792 # in any case, also generate the normal key event for this key,
4793 # even if we generated the ACTIVATED event above: this is what
4794 # wxMSW does and it makes sense because you might not want to
4795 # process ACTIVATED event at all and handle Space and Return
4796 # directly (and differently) which would be impossible otherwise
4799 # up goes to the previous sibling or to the last
4800 # of its children if it's expanded
4801 elif keyCode
== wx
.WXK_UP
:
4802 prev
= self
.GetPrevSibling(self
._key
_current
)
4804 prev
= self
.GetItemParent(self
._key
_current
)
4805 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4809 current
= self
._key
_current
4810 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4811 if current
== self
.GetFirstChild(prev
)[0] and self
.IsItemEnabled(prev
):
4812 # otherwise we return to where we came from
4813 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4814 self
._key
_current
= prev
4817 current
= self
._key
_current
4819 # We are going to another parent node
4820 while self
.IsExpanded(prev
) and self
.HasChildren(prev
):
4821 child
= self
.GetLastChild(prev
)
4826 # Try to get the previous siblings and see if they are active
4827 while prev
and not self
.IsItemEnabled(prev
):
4828 prev
= self
.GetPrevSibling(prev
)
4831 # No previous siblings active: go to the parent and up
4832 prev
= self
.GetItemParent(current
)
4833 while prev
and not self
.IsItemEnabled(prev
):
4834 prev
= self
.GetItemParent(prev
)
4837 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4838 self
._key
_current
= prev
4840 # left arrow goes to the parent
4841 elif keyCode
== wx
.WXK_LEFT
:
4843 prev
= self
.GetItemParent(self
._current
)
4844 if prev
== self
.GetRootItem() and self
.HasFlag(TR_HIDE_ROOT
):
4845 # don't go to root if it is hidden
4846 prev
= self
.GetPrevSibling(self
._current
)
4848 if self
.IsExpanded(self
._current
):
4849 self
.Collapse(self
._current
)
4851 if prev
and self
.IsItemEnabled(prev
):
4852 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4854 elif keyCode
== wx
.WXK_RIGHT
:
4855 # this works the same as the down arrow except that we
4856 # also expand the item if it wasn't expanded yet
4857 if self
.IsExpanded(self
._current
) and self
.HasChildren(self
._current
):
4858 child
, cookie
= self
.GetFirstChild(self
._key
_current
)
4859 if self
.IsItemEnabled(child
):
4860 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4861 self
._key
_current
= child
4863 self
.Expand(self
._current
)
4866 elif keyCode
== wx
.WXK_DOWN
:
4867 if self
.IsExpanded(self
._key
_current
) and self
.HasChildren(self
._key
_current
):
4869 child
= self
.GetNextActiveItem(self
._key
_current
)
4872 self
.DoSelectItem(child
, unselect_others
, extended_select
)
4873 self
._key
_current
= child
4877 next
= self
.GetNextSibling(self
._key
_current
)
4880 current
= self
._key
_current
4881 while current
and not next
:
4882 current
= self
.GetItemParent(current
)
4884 next
= self
.GetNextSibling(current
)
4885 if not next
or not self
.IsItemEnabled(next
):
4889 while next
and not self
.IsItemEnabled(next
):
4890 next
= self
.GetNext(next
)
4893 self
.DoSelectItem(next
, unselect_others
, extended_select
)
4894 self
._key
_current
= next
4897 # <End> selects the last visible tree item
4898 elif keyCode
== wx
.WXK_END
:
4900 last
= self
.GetRootItem()
4902 while last
and self
.IsExpanded(last
):
4904 lastChild
= self
.GetLastChild(last
)
4906 # it may happen if the item was expanded but then all of
4907 # its children have been deleted - so IsExpanded() returned
4908 # true, but GetLastChild() returned invalid item
4914 if last
and self
.IsItemEnabled(last
):
4916 self
.DoSelectItem(last
, unselect_others
, extended_select
)
4918 # <Home> selects the root item
4919 elif keyCode
== wx
.WXK_HOME
:
4921 prev
= self
.GetRootItem()
4926 if self
.HasFlag(TR_HIDE_ROOT
):
4927 prev
, cookie
= self
.GetFirstChild(prev
)
4931 if self
.IsItemEnabled(prev
):
4932 self
.DoSelectItem(prev
, unselect_others
, extended_select
)
4936 if not event
.HasModifiers() and ((keyCode
>= ord('0') and keyCode
<= ord('9')) or \
4937 (keyCode
>= ord('a') and keyCode
<= ord('z')) or \
4938 (keyCode
>= ord('A') and keyCode
<= ord('Z'))):
4940 # find the next item starting with the given prefix
4942 id = self
.FindItem(self
._current
, self
._findPrefix
+ ch
)
4948 if self
.IsItemEnabled(id):
4950 self
._findPrefix
+= ch
4952 # also start the timer to reset the current prefix if the user
4953 # doesn't press any more alnum keys soon -- we wouldn't want
4954 # to use this prefix for a new item search
4955 if not self
._findTimer
:
4956 self
._findTimer
= TreeFindTimer(self
)
4958 self
._findTimer
.Start(_DELAY
, wx
.TIMER_ONE_SHOT
)
4965 def GetNextActiveItem(self
, item
, down
=True):
4966 """Returns the next active item. Used Internally at present. """
4969 sibling
= self
.GetNextSibling
4971 sibling
= self
.GetPrevSibling
4973 if self
.GetItemType(item
) == 2 and not self
.IsItemChecked(item
):
4974 # Is an unchecked radiobutton... all its children are inactive
4975 # try to get the next/previous sibling
4979 child
= sibling(item
)
4980 if (child
and self
.IsItemEnabled(child
)) or not child
:
4985 # Tha's not a radiobutton... but some of its children can be
4987 child
, cookie
= self
.GetFirstChild(item
)
4988 while child
and not self
.IsItemEnabled(child
):
4989 child
, cookie
= self
.GetNextChild(item
, cookie
)
4991 if child
and self
.IsItemEnabled(child
):
4997 def HitTest(self
, point
, flags
=0):
4999 Calculates which (if any) item is under the given point, returning the tree item
5000 at this point plus extra information flags. Flags is a bitlist of the following:
5002 TREE_HITTEST_ABOVE above the client area
5003 TREE_HITTEST_BELOW below the client area
5004 TREE_HITTEST_NOWHERE no item has been hit
5005 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
5006 TREE_HITTEST_ONITEMICON on the icon associated to an item
5007 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
5008 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
5009 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
5010 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
5011 TREE_HITTEST_TOLEFT on the left of the client area
5012 TREE_HITTEST_TORIGHT on the right of the client area
5013 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
5014 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
5015 TREE_HITTEST_ONITEM anywhere on the item
5017 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
5020 w
, h
= self
.GetSize()
5024 flags |
= TREE_HITTEST_TOLEFT
5026 flags |
= TREE_HITTEST_TORIGHT
5028 flags |
= TREE_HITTEST_ABOVE
5030 flags |
= TREE_HITTEST_BELOW
5035 if self
._anchor
== None:
5036 flags
= TREE_HITTEST_NOWHERE
5039 hit
, flags
= self
._anchor
.HitTest(self
.CalcUnscrolledPosition(point
), self
, flags
, 0)
5042 flags
= TREE_HITTEST_NOWHERE
5045 if not self
.IsItemEnabled(hit
):
5051 def GetBoundingRect(self
, item
, textOnly
=False):
5052 """Gets the bounding rectangle of the item."""
5055 raise Exception("\nERROR: Invalid Tree Item. ")
5059 startX
, startY
= self
.GetViewStart()
5062 rect
.x
= i
.GetX() - startX
*_PIXELS_PER_UNIT
5063 rect
.y
= i
.GetY() - startY
*_PIXELS_PER_UNIT
5064 rect
.width
= i
.GetWidth()
5065 rect
.height
= self
.GetLineHeight(i
)
5070 def Edit(self
, item
):
5072 Internal function. Starts the editing of an item label, sending a
5073 EVT_TREE_BEGIN_LABEL_EDIT event.
5076 te
= TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT
, self
.GetId())
5078 te
.SetEventObject(self
)
5079 if self
.GetEventHandler().ProcessEvent(te
) and not te
.IsAllowed():
5083 # We have to call this here because the label in
5084 # question might just have been added and no screen
5085 # update taken place.
5087 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5092 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5093 self
._textCtrl
.StopEditing()
5095 self
._textCtrl
= TreeTextCtrl(self
, item
=item
)
5096 self
._textCtrl
.SetFocus()
5099 def GetEditControl(self
):
5101 Returns a pointer to the edit TextCtrl if the item is being edited or
5102 None otherwise (it is assumed that no more than one item may be edited
5106 return self
._textCtrl
5109 def OnRenameAccept(self
, item
, value
):
5111 Called by TreeTextCtrl, to accept the changes and to send the
5112 EVT_TREE_END_LABEL_EDIT event.
5115 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5117 le
.SetEventObject(self
)
5119 le
._editCancelled
= False
5121 return not self
.GetEventHandler().ProcessEvent(le
) or le
.IsAllowed()
5124 def OnRenameCancelled(self
, item
):
5126 Called by TreeTextCtrl, to cancel the changes and to send the
5127 EVT_TREE_END_LABEL_EDIT event.
5130 # let owner know that the edit was cancelled
5131 le
= TreeEvent(wxEVT_TREE_END_LABEL_EDIT
, self
.GetId())
5133 le
.SetEventObject(self
)
5135 le
._editCancelled
= True
5137 self
.GetEventHandler().ProcessEvent(le
)
5140 def OnRenameTimer(self
):
5141 """The timer for renaming has expired. Start editing."""
5143 self
.Edit(self
._current
)
5146 def OnMouse(self
, event
):
5147 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5149 if not self
._anchor
:
5152 pt
= self
.CalcUnscrolledPosition(event
.GetPosition())
5154 # Is the mouse over a tree item button?
5156 thisItem
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5157 underMouse
= thisItem
5158 underMouseChanged
= underMouse
!= self
._underMouse
5160 if underMouse
and (flags
& TREE_HITTEST_ONITEM
) and not event
.LeftIsDown() and \
5161 not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5162 underMouse
= underMouse
5166 if underMouse
!= self
._underMouse
:
5167 if self
._underMouse
:
5168 # unhighlight old item
5169 self
._underMouse
= None
5171 self
._underMouse
= underMouse
5173 # Determines what item we are hovering over and need a tooltip for
5174 hoverItem
= thisItem
5176 # We do not want a tooltip if we are dragging, or if the rename timer is running
5177 if underMouseChanged
and not self
._isDragging
and (not self
._renameTimer
or not self
._renameTimer
.IsRunning()):
5179 if hoverItem
is not None:
5180 # Ask the tree control what tooltip (if any) should be shown
5181 hevent
= TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP
, self
.GetId())
5182 hevent
._item
= hoverItem
5183 hevent
.SetEventObject(self
)
5185 if self
.GetEventHandler().ProcessEvent(hevent
) and hevent
.IsAllowed():
5186 self
.SetToolTip(hevent
._label
)
5188 if hoverItem
.IsHyperText() and (flags
& TREE_HITTEST_ONITEMLABEL
) and hoverItem
.IsEnabled():
5189 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
5190 self
._isonhyperlink
= True
5192 if self
._isonhyperlink
:
5193 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
5194 self
._isonhyperlink
= False
5196 # we process left mouse up event (enables in-place edit), right down
5197 # (pass to the user code), left dbl click (activate item) and
5198 # dragging/moving events for items drag-and-drop
5200 if not (event
.LeftDown() or event
.LeftUp() or event
.RightDown() or event
.LeftDClick() or \
5201 event
.Dragging() or ((event
.Moving() or event
.RightUp()) and self
._isDragging
)):
5207 item
, flags
= self
._anchor
.HitTest(pt
, self
, flags
, 0)
5209 if event
.Dragging() and not self
._isDragging
and ((flags
& TREE_HITTEST_ONITEMICON
) or (flags
& TREE_HITTEST_ONITEMLABEL
)):
5211 if self
._dragCount
== 0:
5212 self
._dragStart
= pt
5215 self
._dragCount
= self
._dragCount
+ 1
5217 if self
._dragCount
!= 3:
5218 # wait until user drags a bit further...
5221 command
= (event
.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG
] or [wxEVT_TREE_BEGIN_DRAG
])[0]
5223 nevent
= TreeEvent(command
, self
.GetId())
5224 nevent
._item
= self
._current
5225 nevent
.SetEventObject(self
)
5226 newpt
= self
.CalcScrolledPosition(pt
)
5227 nevent
.SetPoint(newpt
)
5229 # by default the dragging is not supported, the user code must
5230 # explicitly allow the event for it to take place
5233 if self
.GetEventHandler().ProcessEvent(nevent
) and nevent
.IsAllowed():
5235 # we're going to drag this item
5236 self
._isDragging
= True
5238 # remember the old cursor because we will change it while
5240 self
._oldCursor
= self
._cursor
5242 # in a single selection control, hide the selection temporarily
5243 if not (self
.GetTreeStyle() & TR_MULTIPLE
):
5244 self
._oldSelection
= self
.GetSelection()
5246 if self
._oldSelection
:
5248 self
._oldSelection
.SetHilight(False)
5249 self
.RefreshLine(self
._oldSelection
)
5251 selections
= self
.GetSelections()
5252 if len(selections
) == 1:
5253 self
._oldSelection
= selections
[0]
5254 self
._oldSelection
.SetHilight(False)
5255 self
.RefreshLine(self
._oldSelection
)
5260 # Create the custom draw image from the icons and the text of the item
5261 self
._dragImage
= DragImage(self
, self
._current
)
5262 self
._dragImage
.BeginDrag(wx
.Point(0,0), self
)
5263 self
._dragImage
.Show()
5264 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5266 elif event
.Dragging() and self
._isDragging
:
5268 self
._dragImage
.Move(self
.CalcScrolledPosition(pt
))
5270 if self
._countDrag
== 0 and item
:
5271 self
._oldItem
= item
5273 if item
!= self
._dropTarget
:
5275 # unhighlight the previous drop target
5276 if self
._dropTarget
:
5277 self
._dropTarget
.SetHilight(False)
5278 self
.RefreshLine(self
._dropTarget
)
5280 item
.SetHilight(True)
5281 self
.RefreshLine(item
)
5282 self
._countDrag
= self
._countDrag
+ 1
5283 self
._dropTarget
= item
5287 if self
._countDrag
>= 3:
5288 # Here I am trying to avoid ugly repainting problems... hope it works
5289 self
.RefreshLine(self
._oldItem
)
5292 elif (event
.LeftUp() or event
.RightUp()) and self
._isDragging
:
5295 self
._dragImage
.EndDrag()
5297 if self
._dropTarget
:
5298 self
._dropTarget
.SetHilight(False)
5300 if self
._oldSelection
:
5302 self
._oldSelection
.SetHilight(True)
5303 self
.RefreshLine(self
._oldSelection
)
5304 self
._oldSelection
= None
5306 # generate the drag end event
5307 event
= TreeEvent(wxEVT_TREE_END_DRAG
, self
.GetId())
5309 event
._pointDrag
= self
.CalcScrolledPosition(pt
)
5310 event
.SetEventObject(self
)
5312 self
.GetEventHandler().ProcessEvent(event
)
5314 self
._isDragging
= False
5315 self
._dropTarget
= None
5317 self
.SetCursor(self
._oldCursor
)
5319 if wx
.Platform
in ["__WXMSW__", "__WXMAC__"]:
5322 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5327 # If we got to this point, we are not dragging or moving the mouse.
5328 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5329 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5330 # We skip even if we didn't hit an item because we still should
5331 # restore focus to the tree control even if we didn't exactly hit an item.
5332 if event
.LeftDown():
5333 self
._hasFocus
= True
5334 self
.SetFocusIgnoringChildren()
5337 # here we process only the messages which happen on tree items
5342 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5343 self
._textCtrl
.StopEditing()
5344 return # we hit the blank area
5346 if event
.RightDown():
5348 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5349 self
._textCtrl
.StopEditing()
5351 self
._hasFocus
= True
5352 self
.SetFocusIgnoringChildren()
5354 # If the item is already selected, do not update the selection.
5355 # Multi-selections should not be cleared if a selected item is clicked.
5356 if not self
.IsSelected(item
):
5358 self
.DoSelectItem(item
, True, False)
5360 nevent
= TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK
, self
.GetId())
5362 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5363 nevent
.SetEventObject(self
)
5364 event
.Skip(not self
.GetEventHandler().ProcessEvent(nevent
))
5366 # Consistent with MSW (for now), send the ITEM_MENU *after*
5367 # the RIGHT_CLICK event. TODO: This behaviour may change.
5368 nevent2
= TreeEvent(wxEVT_TREE_ITEM_MENU
, self
.GetId())
5369 nevent2
._item
= item
5370 nevent2
._pointDrag
= self
.CalcScrolledPosition(pt
)
5371 nevent2
.SetEventObject(self
)
5372 self
.GetEventHandler().ProcessEvent(nevent2
)
5374 elif event
.LeftUp():
5376 # this facilitates multiple-item drag-and-drop
5378 if self
.HasFlag(TR_MULTIPLE
):
5380 selections
= self
.GetSelections()
5382 if len(selections
) > 1 and not event
.CmdDown() and not event
.ShiftDown():
5384 self
.DoSelectItem(item
, True, False)
5386 if self
._lastOnSame
:
5388 if item
== self
._current
and (flags
& TREE_HITTEST_ONITEMLABEL
) and self
.HasFlag(TR_EDIT_LABELS
):
5390 if self
._renameTimer
:
5392 if self
._renameTimer
.IsRunning():
5394 self
._renameTimer
.Stop()
5398 self
._renameTimer
= TreeRenameTimer(self
)
5400 self
._renameTimer
.Start(_DELAY
, True)
5402 self
._lastOnSame
= False
5405 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5407 if not item
or not item
.IsEnabled():
5408 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5409 self
._textCtrl
.StopEditing()
5412 if self
._textCtrl
!= None and item
!= self
._textCtrl
.item():
5413 self
._textCtrl
.StopEditing()
5415 self
._hasFocus
= True
5416 self
.SetFocusIgnoringChildren()
5418 if event
.LeftDown():
5420 self
._lastOnSame
= item
== self
._current
5422 if flags
& TREE_HITTEST_ONITEMBUTTON
:
5424 # only toggle the item for a single click, double click on
5425 # the button doesn't do anything (it toggles the item twice)
5426 if event
.LeftDown():
5430 # don't select the item if the button was clicked
5433 if item
.GetType() > 0 and (flags
& TREE_HITTEST_ONITEMCHECKICON
):
5435 if event
.LeftDown():
5437 self
.CheckItem(item
, not self
.IsItemChecked(item
))
5441 # clear the previously selected items, if the
5442 # user clicked outside of the present selection.
5443 # otherwise, perform the deselection on mouse-up.
5444 # this allows multiple drag and drop to work.
5445 # but if Cmd is down, toggle selection of the clicked item
5446 if not self
.IsSelected(item
) or event
.CmdDown():
5448 if flags
& TREE_HITTEST_ONITEM
:
5449 # how should the selection work for this event?
5450 if item
.IsHyperText():
5451 self
.SetItemVisited(item
, True)
5453 is_multiple
, extended_select
, unselect_others
= EventFlagsToSelType(self
.GetTreeStyle(),
5457 self
.DoSelectItem(item
, unselect_others
, extended_select
)
5459 # For some reason, Windows isn't recognizing a left double-click,
5460 # so we need to simulate it here. Allow 200 milliseconds for now.
5461 if event
.LeftDClick():
5463 # double clicking should not start editing the item label
5464 if self
._renameTimer
:
5465 self
._renameTimer
.Stop()
5467 self
._lastOnSame
= False
5469 # send activate event first
5470 nevent
= TreeEvent(wxEVT_TREE_ITEM_ACTIVATED
, self
.GetId())
5472 nevent
._pointDrag
= self
.CalcScrolledPosition(pt
)
5473 nevent
.SetEventObject(self
)
5474 if not self
.GetEventHandler().ProcessEvent(nevent
):
5476 # if the user code didn't process the activate event,
5477 # handle it ourselves by toggling the item when it is
5479 ## if item.HasPlus():
5483 def OnInternalIdle(self
):
5484 """Performs operations in idle time (essentially drawing)."""
5486 # Check if we need to select the root item
5487 # because nothing else has been selected.
5488 # Delaying it means that we can invoke event handlers
5489 # as required, when a first item is selected.
5490 if not self
.HasFlag(TR_MULTIPLE
) and not self
.GetSelection():
5493 self
.SelectItem(self
._select
_me
)
5494 elif self
.GetRootItem():
5495 self
.SelectItem(self
.GetRootItem())
5497 # after all changes have been done to the tree control,
5498 # we actually redraw the tree when everything is over
5502 if self
._freezeCount
:
5507 self
.CalculatePositions()
5509 self
.AdjustMyScrollbars()
5514 def CalculateSize(self
, item
, dc
):
5515 """Calculates overall position and size of an item."""
5517 attr
= item
.GetAttributes()
5519 if attr
and attr
.HasFont():
5520 dc
.SetFont(attr
.GetFont())
5522 dc
.SetFont(self
._boldFont
)
5524 dc
.SetFont(self
._normalFont
)
5526 text_w
, text_h
, dummy
= dc
.GetMultiLineTextExtent(item
.GetText())
5529 # restore normal font
5530 dc
.SetFont(self
._normalFont
)
5532 image_w
, image_h
= 0, 0
5533 image
= item
.GetCurrentImage()
5535 if image
!= _NO_IMAGE
:
5537 if self
._imageListNormal
:
5539 image_w
, image_h
= self
._imageListNormal
.GetSize(image
)
5542 total_h
= ((image_h
> text_h
) and [image_h
] or [text_h
])[0]
5544 checkimage
= item
.GetCurrentCheckedImage()
5545 if checkimage
is not None:
5546 wcheck
, hcheck
= self
._imageListCheck
.GetSize(checkimage
)
5552 total_h
+= 2 # at least 2 pixels
5554 total_h
+= total_h
/10 # otherwise 10% extra spacing
5556 if total_h
> self
._lineHeight
:
5557 self
._lineHeight
= total_h
5559 if not item
.GetWindow():
5560 item
.SetWidth(image_w
+text_w
+wcheck
+2)
5561 item
.SetHeight(total_h
)
5563 item
.SetWidth(item
.GetWindowSize()[0]+image_w
+text_w
+wcheck
+2)
5564 item
.SetHeight(max(total_h
, item
.GetWindowSize()[1]))
5567 def CalculateLevel(self
, item
, dc
, level
, y
):
5568 """Calculates the level of an item."""
5570 x
= level
*self
._indent
5572 if not self
.HasFlag(TR_HIDE_ROOT
):
5578 # a hidden root is not evaluated, but its
5579 # children are always calculated
5580 children
= item
.GetChildren()
5581 count
= len(children
)
5583 for n
in xrange(count
):
5584 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5588 self
.CalculateSize(item
, dc
)
5591 item
.SetX(x
+self
._spacing
)
5593 y
+= self
.GetLineHeight(item
)
5595 if not item
.IsExpanded():
5596 # we don't need to calculate collapsed branches
5599 children
= item
.GetChildren()
5600 count
= len(children
)
5602 for n
in xrange(count
):
5603 y
= self
.CalculateLevel(children
[n
], dc
, level
, y
) # recurse
5608 def CalculatePositions(self
):
5609 """Calculates all the positions of the visible items."""
5611 if not self
._anchor
:
5614 dc
= wx
.ClientDC(self
)
5617 dc
.SetFont(self
._normalFont
)
5618 dc
.SetPen(self
._dottedPen
)
5620 y
= self
.CalculateLevel(self
._anchor
, dc
, 0, y
) # start recursion
5623 def RefreshSubtree(self
, item
):
5624 """Refreshes a damaged subtree of an item."""
5628 if self
._freezeCount
:
5631 client
= self
.GetClientSize()
5634 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5635 rect
.width
= client
.x
5636 rect
.height
= client
.y
5638 self
.Refresh(True, rect
)
5639 self
.AdjustMyScrollbars()
5642 def RefreshLine(self
, item
):
5643 """Refreshes a damaged item line."""
5647 if self
._freezeCount
:
5651 x
, rect
.y
= self
.CalcScrolledPosition(0, item
.GetY())
5652 rect
.width
= self
.GetClientSize().x
5653 rect
.height
= self
.GetLineHeight(item
)
5655 self
.Refresh(True, rect
)
5658 def RefreshSelected(self
):
5659 """Refreshes a damaged selected item line."""
5661 if self
._freezeCount
:
5664 # TODO: this is awfully inefficient, we should keep the list of all
5665 # selected items internally, should be much faster
5667 self
.RefreshSelectedUnder(self
._anchor
)
5670 def RefreshSelectedUnder(self
, item
):
5671 """Refreshes the selected items under the given item."""
5673 if self
._freezeCount
:
5676 if item
.IsSelected():
5677 self
.RefreshLine(item
)
5679 children
= item
.GetChildren()
5680 for child
in children
:
5681 self
.RefreshSelectedUnder(child
)
5685 """Freeze CustomTreeCtrl."""
5687 self
._freezeCount
= self
._freezeCount
+ 1
5691 """Thaw CustomTreeCtrl."""
5693 if self
._freezeCount
== 0:
5694 raise Exception("\nERROR: Thawing Unfrozen Tree Control?")
5696 self
._freezeCount
= self
._freezeCount
- 1
5698 if not self
._freezeCount
:
5702 # ----------------------------------------------------------------------------
5703 # changing colours: we need to refresh the tree control
5704 # ----------------------------------------------------------------------------
5706 def SetBackgroundColour(self
, colour
):
5707 """Changes the background colour of CustomTreeCtrl."""
5709 if not wx
.Window
.SetBackgroundColour(self
, colour
):
5712 if self
._freezeCount
:
5720 def SetForegroundColour(self
, colour
):
5721 """Changes the foreground colour of CustomTreeCtrl."""
5723 if not wx
.Window
.SetForegroundColour(self
, colour
):
5726 if self
._freezeCount
:
5734 def OnGetToolTip(self
, event
):
5736 Process the tooltip event, to speed up event processing. Does not actually
5743 def DoGetBestSize(self
):
5744 """Something is better than nothing..."""
5746 # something is better than nothing...
5747 # 100x80 is what the MSW version will get from the default
5748 # wxControl::DoGetBestSize
5750 return wx
.Size(100, 80)
5753 def GetClassDefaultAttributes(self
):
5754 """Gets the class default attributes."""
5756 attr
= wx
.VisualAttributes()
5757 attr
.colFg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_WINDOWTEXT
)
5758 attr
.colBg
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_LISTBOX
)
5759 attr
.font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
5762 GetClassDefaultAttributes
= classmethod(GetClassDefaultAttributes
)