]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/customtreectrl.py
Add CustomTreeCtrl from Andrea Gavana
[wxWidgets.git] / wxPython / wx / lib / customtreectrl.py
1 # --------------------------------------------------------------------------------- #
2 # CUSTOMTREECTRL wxPython IMPLEMENTATION
3 # Inspired By And Heavily Based On wxGenericTreeCtrl.
4 #
5 # Andrea Gavana, @ 17 May 2006
6 # Latest Revision: 26 May 2006, 22.30 CET
7 #
8 #
9 # TODO List
10 #
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
13 # To My Mind Are:
14 #
15 # 1. Implement The Style TR_EXTENDED (I Have Never Used It, But It May Be Useful).
16 #
17 # 2. Add Support For 3-State CheckBoxes (Is That Really Useful?).
18 #
19 # 3. Try To Implement A More Flicker-Free Background Image In Cases Like
20 # Centered Or Stretched Image (Now CustomTreeCtrl Supports Only Tiled
21 # Background Images).
22 #
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.
27 #
28 # 5. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite
29 # Fast, But We Should See On Slower Machines.
30 #
31 #
32 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
33 # Write To Me At:
34 #
35 # gavana@kpo.kz
36 # andrea.gavana@gmail.com
37 #
38 # Or, Obviously, To The wxPython Mailing List!!!
39 #
40 #
41 # End Of Comments
42 # --------------------------------------------------------------------------------- #
43
44
45 """
46 Description
47 ===========
48
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:
54
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;
57
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
63 must be unchecked.
64 - If a radiobutton node becomes unchecked, then all of its child nodes will become
65 inactive.
66
67 * Hyperlink-type items: they look like an hyperlink, with the proper mouse cursor on
68 hovering.
69
70 * Multiline text items.
71
72 * Enabling/disabling items (together with their plain or grayed out icons).
73
74 * Whatever non-toplevel widget can be attached next to an item.
75
76 * Default selection style, gradient (horizontal/vertical) selection style and Windows
77 Vista selection style.
78
79 * Customized drag and drop images built on the fly.
80
81 * Setting the CustomTreeCtrl item buttons to a personalized imagelist.
82
83 * Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist.
84
85 * Changing the style of the lines that connect the items (in terms of wx.Pen styles).
86
87 * Using an image as a CustomTreeCtrl background (currently only in "tile" mode).
88
89 And a lot more. Check the demo for an almost complete review of the functionalities.
90
91
92 Base Functionalities
93 ====================
94
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?).
97
98 Plus it has 2 more styles to handle checkbox-type items:
99 - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children;
100 - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children.
101
102 All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl.
103
104
105 Events
106 ======
107
108 All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with
109 a few exceptions:
110
111 - EVT_TREE_GET_INFO (don't know what this means);
112 - EVT_TREE_SET_INFO (don't know what this means);
113 - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add);
114 - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below.
115
116 Plus, CustomTreeCtrl supports the events related to the checkbutton-type items:
117
118 - EVT_TREE_ITEM_CHECKING: an item is being checked;
119 - EVT_TREE_ITEM_CHECKED: an item has been checked.
120
121 And to hyperlink-type items:
122
123 - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent
124 after the EVT_TREE_SEL_CHANGED event).
125
126
127 Supported Platforms
128 ===================
129
130 CustomTreeCtrl has been tested on the following platforms:
131 * Windows (Windows XP);
132 * GTK (Thanks to Michele Petrazzo);
133 * Mac OS (Thanks to John Jackson).
134
135
136 Latest Revision: Andrea Gavana @ 26 May 2006, 22.30 CET
137 Version 0.8
138
139 """
140
141
142 import wx
143 import zlib
144 import cStringIO
145
146 # ----------------------------------------------------------------------------
147 # Constants
148 # ----------------------------------------------------------------------------
149
150 _NO_IMAGE = -1
151 _PIXELS_PER_UNIT = 10
152
153 # Start editing the current item after half a second (if the mouse hasn't
154 # been clicked/moved)
155 _DELAY = 500
156
157 # ----------------------------------------------------------------------------
158 # Constants
159 # ----------------------------------------------------------------------------
160
161 # Enum for different images associated with a treectrl item
162 TreeItemIcon_Normal = 0 # not selected, not expanded
163 TreeItemIcon_Selected = 1 # selected, not expanded
164 TreeItemIcon_Expanded = 2 # not selected, expanded
165 TreeItemIcon_SelectedExpanded = 3 # selected, expanded
166
167 TreeItemIcon_Checked = 0 # check button, checked
168 TreeItemIcon_NotChecked = 1 # check button, not checked
169 TreeItemIcon_Flagged = 2 # radio button, selected
170 TreeItemIcon_NotFlagged = 3 # radio button, not selected
171
172 # ----------------------------------------------------------------------------
173 # CustomTreeCtrl flags
174 # ----------------------------------------------------------------------------
175
176 TR_NO_BUTTONS = wx.TR_NO_BUTTONS # for convenience
177 TR_HAS_BUTTONS = wx.TR_HAS_BUTTONS # draw collapsed/expanded btns
178 TR_NO_LINES = wx.TR_NO_LINES # don't draw lines at all
179 TR_LINES_AT_ROOT = wx.TR_LINES_AT_ROOT # connect top-level nodes
180 TR_TWIST_BUTTONS = wx.TR_TWIST_BUTTONS # still used by wxTreeListCtrl
181
182 TR_SINGLE = wx.TR_SINGLE # for convenience
183 TR_MULTIPLE = wx.TR_MULTIPLE # can select multiple items
184 TR_EXTENDED = wx.TR_EXTENDED # TODO: allow extended selection
185 TR_HAS_VARIABLE_ROW_HEIGHT = wx.TR_HAS_VARIABLE_ROW_HEIGHT # what it says
186
187 TR_EDIT_LABELS = wx.TR_EDIT_LABELS # can edit item labels
188 TR_ROW_LINES = wx.TR_ROW_LINES # put border around items
189 TR_HIDE_ROOT = wx.TR_HIDE_ROOT # don't display root node
190
191 TR_FULL_ROW_HIGHLIGHT = wx.TR_FULL_ROW_HIGHLIGHT # highlight full horz space
192
193 TR_AUTO_CHECK_CHILD = 0x4000 # only meaningful for checkboxes
194 TR_AUTO_TOGGLE_CHILD = 0x8000 # only meaningful for checkboxes
195
196 TR_DEFAULT_STYLE = wx.TR_DEFAULT_STYLE # default style for the tree control
197
198 # Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine
199 # where exactly the specified point is situated:
200
201 TREE_HITTEST_ABOVE = wx.TREE_HITTEST_ABOVE
202 TREE_HITTEST_BELOW = wx.TREE_HITTEST_BELOW
203 TREE_HITTEST_NOWHERE = wx.TREE_HITTEST_NOWHERE
204 # on the button associated with an item.
205 TREE_HITTEST_ONITEMBUTTON = wx.TREE_HITTEST_ONITEMBUTTON
206 # on the bitmap associated with an item.
207 TREE_HITTEST_ONITEMICON = wx.TREE_HITTEST_ONITEMICON
208 # on the indent associated with an item.
209 TREE_HITTEST_ONITEMINDENT = wx.TREE_HITTEST_ONITEMINDENT
210 # on the label (string) associated with an item.
211 TREE_HITTEST_ONITEMLABEL = wx.TREE_HITTEST_ONITEMLABEL
212 # on the right of the label associated with an item.
213 TREE_HITTEST_ONITEMRIGHT = wx.TREE_HITTEST_ONITEMRIGHT
214 # on the label (string) associated with an item.
215 TREE_HITTEST_ONITEMSTATEICON = wx.TREE_HITTEST_ONITEMSTATEICON
216 # on the left of the CustomTreeCtrl.
217 TREE_HITTEST_TOLEFT = wx.TREE_HITTEST_TOLEFT
218 # on the right of the CustomTreeCtrl.
219 TREE_HITTEST_TORIGHT = wx.TREE_HITTEST_TORIGHT
220 # on the upper part (first half) of the item.
221 TREE_HITTEST_ONITEMUPPERPART = wx.TREE_HITTEST_ONITEMUPPERPART
222 # on the lower part (second half) of the item.
223 TREE_HITTEST_ONITEMLOWERPART = wx.TREE_HITTEST_ONITEMLOWERPART
224 # on the check icon, if present
225 TREE_HITTEST_ONITEMCHECKICON = 0x4000
226 # anywhere on the item
227 TREE_HITTEST_ONITEM = TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON
228
229
230 # Background Image Style
231 _StyleTile = 0
232 _StyleStretch = 1
233
234 # Windows Vista Colours
235 _rgbSelectOuter = wx.Colour(170, 200, 245)
236 _rgbSelectInner = wx.Colour(230, 250, 250)
237 _rgbSelectTop = wx.Colour(210, 240, 250)
238 _rgbSelectBottom = wx.Colour(185, 215, 250)
239 _rgbNoFocusTop = wx.Colour(250, 250, 250)
240 _rgbNoFocusBottom = wx.Colour(235, 235, 235)
241 _rgbNoFocusOuter = wx.Colour(220, 220, 220)
242 _rgbNoFocusInner = wx.Colour(245, 245, 245)
243
244 # Flags for wx.RendererNative
245 _CONTROL_EXPANDED = 8
246 _CONTROL_CURRENT = 16
247
248 # Version Info
249 __version__ = "0.8"
250
251
252 # ----------------------------------------------------------------------------
253 # CustomTreeCtrl events and binding for handling them
254 # ----------------------------------------------------------------------------
255
256 wxEVT_TREE_BEGIN_DRAG = wx.wxEVT_COMMAND_TREE_BEGIN_DRAG
257 wxEVT_TREE_BEGIN_RDRAG = wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG
258 wxEVT_TREE_BEGIN_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
259 wxEVT_TREE_END_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT
260 wxEVT_TREE_DELETE_ITEM = wx.wxEVT_COMMAND_TREE_DELETE_ITEM
261 wxEVT_TREE_GET_INFO = wx.wxEVT_COMMAND_TREE_GET_INFO
262 wxEVT_TREE_SET_INFO = wx.wxEVT_COMMAND_TREE_SET_INFO
263 wxEVT_TREE_ITEM_EXPANDED = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDED
264 wxEVT_TREE_ITEM_EXPANDING = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING
265 wxEVT_TREE_ITEM_COLLAPSED = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
266 wxEVT_TREE_ITEM_COLLAPSING = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
267 wxEVT_TREE_SEL_CHANGED = wx.wxEVT_COMMAND_TREE_SEL_CHANGED
268 wxEVT_TREE_SEL_CHANGING = wx.wxEVT_COMMAND_TREE_SEL_CHANGING
269 wxEVT_TREE_KEY_DOWN = wx.wxEVT_COMMAND_TREE_KEY_DOWN
270 wxEVT_TREE_ITEM_ACTIVATED = wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
271 wxEVT_TREE_ITEM_RIGHT_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
272 wxEVT_TREE_ITEM_MIDDLE_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
273 wxEVT_TREE_END_DRAG = wx.wxEVT_COMMAND_TREE_END_DRAG
274 wxEVT_TREE_STATE_IMAGE_CLICK = wx.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
275 wxEVT_TREE_ITEM_GETTOOLTIP = wx.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
276 wxEVT_TREE_ITEM_MENU = wx.wxEVT_COMMAND_TREE_ITEM_MENU
277 wxEVT_TREE_ITEM_CHECKING = wx.NewEventType()
278 wxEVT_TREE_ITEM_CHECKED = wx.NewEventType()
279 wxEVT_TREE_ITEM_HYPERLINK = wx.NewEventType()
280
281 EVT_TREE_BEGIN_DRAG = wx.EVT_TREE_BEGIN_DRAG
282 EVT_TREE_BEGIN_RDRAG = wx.EVT_TREE_BEGIN_RDRAG
283 EVT_TREE_BEGIN_LABEL_EDIT = wx.EVT_TREE_BEGIN_LABEL_EDIT
284 EVT_TREE_END_LABEL_EDIT = wx.EVT_TREE_END_LABEL_EDIT
285 EVT_TREE_DELETE_ITEM = wx.EVT_TREE_DELETE_ITEM
286 EVT_TREE_GET_INFO = wx.EVT_TREE_GET_INFO
287 EVT_TREE_SET_INFO = wx.EVT_TREE_SET_INFO
288 EVT_TREE_ITEM_EXPANDED = wx.EVT_TREE_ITEM_EXPANDED
289 EVT_TREE_ITEM_EXPANDING = wx.EVT_TREE_ITEM_EXPANDING
290 EVT_TREE_ITEM_COLLAPSED = wx.EVT_TREE_ITEM_COLLAPSED
291 EVT_TREE_ITEM_COLLAPSING = wx.EVT_TREE_ITEM_COLLAPSING
292 EVT_TREE_SEL_CHANGED = wx.EVT_TREE_SEL_CHANGED
293 EVT_TREE_SEL_CHANGING = wx.EVT_TREE_SEL_CHANGING
294 EVT_TREE_KEY_DOWN = wx.EVT_TREE_KEY_DOWN
295 EVT_TREE_ITEM_ACTIVATED = wx.EVT_TREE_ITEM_ACTIVATED
296 EVT_TREE_ITEM_RIGHT_CLICK = wx.EVT_TREE_ITEM_RIGHT_CLICK
297 EVT_TREE_ITEM_MIDDLE_CLICK = wx.EVT_TREE_ITEM_MIDDLE_CLICK
298 EVT_TREE_END_DRAG = wx.EVT_TREE_END_DRAG
299 EVT_TREE_STATE_IMAGE_CLICK = wx.EVT_TREE_STATE_IMAGE_CLICK
300 EVT_TREE_ITEM_GETTOOLTIP = wx.EVT_TREE_ITEM_GETTOOLTIP
301 EVT_TREE_ITEM_MENU = wx.EVT_TREE_ITEM_MENU
302 EVT_TREE_ITEM_CHECKING = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKING, 1)
303 EVT_TREE_ITEM_CHECKED = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKED, 1)
304 EVT_TREE_ITEM_HYPERLINK = wx.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK, 1)
305
306
307 def GetFlaggedData():
308 return zlib.decompress(
309 'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
310 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
311 |\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\
312 \x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\
313 \xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\
314 \xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\
315 \xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\
316 \xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\
317 \r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\
318 \xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\
319 \xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\
320 \xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\
321 \x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\
322 \xb3\xb7H,a\xe1\xa9)\xdf<e$2Ble\x85\x94e\xb1\x96\xcep\xfb\xdd-D\x04\xa5\x14\
323 \xdeZ\'\xb1\x84\x85\xd8\x8bm\x84\xe6\x977\x7f8kog)\xba\xc4\xb7\xe5\xef$\xe2?\
324 \xe9\xa9\xbf\x86R\n\x11a&\x1c\xc1^lC|\r.\x02\xb3\x8b\x9b\xa6&G\x13W\xaa\xbb\
325 \x91_\x05\x0c\x1d\xbfI\xc7\xa1\x8e\xbf&a|:\x8c\xaf\xc1\x05J4\x8e\xd6>36\x192\
326 \xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\
327 \xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\
328 nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\
329 \xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\
330 \xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' )
331
332 def GetFlaggedBitmap():
333 return wx.BitmapFromImage(GetFlaggedImage())
334
335 def GetFlaggedImage():
336 stream = cStringIO.StringIO(GetFlaggedData())
337 return wx.ImageFromStream(stream)
338
339 #----------------------------------------------------------------------
340 def GetNotFlaggedData():
341 return zlib.decompress(
342 'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
343 \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
344 |\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\
345 \x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\
346 \x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\
347 \x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\
348 \x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\
349 \xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\
350 \x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\
351 \xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\
352 \x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\
353 \xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\
354 \x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\
355 \x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\
356 \xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\
357 \xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\
358 \xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' )
359
360 def GetNotFlaggedBitmap():
361 return wx.BitmapFromImage(GetNotFlaggedImage())
362
363 def GetNotFlaggedImage():
364 stream = cStringIO.StringIO(GetNotFlaggedData())
365 return wx.ImageFromStream(stream)
366
367 #----------------------------------------------------------------------
368 def GetCheckedData():
369 return zlib.decompress(
370 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
371 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\
372 \x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\
373 \xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\
374 \x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\
375 \xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\
376 \xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\
377 \xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\
378 \x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\
379 \xeb\x9c\x12\x9a\x00\x92\xccS\x9f" )
380
381 def GetCheckedBitmap():
382 return wx.BitmapFromImage(GetCheckedImage())
383
384 def GetCheckedImage():
385 stream = cStringIO.StringIO(GetCheckedData())
386 return wx.ImageFromStream(stream)
387
388 #----------------------------------------------------------------------
389 def GetNotCheckedData():
390 return zlib.decompress(
391 "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
392 \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\
393 \xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\
394 \xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\
395 \x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\
396 \xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\
397 \xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\
398 \x00\x87S=\xbe" )
399
400 def GetNotCheckedBitmap():
401 return wx.BitmapFromImage(GetNotCheckedImage())
402
403 def GetNotCheckedImage():
404 stream = cStringIO.StringIO(GetNotCheckedData())
405 return wx.ImageFromStream(stream)
406
407
408 def GrayOut(anImage):
409 """
410 Convert the given image (in place) to a grayed-out version,
411 appropriate for a 'disabled' appearance.
412 """
413
414 factor = 0.7 # 0 < f < 1. Higher Is Grayer
415
416 if anImage.HasMask():
417 maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue())
418 else:
419 maskColor = None
420
421 data = map(ord, list(anImage.GetData()))
422
423 for i in range(0, len(data), 3):
424
425 pixel = (data[i], data[i+1], data[i+2])
426 pixel = MakeGray(pixel, factor, maskColor)
427
428 for x in range(3):
429 data[i+x] = pixel[x]
430
431 anImage.SetData(''.join(map(chr, data)))
432
433 return anImage
434
435
436 def MakeGray((r,g,b), factor, maskColor):
437 """
438 Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be
439 changed.
440 """
441
442 if (r,g,b) != maskColor:
443 return map(lambda x: int((230 - x) * factor) + x, (r,g,b))
444 else:
445 return (r,g,b)
446
447
448 def DrawTreeItemButton(win, dc, rect, flags):
449 """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """
450
451 # white background
452 dc.SetPen(wx.GREY_PEN)
453 dc.SetBrush(wx.WHITE_BRUSH)
454 dc.DrawRectangleRect(rect)
455
456 # black lines
457 xMiddle = rect.x + rect.width/2
458 yMiddle = rect.y + rect.height/2
459
460 # half of the length of the horz lines in "-" and "+"
461 halfWidth = rect.width/2 - 2
462 dc.SetPen(wx.BLACK_PEN)
463 dc.DrawLine(xMiddle - halfWidth, yMiddle,
464 xMiddle + halfWidth + 1, yMiddle)
465
466 if not flags & _CONTROL_EXPANDED:
467
468 # turn "-" into "+"
469 halfHeight = rect.height/2 - 2
470 dc.DrawLine(xMiddle, yMiddle - halfHeight,
471 xMiddle, yMiddle + halfHeight + 1)
472
473
474 #---------------------------------------------------------------------------
475 # DragImage Implementation
476 # This Class Handles The Creation Of A Custom Image In Case Of Item Drag
477 # And Drop.
478 #---------------------------------------------------------------------------
479
480 class DragImage(wx.DragImage):
481 """
482 This class handles the creation of a custom image in case of item drag
483 and drop.
484 """
485
486 def __init__(self, treeCtrl, item):
487 """
488 Default class constructor.
489 For internal use: do not call it in your code!
490 """
491
492 text = item.GetText()
493 font = item.Attr().GetFont()
494 colour = item.Attr().GetTextColour()
495 if colour is None:
496 colour = wx.BLACK
497 if font is None:
498 font = treeCtrl._normalFont
499
500 backcolour = treeCtrl.GetBackgroundColour()
501 r, g, b = int(backcolour.Red()), int(backcolour.Green()), int(backcolour.Blue())
502 backcolour = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
503 backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2])
504 self._backgroundColour = backcolour
505
506 tempdc = wx.ClientDC(treeCtrl)
507 tempdc.SetFont(font)
508 width, height, dummy = tempdc.GetMultiLineTextExtent(text + "M")
509
510 image = item.GetCurrentImage()
511
512 image_w, image_h = 0, 0
513 wcheck, hcheck = 0, 0
514 itemcheck = None
515 itemimage = None
516 ximagepos = 0
517 yimagepos = 0
518 xcheckpos = 0
519 ycheckpos = 0
520
521 if image != _NO_IMAGE:
522 if treeCtrl._imageListNormal:
523 image_w, image_h = treeCtrl._imageListNormal.GetSize(image)
524 image_w += 4
525 itemimage = treeCtrl._imageListNormal.GetBitmap(image)
526
527 checkimage = item.GetCurrentCheckedImage()
528
529 if checkimage is not None:
530 if treeCtrl._imageListCheck:
531 wcheck, hcheck = treeCtrl._imageListCheck.GetSize(checkimage)
532 wcheck += 4
533 itemcheck = treeCtrl._imageListCheck.GetBitmap(checkimage)
534
535 total_h = max(hcheck, height)
536 total_h = max(image_h, total_h)
537
538 if image_w:
539 ximagepos = wcheck
540 yimagepos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0]
541
542 if checkimage is not None:
543 xcheckpos = 2
544 ycheckpos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] + 2
545
546 extraH = ((total_h > height) and [(total_h - height)/2] or [0])[0]
547
548 xtextpos = wcheck + image_w
549 ytextpos = extraH
550
551 total_h = max(image_h, hcheck)
552 total_h = max(total_h, height)
553
554 if total_h < 30:
555 total_h += 2 # at least 2 pixels
556 else:
557 total_h += total_h/10 # otherwise 10% extra spacing
558
559 total_w = image_w + wcheck + width
560
561 self._total_w = total_w
562 self._total_h = total_h
563 self._itemimage = itemimage
564 self._itemcheck = itemcheck
565 self._text = text
566 self._colour = colour
567 self._font = font
568 self._xtextpos = xtextpos
569 self._ytextpos = ytextpos
570 self._ximagepos = ximagepos
571 self._yimagepos = yimagepos
572 self._xcheckpos = xcheckpos
573 self._ycheckpos = ycheckpos
574 self._textwidth = width
575 self._textheight = height
576 self._extraH = extraH
577
578 self._bitmap = self.CreateBitmap()
579
580 wx.DragImage.__init__(self, self._bitmap)
581
582
583 def CreateBitmap(self):
584 """Actually creates the dnd bitmap."""
585
586 memory = wx.MemoryDC()
587
588 bitmap = wx.EmptyBitmap(self._total_w, self._total_h)
589 memory.SelectObject(bitmap)
590
591 memory.SetTextBackground(self._backgroundColour)
592 memory.SetBackground(wx.Brush(self._backgroundColour))
593 memory.SetFont(self._font)
594 memory.SetTextForeground(self._colour)
595 memory.Clear()
596
597 if self._itemimage:
598 memory.DrawBitmap(self._itemimage, self._ximagepos, self._yimagepos, True)
599
600 if self._itemcheck:
601 memory.DrawBitmap(self._itemcheck, self._xcheckpos, self._ycheckpos, True)
602
603 textrect = wx.Rect(self._xtextpos, self._ytextpos+self._extraH, self._textwidth, self._textheight)
604 memory.DrawLabel(self._text, textrect)
605
606 memory.SelectObject(wx.NullBitmap)
607
608 return bitmap
609
610
611 # ----------------------------------------------------------------------------
612 # TreeItemAttr: a structure containing the visual attributes of an item
613 # ----------------------------------------------------------------------------
614
615 class TreeItemAttr:
616 """Creates the item attributes (text colour, background colour and font)."""
617
618 def __init__(self, colText=wx.NullColour, colBack=wx.NullColour, font=wx.NullFont):
619 """
620 Default class constructor.
621 For internal use: do not call it in your code!
622 """
623
624 self._colText = colText
625 self._colBack = colBack
626 self._font = font
627
628 # setters
629 def SetTextColour(self, colText):
630 """Sets the attribute text colour."""
631
632 self._colText = colText
633
634
635 def SetBackgroundColour(self, colBack):
636 """Sets the attribute background colour."""
637
638 self._colBack = colBack
639
640
641 def SetFont(self, font):
642 """Sets the attribute font."""
643
644 self._font = font
645
646
647 # accessors
648 def HasTextColour(self):
649 """Returns whether the attribute has text colour."""
650
651 return self._colText != wx.NullColour
652
653
654 def HasBackgroundColour(self):
655 """Returns whether the attribute has background colour."""
656
657 return self._colBack != wx.NullColour
658
659
660 def HasFont(self):
661 """Returns whether the attribute has font."""
662
663 return self._font != wx.NullFont
664
665
666 # getters
667 def GetTextColour(self):
668 """Returns the attribute text colour."""
669
670 return self._colText
671
672
673 def GetBackgroundColour(self):
674 """Returns the attribute background colour."""
675
676 return self._colBack
677
678
679 def GetFont(self):
680 """Returns the attribute font."""
681
682 return self._font
683
684
685 # ----------------------------------------------------------------------------
686 # CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent
687 #
688 # NB: Note That Not All The Accessors Make Sense For All The Events, See The
689 # Event Description Below.
690 # ----------------------------------------------------------------------------
691
692 class CommandTreeEvent(wx.PyCommandEvent):
693 """
694 CommandTreeEvent is a special subclassing of wx.PyCommandEvent.
695 NB: note that not all the accessors make sense for all the events, see the
696 event description for every method in this class.
697 """
698
699 def __init__(self, type, id, item=None, evtKey=None, point=None,
700 label=None, **kwargs):
701 """
702 Default class constructor.
703 For internal use: do not call it in your code!
704 """
705
706 wx.PyCommandEvent.__init__(self, type, id, **kwargs)
707 self._item = item
708 self._evtKey = evtKey
709 self._pointDrag = point
710 self._label = label
711
712
713 def GetItem(self):
714 """
715 Gets the item on which the operation was performed or the newly selected
716 item for EVT_TREE_SEL_CHANGED/ING events.
717 """
718
719 return self._item
720
721
722 def SetItem(self, item):
723 """
724 Sets the item on which the operation was performed or the newly selected
725 item for EVT_TREE_SEL_CHANGED/ING events.
726 """
727
728 self._item = item
729
730
731 def GetOldItem(self):
732 """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item."""
733
734 return self._itemOld
735
736
737 def SetOldItem(self, item):
738 """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item."""
739
740 self._itemOld = item
741
742
743 def GetPoint(self):
744 """
745 Returns the point where the mouse was when the drag operation started
746 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
747 """
748
749 return self._pointDrag
750
751
752 def SetPoint(self, pt):
753 """
754 Sets the point where the mouse was when the drag operation started
755 (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
756 """
757
758 self._pointDrag = pt
759
760
761 def GetKeyEvent(self):
762 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
763
764 return self._evtKey
765
766
767 def GetKeyCode(self):
768 """Returns the integer key code (for EVT_TREE_KEY_DOWN only)."""
769
770 return self._evtKey.GetKeyCode()
771
772
773 def SetKeyEvent(self, evt):
774 """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
775
776 self._evtKey = evt
777
778
779 def GetLabel(self):
780 """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
781
782 return self._label
783
784
785 def SetLabel(self, label):
786 """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
787
788 self._label = label
789
790
791 def IsEditCancelled(self):
792 """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
793
794 return self._editCancelled
795
796
797 def SetEditCanceled(self, editCancelled):
798 """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
799
800 self._editCancelled = editCancelled
801
802
803 def SetToolTip(self, toolTip):
804 """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
805
806 self._label = toolTip
807
808
809 def GetToolTip(self):
810 """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
811
812 return self._label
813
814
815 # ----------------------------------------------------------------------------
816 # TreeEvent is a special class for all events associated with tree controls
817 #
818 # NB: note that not all accessors make sense for all events, see the event
819 # descriptions below
820 # ----------------------------------------------------------------------------
821
822 class TreeEvent(CommandTreeEvent):
823
824 def __init__(self, type, id, item=None, evtKey=None, point=None,
825 label=None, **kwargs):
826 """
827 Default class constructor.
828 For internal use: do not call it in your code!
829 """
830
831 CommandTreeEvent.__init__(self, type, id, item, evtKey, point, label, **kwargs)
832 self.notify = wx.NotifyEvent(type, id)
833
834
835 def GetNotifyEvent(self):
836 """Returns the actual wx.NotifyEvent."""
837
838 return self.notify
839
840
841 def IsAllowed(self):
842 """Returns whether the event is allowed or not."""
843
844 return self.notify.IsAllowed()
845
846
847 def Veto(self):
848 """Vetos the event."""
849
850 self.notify.Veto()
851
852
853 def Allow(self):
854 """The event is allowed."""
855
856 self.notify.Allow()
857
858
859 # -----------------------------------------------------------------------------
860 # Auxiliary Classes: TreeRenameTimer
861 # -----------------------------------------------------------------------------
862
863 class TreeRenameTimer(wx.Timer):
864 """Timer used for enabling in-place edit."""
865
866 def __init__(self, owner):
867 """
868 Default class constructor.
869 For internal use: do not call it in your code!
870 """
871
872 wx.Timer.__init__(self)
873 self._owner = owner
874
875
876 def Notify(self):
877 """The timer has expired."""
878
879 self._owner.OnRenameTimer()
880
881
882 # -----------------------------------------------------------------------------
883 # Auxiliary Classes: TreeTextCtrl
884 # This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item
885 # -----------------------------------------------------------------------------
886
887 class TreeTextCtrl(wx.TextCtrl):
888 """Control used for in-place edit."""
889
890 def __init__(self, owner, item=None):
891 """
892 Default class constructor.
893 For internal use: do not call it in your code!
894 """
895
896 self._owner = owner
897 self._itemEdited = item
898 self._startValue = item.GetText()
899 self._finished = False
900 self._aboutToFinish = False
901
902 w = self._itemEdited.GetWidth()
903 h = self._itemEdited.GetHeight()
904
905 wnd = self._itemEdited.GetWindow()
906 if wnd:
907 w = w - self._itemEdited.GetWindowSize()[0]
908 h = 0
909
910 x, y = self._owner.CalcScrolledPosition(item.GetX(), item.GetY())
911
912 image_h = 0
913 image_w = 0
914
915 image = item.GetCurrentImage()
916
917 if image != _NO_IMAGE:
918
919 if self._owner._imageListNormal:
920 image_w, image_h = self._owner._imageListNormal.GetSize(image)
921 image_w += 4
922
923 else:
924
925 raise "\n ERROR: You Must Create An Image List To Use Images!"
926
927 checkimage = item.GetCurrentCheckedImage()
928
929 if checkimage is not None:
930 wcheck, hcheck = self._owner._imageListCheck.GetSize(checkimage)
931 wcheck += 4
932 else:
933 wcheck = 0
934
935 if wnd:
936 h = max(hcheck, image_h)
937 dc = wx.ClientDC(self._owner)
938 h = max(h, dc.GetTextExtent("Aq")[1])
939 h = h + 2
940
941 # FIXME: what are all these hardcoded 4, 8 and 11s really?
942 x += image_w + wcheck
943 w -= image_w + 4 + wcheck
944
945 if wx.Platform == "__WXMAC__":
946 bs = self.DoGetBestSize()
947 # edit control height
948 if h > bs.y - 8:
949 diff = h - ( bs.y - 8 )
950 h -= diff
951 y += diff / 2
952
953 wx.TextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue,
954 wx.Point(x - 4, y), wx.Size(w + 15, h))
955
956 self.Bind(wx.EVT_CHAR, self.OnChar)
957 self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
958 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
959
960
961 def AcceptChanges(self):
962 """Accepts/refuses the changes made by the user."""
963
964 value = self.GetValue()
965
966 if value == self._startValue:
967 # nothing changed, always accept
968 # when an item remains unchanged, the owner
969 # needs to be notified that the user decided
970 # not to change the tree item label, and that
971 # the edit has been cancelled
972 self._owner.OnRenameCancelled(self._itemEdited)
973 return True
974
975 if not self._owner.OnRenameAccept(self._itemEdited, value):
976 # vetoed by the user
977 return False
978
979 # accepted, do rename the item
980 self._owner.SetItemText(self._itemEdited, value)
981
982 return True
983
984
985 def Finish(self):
986 """Finish editing."""
987
988 if not self._finished:
989
990 ## wxPendingDelete.Append(this)
991 self._finished = True
992 self._owner.SetFocusIgnoringChildren()
993 self._owner.ResetTextControl()
994
995
996 def OnChar(self, event):
997 """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
998
999 keycode = event.GetKeyCode()
1000
1001 if keycode == wx.WXK_RETURN:
1002 self._aboutToFinish = True
1003 # Notify the owner about the changes
1004 self.AcceptChanges()
1005 # Even if vetoed, close the control (consistent with MSW)
1006 wx.CallAfter(self.Finish)
1007
1008 elif keycode == wx.WXK_ESCAPE:
1009 self.StopEditing()
1010
1011 else:
1012 event.Skip()
1013
1014
1015 def OnKeyUp(self, event):
1016 """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
1017
1018 if not self._finished:
1019
1020 # auto-grow the textctrl:
1021 parentSize = self._owner.GetSize()
1022 myPos = self.GetPosition()
1023 mySize = self.GetSize()
1024
1025 sx, sy = self.GetTextExtent(self.GetValue() + "M")
1026 if myPos.x + sx > parentSize.x:
1027 sx = parentSize.x - myPos.x
1028 if mySize.x > sx:
1029 sx = mySize.x
1030
1031 self.SetSize((sx, -1))
1032
1033 event.Skip()
1034
1035
1036 def OnKillFocus(self, event):
1037 """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
1038
1039 # I commented out those lines, and everything seems to work fine.
1040 # But why in the world are these lines of code here? Maybe GTK
1041 # or MAC give troubles?
1042
1043 ## if not self._finished and not self._aboutToFinish:
1044 ##
1045 ## # We must finish regardless of success, otherwise we'll get
1046 ## # focus problems:
1047 ##
1048 ## if not self.AcceptChanges():
1049 ## self._owner.OnRenameCancelled(self._itemEdited)
1050
1051 # We must let the native text control handle focus, too, otherwise
1052 # it could have problems with the cursor (e.g., in wxGTK).
1053 event.Skip()
1054
1055
1056 def StopEditing(self):
1057 """Suddenly stops the editing."""
1058
1059 self._owner.OnRenameCancelled(self._itemEdited)
1060 self.Finish()
1061
1062
1063 def item(self):
1064 """Returns the item currently edited."""
1065
1066 return self._itemEdited
1067
1068
1069 # -----------------------------------------------------------------------------
1070 # Auxiliary Classes: TreeFindTimer
1071 # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
1072 # Sufficiently Long Time.
1073 # -----------------------------------------------------------------------------
1074
1075 class TreeFindTimer(wx.Timer):
1076 """
1077 Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
1078 for a sufficiently long time.
1079 """
1080
1081 def __init__(self, owner):
1082 """
1083 Default class constructor.
1084 For internal use: do not call it in your code!
1085 """
1086
1087 wx.Timer.__init__(self)
1088 self._owner = owner
1089
1090
1091 def Notify(self):
1092 """The timer has expired."""
1093
1094 self._owner._findPrefix = ""
1095
1096
1097 # -----------------------------------------------------------------------------
1098 # GenericTreeItem Implementation.
1099 # This Class Holds All The Information And Methods For Every Single Item In
1100 # CustomTreeCtrl.
1101 # -----------------------------------------------------------------------------
1102
1103 class GenericTreeItem:
1104 """
1105 This class holds all the information and methods for every single item in
1106 CustomTreeCtrl. No wx based.
1107 """
1108
1109 def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
1110 """
1111 Default class constructor.
1112 For internal use: do not call it in your code!
1113 """
1114
1115 # since there can be very many of these, we save size by chosing
1116 # the smallest representation for the elements and by ordering
1117 # the members to avoid padding.
1118 self._text = text # label to be rendered for item
1119 self._data = data # user-provided data
1120
1121 self._children = [] # list of children
1122 self._parent = parent # parent of this item
1123
1124 self._attr = None # attributes???
1125
1126 # tree ctrl images for the normal, selected, expanded and
1127 # expanded+selected states
1128 self._images = [-1, -1, -1, -1]
1129 self._images[TreeItemIcon_Normal] = image
1130 self._images[TreeItemIcon_Selected] = selImage
1131 self._images[TreeItemIcon_Expanded] = _NO_IMAGE
1132 self._images[TreeItemIcon_SelectedExpanded] = _NO_IMAGE
1133
1134 self._checkedimages = [None, None, None, None]
1135
1136 self._x = 0 # (virtual) offset from top
1137 self._y = 0 # (virtual) offset from left
1138 self._width = 0 # width of this item
1139 self._height = 0 # height of this item
1140
1141 self._isCollapsed = True
1142 self._hasHilight = False # same as focused
1143 self._hasPlus = False # used for item which doesn't have
1144 # children but has a [+] button
1145 self._isBold = False # render the label in bold font
1146 self._isItalic = False # render the label in italic font
1147 self._ownsAttr = False # delete attribute when done
1148 self._type = ct_type # item type: 0=normal, 1=check, 2=radio
1149 self._checked = False # only meaningful for check and radio
1150 self._enabled = True # flag to enable/disable an item
1151 self._hypertext = False # indicates if the item is hypertext
1152 self._visited = False # visited state for an hypertext item
1153
1154 if self._type > 0:
1155 # do not construct the array for normal items
1156 self._checkedimages[TreeItemIcon_Checked] = 0
1157 self._checkedimages[TreeItemIcon_NotChecked] = 1
1158 self._checkedimages[TreeItemIcon_Flagged] = 2
1159 self._checkedimages[TreeItemIcon_NotFlagged] = 3
1160
1161 if parent:
1162 if parent.GetType() == 2 and not parent.IsChecked():
1163 # if the node parent is a radio not enabled, we are disabled
1164 self._enabled = False
1165
1166 self._wnd = wnd # are we holding a window?
1167
1168 if wnd:
1169 if wnd.GetSizer(): # the window is a complex one hold by a sizer
1170 size = wnd.GetBestSize()
1171 else: # simple window, without sizers
1172 size = wnd.GetSize()
1173
1174 # We have to bind the wx.EVT_SET_FOCUS for the associated window
1175 # No other solution to handle the focus changing from an item in
1176 # CustomTreeCtrl and the window associated to an item
1177 # Do better strategies exist?
1178 self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
1179
1180 self._height = size.GetHeight() + 2
1181 self._width = size.GetWidth()
1182 self._windowsize = size
1183
1184 # We don't show the window if the item is collapsed
1185 if self._isCollapsed:
1186 self._wnd.Show(False)
1187
1188 # The window is enabled only if the item is enabled
1189 self._wnd.Enable(self._enabled)
1190 self._windowenabled = self._enabled
1191
1192
1193 def IsOk(self):
1194 """
1195 Returns whether the item is ok or not. Useless on Python, but added for
1196 backward compatibility with the C++ implementation.
1197 """
1198
1199 return True
1200
1201
1202 def GetChildren(self):
1203 """Returns the item's children."""
1204
1205 return self._children
1206
1207
1208 def GetText(self):
1209 """Returns the item text."""
1210
1211 return self._text
1212
1213
1214 def GetImage(self, which=TreeItemIcon_Normal):
1215 """Returns the item image for a particular state."""
1216
1217 return self._images[which]
1218
1219
1220 def GetCheckedImage(self, which=TreeItemIcon_Checked):
1221 """Returns the item check image. Meaningful only for radio & check items."""
1222
1223 return self._checkedimages[which]
1224
1225
1226 def GetData(self):
1227 """Returns the data associated to this item."""
1228
1229 return self._data
1230
1231
1232 def SetImage(self, image, which):
1233 """Sets the item image."""
1234
1235 self._images[which] = image
1236
1237
1238 def SetData(self, data):
1239 """Sets the data associated to this item."""
1240
1241 self._data = data
1242
1243
1244 def SetHasPlus(self, has=True):
1245 """Sets whether an item has the 'plus' button."""
1246
1247 self._hasPlus = has
1248
1249
1250 def SetBold(self, bold):
1251 """Sets the item font bold."""
1252
1253 self._isBold = bold
1254
1255
1256 def SetItalic(self, italic):
1257 """Sets the item font italic."""
1258
1259 self._isItalic = italic
1260
1261
1262 def GetX(self):
1263 """Returns the x position on an item in the ScrolledWindow."""
1264
1265 return self._x
1266
1267
1268 def GetY(self):
1269 """Returns the y position on an item in the ScrolledWindow."""
1270
1271 return self._y
1272
1273
1274 def SetX(self, x):
1275 """Sets the x position on an item in the ScrolledWindow."""
1276
1277 self._x = x
1278
1279
1280 def SetY(self, y):
1281 """Sets the y position on an item in the ScrolledWindow."""
1282
1283 self._y = y
1284
1285
1286 def GetHeight(self):
1287 """Returns the height of the item."""
1288
1289 return self._height
1290
1291
1292 def GetWidth(self):
1293 """Returns the width of the item."""
1294
1295 return self._width
1296
1297
1298 def SetHeight(self, h):
1299 """Sets the height of the item."""
1300
1301 self._height = h
1302
1303
1304 def SetWidth(self, w):
1305 """Sets the width of the item."""
1306
1307 self._width = w
1308
1309
1310 def SetWindow(self, wnd):
1311 """Sets the window associated to the item."""
1312
1313 self._wnd = wnd
1314
1315
1316 def GetWindow(self):
1317 """Returns the window associated to the item."""
1318
1319 return self._wnd
1320
1321
1322 def GetWindowEnabled(self):
1323 """Returns whether the associated window is enabled or not."""
1324
1325 if not self._wnd:
1326 raise "\nERROR: This Item Has No Window Associated"
1327
1328 return self._windowenabled
1329
1330
1331 def SetWindowEnabled(self, enable=True):
1332 """Sets whether the associated window is enabled or not."""
1333
1334 if not self._wnd:
1335 raise "\nERROR: This Item Has No Window Associated"
1336
1337 self._windowenabled = enable
1338 self._wnd.Enable(enable)
1339
1340
1341 def GetWindowSize(self):
1342 """Returns the associated window size."""
1343
1344 return self._windowsize
1345
1346
1347 def OnSetFocus(self, event):
1348 """Handles the wx.EVT_SET_FOCUS event for the associated window."""
1349
1350 treectrl = self._wnd.GetParent()
1351 select = treectrl.GetSelection()
1352
1353 # If the window is associated to an item that currently is selected
1354 # (has focus) we don't kill the focus. Otherwise we do it.
1355 if select != self:
1356 treectrl._hasFocus = False
1357 else:
1358 treectrl._hasFocus = True
1359
1360 event.Skip()
1361
1362
1363 def GetType(self):
1364 """
1365 Returns the item type. It should be one of:
1366 0: normal items
1367 1: checkbox item
1368 2: radiobutton item
1369 """
1370
1371 return self._type
1372
1373
1374 def SetHyperText(self, hyper=True):
1375 """Sets whether the item is hypertext or not."""
1376
1377 self._hypertext = hyper
1378
1379
1380 def SetVisited(self, visited=True):
1381 """Sets whether an hypertext item was visited or not."""
1382
1383 self._visited = visited
1384
1385
1386 def GetVisited(self):
1387 """Returns whether an hypertext item was visited or not."""
1388
1389 return self._visited
1390
1391
1392 def IsHyperText(self):
1393 """Returns whether the item is hypetext or not."""
1394
1395 return self._hypertext
1396
1397
1398 def GetParent(self):
1399 """Gets the item parent."""
1400
1401 return self._parent
1402
1403
1404 def Insert(self, child, index):
1405 """Inserts an item in the item children."""
1406
1407 self._children.insert(index, child)
1408
1409
1410 def Expand(self):
1411 """Expand the item."""
1412
1413 self._isCollapsed = False
1414
1415
1416 def Collapse(self):
1417 """Collapse the item."""
1418
1419 self._isCollapsed = True
1420
1421
1422 def SetHilight(self, set=True):
1423 """Sets the item focus/unfocus."""
1424
1425 self._hasHilight = set
1426
1427
1428 def HasChildren(self):
1429 """Returns whether the item has children or not."""
1430
1431 return len(self._children) > 0
1432
1433
1434 def IsSelected(self):
1435 """Returns whether the item is selected or not."""
1436
1437 return self._hasHilight != 0
1438
1439
1440 def IsExpanded(self):
1441 """Returns whether the item is expanded or not."""
1442
1443 return not self._isCollapsed
1444
1445
1446 def IsChecked(self):
1447 """Returns whether the item is checked or not."""
1448
1449 return self._checked
1450
1451
1452 def Check(self, checked=True):
1453 """Check an item. Meaningful only for check and radio items."""
1454
1455 self._checked = checked
1456
1457
1458 def HasPlus(self):
1459 """Returns whether the item has the plus button or not."""
1460
1461 return self._hasPlus or self.HasChildren()
1462
1463
1464 def IsBold(self):
1465 """Returns whether the item font is bold or not."""
1466
1467 return self._isBold != 0
1468
1469
1470 def IsItalic(self):
1471 """Returns whether the item font is italic or not."""
1472
1473 return self._isItalic != 0
1474
1475
1476 def Enable(self, enable=True):
1477 """Enables/disables the item."""
1478
1479 self._enabled = enable
1480
1481
1482 def IsEnabled(self):
1483 """Returns whether the item is enabled or not."""
1484
1485 return self._enabled
1486
1487
1488 def GetAttributes(self):
1489 """Returns the item attributes (font, colours)."""
1490
1491 return self._attr
1492
1493
1494 def Attr(self):
1495 """Creates a new attribute (font, colours)."""
1496
1497 if not self._attr:
1498
1499 self._attr = TreeItemAttr()
1500 self._ownsAttr = True
1501
1502 return self._attr
1503
1504
1505 def SetAttributes(self, attr):
1506 """Sets the item attributes (font, colours)."""
1507
1508 if self._ownsAttr:
1509 del self._attr
1510
1511 self._attr = attr
1512 self._ownsAttr = False
1513
1514
1515 def AssignAttributes(self, attr):
1516 """Assigns the item attributes (font, colours)."""
1517
1518 self.SetAttributes(attr)
1519 self._ownsAttr = True
1520
1521
1522 def DeleteChildren(self, tree):
1523 """Deletes the item children."""
1524
1525 for child in self._children:
1526 if tree:
1527 tree.SendDeleteEvent(child)
1528
1529 child.DeleteChildren(tree)
1530
1531 if child == tree._select_me:
1532 tree._select_me = None
1533
1534 # We have to destroy the associated window
1535 wnd = child.GetWindow()
1536 if wnd:
1537 wnd.Destroy()
1538 child._wnd = None
1539
1540 if child in tree._itemWithWindow:
1541 tree._itemWithWindow.remove(child)
1542
1543 del child
1544
1545 self._children = []
1546
1547
1548 def SetText(self, text):
1549 """Sets the item text."""
1550
1551 self._text = text
1552
1553
1554 def GetChildrenCount(self, recursively=True):
1555 """Gets the number of children."""
1556
1557 count = len(self._children)
1558
1559 if not recursively:
1560 return count
1561
1562 total = count
1563
1564 for n in xrange(count):
1565 total += self._children[n].GetChildrenCount()
1566
1567 return total
1568
1569
1570 def GetSize(self, x, y, theButton):
1571 """Returns the item size."""
1572
1573 bottomY = self._y + theButton.GetLineHeight(self)
1574
1575 if y < bottomY:
1576 y = bottomY
1577
1578 width = self._x + self._width
1579
1580 if x < width:
1581 x = width
1582
1583 if self.IsExpanded():
1584 for child in self._children:
1585 x, y = child.GetSize(x, y, theButton)
1586
1587 return x, y
1588
1589
1590 def HitTest(self, point, theCtrl, flags=0, level=0):
1591 """
1592 HitTest method for an item. Called from the main window HitTest.
1593 see the CustomTreeCtrl HitTest method for the flags explanation.
1594 """
1595
1596 # for a hidden root node, don't evaluate it, but do evaluate children
1597 if not (level == 0 and theCtrl.HasFlag(TR_HIDE_ROOT)):
1598
1599 # evaluate the item
1600 h = theCtrl.GetLineHeight(self)
1601
1602 if point.y > self._y and point.y < self._y + h:
1603
1604 y_mid = self._y + h/2
1605
1606 if point.y < y_mid:
1607 flags |= TREE_HITTEST_ONITEMUPPERPART
1608 else:
1609 flags |= TREE_HITTEST_ONITEMLOWERPART
1610
1611 xCross = self._x - theCtrl.GetSpacing()
1612
1613 if wx.Platform == "__WXMAC__":
1614 # according to the drawing code the triangels are drawn
1615 # at -4 , -4 from the position up to +10/+10 max
1616 if point.x > xCross-4 and point.x < xCross+10 and point.y > y_mid-4 and \
1617 point.y < y_mid+10 and self.HasPlus() and theCtrl.HasButtons():
1618
1619 flags |= TREE_HITTEST_ONITEMBUTTON
1620 return self, flags
1621 else:
1622 # 5 is the size of the plus sign
1623 if point.x > xCross-6 and point.x < xCross+6 and point.y > y_mid-6 and \
1624 point.y < y_mid+6 and self.HasPlus() and theCtrl.HasButtons():
1625
1626 flags |= TREE_HITTEST_ONITEMBUTTON
1627 return self, flags
1628
1629 if point.x >= self._x and point.x <= self._x + self._width:
1630
1631 image_w = -1
1632 wcheck = 0
1633
1634 # assuming every image (normal and selected) has the same size!
1635 if self.GetImage() != _NO_IMAGE and theCtrl._imageListNormal:
1636 image_w, image_h = theCtrl._imageListNormal.GetSize(self.GetImage())
1637
1638 if self.GetCheckedImage() is not None:
1639 wcheck, hcheck = theCtrl._imageListCheck.GetSize(self.GetCheckedImage())
1640
1641 if wcheck and point.x <= self._x + wcheck + 1:
1642 flags |= TREE_HITTEST_ONITEMCHECKICON
1643 return self, flags
1644
1645 if image_w != -1 and point.x <= self._x + wcheck + image_w + 1:
1646 flags |= TREE_HITTEST_ONITEMICON
1647 else:
1648 flags |= TREE_HITTEST_ONITEMLABEL
1649
1650 return self, flags
1651
1652 if point.x < self._x:
1653 flags |= TREE_HITTEST_ONITEMINDENT
1654 if point.x > self._x + self._width:
1655 flags |= TREE_HITTEST_ONITEMRIGHT
1656
1657 return self, flags
1658
1659 # if children are expanded, fall through to evaluate them
1660 if self._isCollapsed:
1661 return None, 0
1662
1663 # evaluate children
1664 for child in self._children:
1665 res, flags = child.HitTest(point, theCtrl, flags, level + 1)
1666 if res != None:
1667 return res, flags
1668
1669 return None, 0
1670
1671
1672 def GetCurrentImage(self):
1673 """Returns the current item image."""
1674
1675 image = _NO_IMAGE
1676
1677 if self.IsExpanded():
1678
1679 if self.IsSelected():
1680
1681 image = self.GetImage(TreeItemIcon_SelectedExpanded)
1682
1683 if image == _NO_IMAGE:
1684
1685 # we usually fall back to the normal item, but try just the
1686 # expanded one (and not selected) first in this case
1687 image = self.GetImage(TreeItemIcon_Expanded)
1688
1689 else: # not expanded
1690
1691 if self.IsSelected():
1692 image = self.GetImage(TreeItemIcon_Selected)
1693
1694 # maybe it doesn't have the specific image we want,
1695 # try the default one instead
1696 if image == _NO_IMAGE:
1697 image = self.GetImage()
1698
1699 return image
1700
1701
1702 def GetCurrentCheckedImage(self):
1703 """Returns the current item check image."""
1704
1705 if self._type == 0:
1706 return None
1707
1708 if self.IsChecked():
1709 if self._type == 1: # Checkbox
1710 return self._checkedimages[TreeItemIcon_Checked]
1711 else: # Radiobutton
1712 return self._checkedimages[TreeItemIcon_Flagged]
1713 else:
1714 if self._type == 1: # Checkbox
1715 return self._checkedimages[TreeItemIcon_NotChecked]
1716 else: # Radiobutton
1717 return self._checkedimages[TreeItemIcon_NotFlagged]
1718
1719
1720 def EventFlagsToSelType(style, shiftDown=False, ctrlDown=False):
1721 """
1722 Translate the key or mouse event flag to the type of selection we
1723 are dealing with.
1724 """
1725
1726 is_multiple = (style & TR_MULTIPLE) != 0
1727 extended_select = shiftDown and is_multiple
1728 unselect_others = not (extended_select or (ctrlDown and is_multiple))
1729
1730 return is_multiple, extended_select, unselect_others
1731
1732
1733 # -----------------------------------------------------------------------------
1734 # CustomTreeCtrl Main Implementation.
1735 # This Is The Main Class.
1736 # -----------------------------------------------------------------------------
1737
1738 class CustomTreeCtrl(wx.ScrolledWindow):
1739
1740 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
1741 style=0, ctstyle=TR_DEFAULT_STYLE, validator=wx.DefaultValidator,
1742 name="CustomTreeCtrl"):
1743 """
1744 Default class constructor.
1745
1746 parent: parent window. Must not be none.
1747
1748 id: window identifier. A value of -1 indicates a default value.
1749
1750 pos: window position.
1751
1752 size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
1753
1754 style: the underlying wx.ScrolledWindow style
1755
1756 ctstyle: CustomTreeCtrl window style. This can be one of:
1757 TR_NO_BUTTONS
1758 TR_HAS_BUTTONS # draw collapsed/expanded btns
1759 TR_NO_LINES # don't draw lines at all
1760 TR_LINES_AT_ROOT # connect top-level nodes
1761 TR_TWIST_BUTTONS # draw mac-like twist buttons
1762 TR_SINGLE # single selection mode
1763 TR_MULTIPLE # can select multiple items
1764 TR_EXTENDED # todo: allow extended selection
1765 TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
1766 TR_EDIT_LABELS # can edit item labels
1767 TR_ROW_LINES # put border around items
1768 TR_HIDE_ROOT # don't display root node
1769 TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
1770 TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
1771 TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
1772
1773 validator: window validator.
1774
1775 name: window name.
1776 """
1777
1778 self._current = self._key_current = self._anchor = self._select_me = None
1779 self._hasFocus = False
1780 self._dirty = False
1781
1782 # Default line height: it will soon be changed
1783 self._lineHeight = 10
1784 # Item indent wrt parent
1785 self._indent = 15
1786 # item horizontal spacing between the start and the text
1787 self._spacing = 18
1788
1789 # Brushes for focused/unfocused items (also gradient type)
1790 self._hilightBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
1791 btnshadow = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
1792 self._hilightUnfocusedBrush = wx.Brush(btnshadow)
1793 r, g, b = btnshadow.Red(), btnshadow.Green(), btnshadow.Blue()
1794 backcolour = ((r >> 1) - 20, (g >> 1) - 20, (b >> 1) - 20)
1795 backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2])
1796 self._hilightUnfocusedBrush2 = wx.Brush(backcolour)
1797
1798 # image list for icons
1799 self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = None
1800 self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = False
1801
1802 # Drag and drop initial settings
1803 self._dragCount = 0
1804 self._countDrag = 0
1805 self._isDragging = False
1806 self._dropTarget = self._oldSelection = None
1807 self._dragImage = None
1808 self._underMouse = None
1809
1810 # TextCtrl initial settings for editable items
1811 self._textCtrl = None
1812 self._renameTimer = None
1813
1814 # This one allows us to handle Freeze() and Thaw() calls
1815 self._freezeCount = 0
1816
1817 self._findPrefix = ""
1818 self._findTimer = None
1819
1820 self._dropEffectAboveItem = False
1821 self._lastOnSame = False
1822
1823 # Default normal and bold fonts for an item
1824 self._hasFont = True
1825 self._normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
1826 self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
1827 self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(),
1828 self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
1829
1830
1831 # Hyperlinks things
1832 self._hypertextfont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
1833 self._normalFont.GetStyle(), wx.NORMAL, True,
1834 self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
1835 self._hypertextnewcolour = wx.BLUE
1836 self._hypertextvisitedcolour = wx.Colour(200, 47, 200)
1837 self._isonhyperlink = False
1838
1839 # Default CustomTreeCtrl background colour.
1840 self._backgroundColour = wx.WHITE
1841
1842 # Background image settings
1843 self._backgroundImage = None
1844 self._imageStretchStyle = _StyleTile
1845
1846 # Disabled items colour
1847 self._disabledColour = wx.Colour(180, 180, 180)
1848
1849 # Gradient selection colours
1850 self._firstcolour = color= wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
1851 self._secondcolour = wx.WHITE
1852 self._usegradients = False
1853 self._gradientstyle = 0 # Horizontal Gradient
1854
1855 # Vista Selection Styles
1856 self._vistaselection = False
1857
1858 # Connection lines style
1859 if wx.Platform != "__WXMAC__":
1860 self._dottedPen = wx.Pen("grey", 1, wx.USER_DASH)
1861 self._dottedPen.SetDashes([1,1])
1862 self._dottedPen.SetCap(wx.CAP_BUTT)
1863 else:
1864 self._dottedPen = wx.Pen("grey", 1)
1865
1866 # Pen Used To Draw The Border Around Selected Items
1867 self._borderPen = wx.BLACK_PEN
1868 self._cursor = wx.StockCursor(wx.CURSOR_ARROW)
1869
1870 # For Appended Windows
1871 self._hasWindows = False
1872 self._itemWithWindow = []
1873
1874 if wx.Platform == "__WXMAC__":
1875
1876 platform, major, minor = wx.GetOsVersion()
1877
1878 ctstyle &= ~TR_LINES_AT_ROOT
1879 ctstyle |= TR_NO_LINES
1880
1881 if major < 10:
1882 ctstyle |= TR_ROW_LINES
1883
1884 self._windowStyle = ctstyle
1885
1886 # Create the default check image list
1887 self.SetImageListCheck(13, 13)
1888
1889 # A constant to use my translation of RendererNative.DrawTreeItemButton
1890 # if the wxPython version is less than 2.6.2.1.
1891 if wx.VERSION_STRING < "2.6.2.1":
1892 self._drawingfunction = DrawTreeItemButton
1893 else:
1894 self._drawingfunction = wx.RendererNative.Get().DrawTreeItemButton
1895
1896 # Create our container... at last!
1897 wx.ScrolledWindow.__init__(self, parent, id, pos, size, style|wx.HSCROLL|wx.VSCROLL, name)
1898
1899 # If the tree display has no buttons, but does have
1900 # connecting lines, we can use a narrower layout.
1901 # It may not be a good idea to force this...
1902 if not self.HasButtons() and not self.HasFlag(TR_NO_LINES):
1903 self._indent= 10
1904 self._spacing = 10
1905
1906 self.SetValidator(validator)
1907
1908 attr = self.GetDefaultAttributes()
1909 self.SetOwnForegroundColour(attr.colFg)
1910 self.SetOwnBackgroundColour(wx.WHITE)
1911
1912 if not self._hasFont:
1913 self.SetOwnFont(attr.font)
1914
1915 self.SetSize(size)
1916
1917 # Bind the events
1918 self.Bind(wx.EVT_PAINT, self.OnPaint)
1919 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
1920 self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
1921 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
1922 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
1923 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
1924 self.Bind(EVT_TREE_ITEM_GETTOOLTIP, self.OnGetToolTip)
1925 self.Bind(wx.EVT_IDLE, self.OnInternalIdle)
1926 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
1927
1928 # Sets the focus to ourselves: this is useful if you have items
1929 # with associated widgets.
1930 self.SetFocus()
1931
1932 return True
1933
1934
1935 def OnDestroy(self, event):
1936 """Handles the wx.EVT_WINDOW_DESTROY event."""
1937
1938 # Here there may be something I miss... do I have to destroy
1939 # something else?
1940 if self._renameTimer and self._renameTimer.IsRunning():
1941 self._renameTimer.Stop()
1942 del self._renameTimer
1943
1944 if self._findTimer and self._findTimer.IsRunning():
1945 self._findTimer.Stop()
1946 del self._findTimer
1947
1948 event.Skip()
1949
1950
1951 def GetCount(self):
1952 """Returns the global number of items in the tree."""
1953
1954 if not self._anchor:
1955 # the tree is empty
1956 return 0
1957
1958 count = self._anchor.GetChildrenCount()
1959
1960 if not self.HasFlag(TR_HIDE_ROOT):
1961 # take the root itself into account
1962 count = count + 1
1963
1964 return count
1965
1966
1967 def GetIndent(self):
1968 """Returns the item indentation."""
1969
1970 return self._indent
1971
1972
1973 def GetSpacing(self):
1974 """Returns the spacing between the start and the text."""
1975
1976 return self._spacing
1977
1978
1979 def GetRootItem(self):
1980 """Returns the root item."""
1981
1982 return self._anchor
1983
1984
1985 def GetSelection(self):
1986 """Returns the current selection: TR_SINGLE only."""
1987
1988 return self._current
1989
1990
1991 def ToggleItemSelection(self, item):
1992 """Toggles the item selection."""
1993
1994 if not item:
1995 raise "\nERROR: Invalid Tree Item. "
1996
1997 self.SelectItem(item, not self.IsSelected(item))
1998
1999
2000 def EnableChildren(self, item, enable=True):
2001 """Enables/disables item children. Used internally."""
2002
2003 torefresh = False
2004 if item.IsExpanded():
2005 torefresh = True
2006
2007 if item.GetType() == 2 and enable and not item.IsChecked():
2008 # We hit a radiobutton item not checked, we don't want to
2009 # enable the children
2010 return
2011
2012 child, cookie = self.GetFirstChild(item)
2013 while child:
2014 self.EnableItem(child, enable, torefresh=torefresh)
2015 # Recurse on tree
2016 if child.GetType != 2 or (child.GetType() == 2 and item.IsChecked()):
2017 self.EnableChildren(child, enable)
2018 (child, cookie) = self.GetNextChild(item, cookie)
2019
2020
2021 def EnableItem(self, item, enable=True, torefresh=True):
2022 """Enables/disables an item."""
2023
2024 if not item:
2025 raise "\nERROR: Invalid Tree Item. "
2026
2027 if item.IsEnabled() == enable:
2028 return
2029
2030 if not enable and item.IsSelected():
2031 self.SelectItem(item, False)
2032
2033 item.Enable(enable)
2034 wnd = item.GetWindow()
2035
2036 # Handles the eventual window associated to the item
2037 if wnd:
2038 wndenable = item.GetWindowEnabled()
2039 if enable:
2040 if wndenable:
2041 wnd.Enable(enable)
2042 else:
2043 wnd.Enable(enable)
2044
2045 if torefresh:
2046 # We have to refresh the item line
2047 dc = wx.ClientDC(self)
2048 self.CalculateSize(item, dc)
2049 self.RefreshLine(item)
2050
2051
2052 def IsEnabled(self, item):
2053 """Returns whether an item is enabled or disabled."""
2054
2055 if not item:
2056 raise "\nERROR: Invalid Tree Item. "
2057
2058 return item.IsEnabled()
2059
2060
2061 def SetDisabledColour(self, colour):
2062 """Sets the items disabled colour."""
2063
2064 self._disabledColour = colour
2065 self._dirty = True
2066
2067
2068 def GetDisabledColour(self):
2069 """Returns the items disabled colour."""
2070
2071 return self._disabledColour
2072
2073
2074 def IsItemChecked(self, item):
2075 """Returns whether an item is checked or not."""
2076
2077 if not item:
2078 raise "\nERROR: Invalid Tree Item. "
2079
2080 return item.IsChecked()
2081
2082
2083 def CheckItem2(self, item, checked=True, torefresh=False):
2084 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2085
2086 if item.GetType() == 0:
2087 return
2088
2089 item.Check(checked)
2090
2091 if torefresh:
2092 dc = wx.ClientDC(self)
2093 self.CalculateSize(item, dc)
2094 self.RefreshLine(item)
2095
2096
2097 def UnCheckRadioParent(self, item, checked=False):
2098 """Used internally to handle radio node parent correctly."""
2099
2100 e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
2101 e.SetItem(item)
2102 e.SetEventObject(self)
2103
2104 if self.GetEventHandler().ProcessEvent(e):
2105 return False
2106
2107 item.Check(checked)
2108 dc = wx.ClientDC(self)
2109 self.RefreshLine(item)
2110 self.EnableChildren(item, checked)
2111 e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
2112 e.SetItem(item)
2113 e.SetEventObject(self)
2114 self.GetEventHandler().ProcessEvent(e)
2115
2116 return True
2117
2118
2119 def CheckItem(self, item, checked=True):
2120 """
2121 Actually checks/uncheks an item, sending (eventually) the two
2122 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2123 """
2124
2125 if not item:
2126 raise "\nERROR: Invalid Tree Item. "
2127
2128 # Should we raise an error here?!?
2129 if item.GetType() == 0:
2130 return
2131
2132 if item.GetType() == 2: # it's a radio button
2133 if not checked and item.IsChecked(): # Try To Unckeck?
2134 if item.HasChildren():
2135 self.UnCheckRadioParent(item, checked)
2136 return
2137 else:
2138 if not self.UnCheckRadioParent(item, checked):
2139 return
2140
2141 self.CheckSameLevel(item, False)
2142 return
2143
2144 # Radiobuttons are done, let's handle checkbuttons...
2145 e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
2146 e.SetItem(item)
2147 e.SetEventObject(self)
2148
2149 if self.GetEventHandler().ProcessEvent(e):
2150 # Blocked by user
2151 return
2152
2153 item.Check(checked)
2154 dc = wx.ClientDC(self)
2155 self.RefreshLine(item)
2156
2157 if self._windowStyle & TR_AUTO_CHECK_CHILD:
2158 ischeck = self.IsItemChecked(item)
2159 self.AutoCheckChild(item, ischeck)
2160 elif self._windowStyle & TR_AUTO_TOGGLE_CHILD:
2161 self.AutoToggleChild(item)
2162
2163 e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
2164 e.SetItem(item)
2165 e.SetEventObject(self)
2166 self.GetEventHandler().ProcessEvent(e)
2167
2168
2169 def AutoToggleChild(self, item):
2170 """Transverses the tree and toggles the items. Meaningful only for check items."""
2171
2172 if not item:
2173 raise "\nERROR: Invalid Tree Item. "
2174
2175 child, cookie = self.GetFirstChild(item)
2176
2177 torefresh = False
2178 if item.IsExpanded():
2179 torefresh = True
2180
2181 # Recurse on tree
2182 while child:
2183 if child.GetType() == 1 and child.IsEnabled():
2184 self.CheckItem2(child, not child.IsChecked(), torefresh=torefresh)
2185 self.AutoToggleChild(child)
2186 (child, cookie) = self.GetNextChild(item, cookie)
2187
2188
2189 def AutoCheckChild(self, item, checked):
2190 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2191
2192 if not item:
2193 raise "\nERROR: Invalid Tree Item. "
2194
2195 (child, cookie) = self.GetFirstChild(item)
2196
2197 torefresh = False
2198 if item.IsExpanded():
2199 torefresh = True
2200
2201 while child:
2202 if child.GetType() == 1 and child.IsEnabled():
2203 self.CheckItem2(child, checked, torefresh=torefresh)
2204 self.AutoCheckChild(child, checked)
2205 (child, cookie) = self.GetNextChild(item, cookie)
2206
2207
2208 def CheckChilds(self, item, checked=True):
2209 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2210
2211 if not item:
2212 raise "\nERROR: Invalid Tree Item. "
2213
2214 if checked == None:
2215 self.AutoToggleChild(item)
2216 else:
2217 self.AutoCheckChild(item, checked)
2218
2219
2220 def CheckSameLevel(self, item, checked=False):
2221 """
2222 Uncheck radio items which are on the same level of the checked one.
2223 Used internally.
2224 """
2225
2226 parent = item.GetParent()
2227
2228 if not parent:
2229 return
2230
2231 torefresh = False
2232 if parent.IsExpanded():
2233 torefresh = True
2234
2235 (child, cookie) = self.GetFirstChild(parent)
2236 while child:
2237 if child.GetType() == 2 and child != item:
2238 self.CheckItem2(child, checked, torefresh=torefresh)
2239 if child.GetType != 2 or (child.GetType() == 2 and child.IsChecked()):
2240 self.EnableChildren(child, checked)
2241 (child, cookie) = self.GetNextChild(parent, cookie)
2242
2243
2244 def EditLabel(self, item):
2245 """Starts editing an item label."""
2246
2247 if not item:
2248 raise "\nERROR: Invalid Tree Item. "
2249
2250 self.Edit(item)
2251
2252
2253 def ShouldInheritColours(self):
2254 """We don't inherit colours from anyone."""
2255
2256 return False
2257
2258
2259 def SetIndent(self, indent):
2260 """Sets item indentation."""
2261
2262 self._indent = indent
2263 self._dirty = True
2264
2265
2266 def SetSpacing(self, spacing):
2267 """Sets item spacing."""
2268
2269 self._spacing = spacing
2270 self._dirty = True
2271
2272
2273 def HasFlag(self, flag):
2274 """Returns whether CustomTreeCtrl has a flag."""
2275
2276 return self._windowStyle & flag
2277
2278
2279 def HasChildren(self, item):
2280 """Returns whether an item has children or not."""
2281
2282 if not item:
2283 raise "\nERROR: Invalid Tree Item. "
2284
2285 return len(item.GetChildren()) > 0
2286
2287
2288 def GetChildrenCount(self, item, recursively=True):
2289 """Gets the item children count."""
2290
2291 if not item:
2292 raise "\nERROR: Invalid Tree Item. "
2293
2294 return item.GetChildrenCount(recursively)
2295
2296
2297 def SetTreeStyle(self, styles):
2298 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2299
2300 # Do not try to expand the root node if it hasn't been created yet
2301 if self._anchor and not self.HasFlag(TR_HIDE_ROOT) and styles & TR_HIDE_ROOT:
2302
2303 # if we will hide the root, make sure children are visible
2304 self._anchor.SetHasPlus()
2305 self._anchor.Expand()
2306 self.CalculatePositions()
2307
2308 # right now, just sets the styles. Eventually, we may
2309 # want to update the inherited styles, but right now
2310 # none of the parents has updatable styles
2311
2312 if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE):
2313 selections = self.GetSelections()
2314 for select in selections[0:-1]:
2315 self.SelectItem(select, False)
2316
2317 self._windowStyle = styles
2318 self._dirty = True
2319
2320
2321 def GetTreeStyle(self):
2322 """Returns the CustomTreeCtrl style."""
2323
2324 return self._windowStyle
2325
2326
2327 def HasButtons(self):
2328 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2329
2330 return self.HasFlag(TR_HAS_BUTTONS)
2331
2332
2333 # -----------------------------------------------------------------------------
2334 # functions to work with tree items
2335 # -----------------------------------------------------------------------------
2336
2337 def GetItemText(self, item):
2338 """Returns the item text."""
2339
2340 if not item:
2341 raise "\nERROR: Invalid Tree Item. "
2342
2343 return item.GetText()
2344
2345
2346 def GetItemImage(self, item, which):
2347 """Returns the item image."""
2348
2349 if not item:
2350 raise "\nERROR: Invalid Tree Item. "
2351
2352 return item.GetImage(which)
2353
2354
2355 def GetPyData(self, item):
2356 """Returns the data associated to an item."""
2357
2358 if not item:
2359 raise "\nERROR: Invalid Tree Item. "
2360
2361 return item.GetData()
2362
2363 GetItemPyData = GetPyData
2364
2365
2366 def GetItemTextColour(self, item):
2367 """Returns the item text colour."""
2368
2369 if not item:
2370 raise "\nERROR: Invalid Tree Item. "
2371
2372 return item.Attr().GetTextColour()
2373
2374
2375 def GetItemBackgroundColour(self, item):
2376 """Returns the item background colour."""
2377
2378 if not item:
2379 raise "\nERROR: Invalid Tree Item. "
2380
2381 return item.Attr().GetBackgroundColour()
2382
2383
2384 def GetItemFont(self, item):
2385 """Returns the item font."""
2386
2387 if not item:
2388 raise "\nERROR: Invalid Tree Item. "
2389
2390 return item.Attr().GetFont()
2391
2392
2393 def IsItemHyperText(self, item):
2394 """Returns whether an item is hypertext or not."""
2395
2396 if not item:
2397 raise "\nERROR: Invalid Tree Item. "
2398
2399 return item.IsHyperText()
2400
2401
2402 def SetItemText(self, item, text):
2403 """Sets the item text."""
2404
2405 if not item:
2406 raise "\nERROR: Invalid Tree Item. "
2407
2408 dc = wx.ClientDC(self)
2409 item.SetText(text)
2410 self.CalculateSize(item, dc)
2411 self.RefreshLine(item)
2412
2413
2414 def SetItemImage(self, item, image, which=TreeItemIcon_Normal):
2415 """Sets the item image, depending on the item state."""
2416
2417 if not item:
2418 raise "\nERROR: Invalid Tree Item. "
2419
2420 item.SetImage(image, which)
2421
2422 dc = wx.ClientDC(self)
2423 self.CalculateSize(item, dc)
2424 self.RefreshLine(item)
2425
2426
2427 def SetPyData(self, item, data):
2428 """Sets the data associated to an item."""
2429
2430 if not item:
2431 raise "\nERROR: Invalid Tree Item. "
2432
2433 item.SetData(data)
2434
2435 SetItemPyData = SetPyData
2436
2437
2438 def SetItemHasChildren(self, item, has=True):
2439 """Forces the appearance of the button next to the item."""
2440
2441 if not item:
2442 raise "\nERROR: Invalid Tree Item. "
2443
2444 item.SetHasPlus(has)
2445 self.RefreshLine(item)
2446
2447
2448 def SetItemBold(self, item, bold=True):
2449 """Sets the item font bold/unbold."""
2450
2451 if not item:
2452 raise "\nERROR: Invalid Tree Item. "
2453
2454 # avoid redrawing the tree if no real change
2455 if item.IsBold() != bold:
2456 item.SetBold(bold)
2457 self._dirty = True
2458
2459
2460 def SetItemItalic(self, item, italic=True):
2461 """Sets the item font italic/non-italic."""
2462
2463 if not item:
2464 raise "\nERROR: Invalid Tree Item. "
2465
2466 if item.IsItalic() != italic:
2467 itemFont = self.GetItemFont(item)
2468 if itemFont != wx.NullFont:
2469 style = wx.ITALIC
2470 if not italic:
2471 style = ~style
2472
2473 item.SetItalic(italic)
2474 itemFont.SetStyle(style)
2475 self.SetItemFont(item, itemFont)
2476 self._dirty = True
2477
2478
2479 def SetItemDropHighlight(self, item, highlight=True):
2480 """
2481 Gives the item the visual feedback for drag and drop operations.
2482 This is useful when something is dragged from outside the CustomTreeCtrl.
2483 """
2484
2485 if not item:
2486 raise "\nERROR: Invalid Tree Item. "
2487
2488 if highlight:
2489 bg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
2490 fg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
2491
2492 item.Attr().SetTextColour(fg)
2493 item.Attr.SetBackgroundColour(bg)
2494 self.RefreshLine(item)
2495
2496
2497 def SetItemTextColour(self, item, col):
2498 """Sets the item text colour."""
2499
2500 if not item:
2501 raise "\nERROR: Invalid Tree Item. "
2502
2503 if self.GetItemTextColour(item) == col:
2504 return
2505
2506 item.Attr().SetTextColour(col)
2507 self.RefreshLine(item)
2508
2509
2510 def SetItemBackgroundColour(self, item, col):
2511 """Sets the item background colour."""
2512
2513 if not item:
2514 raise "\nERROR: Invalid Tree Item. "
2515
2516 item.Attr().SetBackgroundColour(col)
2517 self.RefreshLine(item)
2518
2519
2520 def SetItemHyperText(self, item, hyper=True):
2521 """Sets whether the item is hypertext or not."""
2522
2523 if not item:
2524 raise "\nERROR: Invalid Tree Item. "
2525
2526 item.SetHyperText(hyper)
2527 self.RefreshLine(item)
2528
2529
2530 def SetItemFont(self, item, font):
2531 """Sets the item font."""
2532
2533 if not item:
2534 raise "\nERROR: Invalid Tree Item. "
2535
2536 if self.GetItemFont(item) == font:
2537 return
2538
2539 item.Attr().SetFont(font)
2540 self._dirty = True
2541
2542
2543 def SetFont(self, font):
2544 """Sets the CustomTreeCtrl font."""
2545
2546 wx.ScrolledWindow.SetFont(self, font)
2547
2548 self._normalFont = font
2549 self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
2550 self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(),
2551 self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
2552
2553 return True
2554
2555
2556 def GetHyperTextFont(self):
2557 """Returns the font used to render an hypertext item."""
2558
2559 return self._hypertextfont
2560
2561
2562 def SetHyperTextFont(self, font):
2563 """Sets the font used to render an hypertext item."""
2564
2565 self._hypertextfont = font
2566 self._dirty = True
2567
2568
2569 def SetHyperTextNewColour(self, colour):
2570 """Sets the colour used to render a non-visited hypertext item."""
2571
2572 self._hypertextnewcolour = colour
2573 self._dirty = True
2574
2575
2576 def GetHyperTextNewColour(self):
2577 """Returns the colour used to render a non-visited hypertext item."""
2578
2579 return self._hypertextnewcolour
2580
2581
2582 def SetHyperTextVisitedColour(self, colour):
2583 """Sets the colour used to render a visited hypertext item."""
2584
2585 self._hypertextvisitedcolour = colour
2586 self._dirty = True
2587
2588
2589 def GetHyperTextVisitedColour(self):
2590 """Returns the colour used to render a visited hypertext item."""
2591
2592 return self._hypertextvisitedcolour
2593
2594
2595 def SetItemVisited(self, item, visited=True):
2596 """Sets whether an hypertext item was visited."""
2597
2598 if not item:
2599 raise "\nERROR: Invalid Tree Item. "
2600
2601 item.SetVisited(visited)
2602 self.RefreshLine(item)
2603
2604
2605 def GetItemVisited(self, item):
2606 """Returns whether an hypertext item was visited."""
2607
2608 if not item:
2609 raise "\nERROR: Invalid Tree Item. "
2610
2611 return item.GetVisited()
2612
2613
2614 def SetHilightFocusColour(self, colour):
2615 """
2616 Sets the colour used to highlight focused selected items.
2617 This is applied only if gradient and Windows Vista styles are disabled.
2618 """
2619
2620 self._hilightBrush = wx.Brush(colour)
2621 self.RefreshSelected()
2622
2623
2624 def SetHilightNonFocusColour(self, colour):
2625 """
2626 Sets the colour used to highlight unfocused selected items.
2627 This is applied only if gradient and Windows Vista styles are disabled.
2628 """
2629
2630 self._hilightUnfocusedBrush = wx.Brush(colour)
2631 self.RefreshSelected()
2632
2633
2634 def GetHilightFocusColour(self):
2635 """
2636 Returns the colour used to highlight focused selected items.
2637 This is applied only if gradient and Windows Vista styles are disabled.
2638 """
2639
2640 return self._hilightBrush.GetColour()
2641
2642
2643 def GetHilightNonFocusColour(self):
2644 """
2645 Returns the colour used to highlight unfocused selected items.
2646 This is applied only if gradient and Windows Vista styles are disabled.
2647 """
2648
2649 return self._hilightUnfocusedBrush.GetColour()
2650
2651
2652 def SetFirstGradientColour(self, colour=None):
2653 """Sets the first gradient colour."""
2654
2655 if colour is None:
2656 colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
2657
2658 self._firstcolour = colour
2659 if self._usegradients:
2660 self.RefreshSelected()
2661
2662
2663 def SetSecondGradientColour(self, colour=None):
2664 """Sets the second gradient colour."""
2665
2666 if colour is None:
2667 # No colour given, generate a slightly darker from the
2668 # CustomTreeCtrl background colour
2669 color = self.GetBackgroundColour()
2670 r, g, b = int(color.Red()), int(color.Green()), int(color.Blue())
2671 color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
2672 colour = wx.Colour(color[0], color[1], color[2])
2673
2674 self._secondcolour = colour
2675
2676 if self._usegradients:
2677 self.RefreshSelected()
2678
2679
2680 def GetFirstGradientColour(self):
2681 """Returns the first gradient colour."""
2682
2683 return self._firstcolour
2684
2685
2686 def GetSecondGradientColour(self):
2687 """Returns the second gradient colour."""
2688
2689 return self._secondcolour
2690
2691
2692 def EnableSelectionGradient(self, enable=True):
2693 """Globally enables/disables drawing of gradient selection."""
2694
2695 self._usegradients = enable
2696 self._vistaselection = False
2697 self.RefreshSelected()
2698
2699
2700 def SetGradientStyle(self, vertical=0):
2701 """
2702 Sets the gradient style:
2703 0: horizontal gradient
2704 1: vertical gradient
2705 """
2706
2707 # 0 = Horizontal, 1 = Vertical
2708 self._gradientstyle = vertical
2709
2710 if self._usegradients:
2711 self.RefreshSelected()
2712
2713
2714 def GetGradientStyle(self):
2715 """
2716 Returns the gradient style:
2717 0: horizontal gradient
2718 1: vertical gradient
2719 """
2720
2721 return self._gradientstyle
2722
2723
2724 def EnableSelectionVista(self, enable=True):
2725 """Globally enables/disables drawing of Windows Vista selection."""
2726
2727 self._usegradients = False
2728 self._vistaselection = enable
2729 self.RefreshSelected()
2730
2731
2732 def SetBorderPen(self, pen):
2733 """
2734 Sets the pen used to draw the selected item border.
2735 The border pen is not used if the Windows Vista style is applied.
2736 """
2737
2738 self._borderPen = pen
2739 self.RefreshSelected()
2740
2741
2742 def GetBorderPen(self):
2743 """
2744 Returns the pen used to draw the selected item border.
2745 The border pen is not used if the Windows Vista style is applied.
2746 """
2747
2748 return self._borderPen
2749
2750
2751 def SetConnectionPen(self, pen):
2752 """Sets the pen used to draw the connecting lines between items."""
2753
2754 self._dottedPen = pen
2755 self._dirty = True
2756
2757
2758 def GetConnectionPen(self):
2759 """Returns the pen used to draw the connecting lines between items."""
2760
2761 return self._dottedPen
2762
2763
2764 def SetBackgroundImage(self, image):
2765 """Sets the CustomTreeCtrl background image (can be none)."""
2766
2767 self._backgroundImage = image
2768 self.Refresh()
2769
2770
2771 def GetBackgroundImage(self):
2772 """Returns the CustomTreeCtrl background image (can be none)."""
2773
2774 return self._backgroundImage
2775
2776
2777 def GetItemWindow(self, item):
2778 """Returns the window associated to the item (if any)."""
2779
2780 if not item:
2781 raise "\nERROR: Invalid Item"
2782
2783 return item.GetWindow()
2784
2785
2786 def GetItemWindowEnabled(self, item):
2787 """Returns whether the window associated to the item is enabled."""
2788
2789 if not item:
2790 raise "\nERROR: Invalid Item"
2791
2792 return item.GetWindowEnabled()
2793
2794
2795 def SetItemWindowEnabled(self, item, enable=True):
2796 """Enables/disables the window associated to the item."""
2797
2798 if not item:
2799 raise "\nERROR: Invalid Item"
2800
2801 item.SetWindowEnabled(enable)
2802
2803
2804 def GetItemType(self, item):
2805 """
2806 Returns the item type:
2807 0: normal
2808 1: checkbox item
2809 2: radiobutton item
2810 """
2811
2812 if not item:
2813 raise "\nERROR: Invalid Item"
2814
2815 return item.GetType()
2816
2817 # -----------------------------------------------------------------------------
2818 # item status inquiries
2819 # -----------------------------------------------------------------------------
2820
2821 def IsVisible(self, item):
2822 """Returns whether the item is visible or not."""
2823
2824 if not item:
2825 raise "\nERROR: Invalid Tree Item. "
2826
2827 # An item is only visible if it's not a descendant of a collapsed item
2828 parent = item.GetParent()
2829
2830 while parent:
2831
2832 if not parent.IsExpanded():
2833 return False
2834
2835 parent = parent.GetParent()
2836
2837 startX, startY = self.GetViewStart()
2838 clientSize = self.GetClientSize()
2839
2840 rect = self.GetBoundingRect(item)
2841
2842 if not rect:
2843 return False
2844 if rect.GetWidth() == 0 or rect.GetHeight() == 0:
2845 return False
2846 if rect.GetBottom() < 0 or rect.GetTop() > clientSize.y:
2847 return False
2848 if rect.GetRight() < 0 or rect.GetLeft() > clientSize.x:
2849 return False
2850
2851 return True
2852
2853
2854 def ItemHasChildren(self, item):
2855 """Returns whether the item has children or not."""
2856
2857 if not item:
2858 raise "\nERROR: Invalid Tree Item. "
2859
2860 # consider that the item does have children if it has the "+" button: it
2861 # might not have them (if it had never been expanded yet) but then it
2862 # could have them as well and it's better to err on this side rather than
2863 # disabling some operations which are restricted to the items with
2864 # children for an item which does have them
2865 return item.HasPlus()
2866
2867
2868 def IsExpanded(self, item):
2869 """Returns whether the item is expanded or not."""
2870
2871 if not item:
2872 raise "\nERROR: Invalid Tree Item. "
2873
2874 return item.IsExpanded()
2875
2876
2877 def IsSelected(self, item):
2878 """Returns whether the item is selected or not."""
2879
2880 if not item:
2881 raise "\nERROR: Invalid Tree Item. "
2882
2883 return item.IsSelected()
2884
2885
2886 def IsBold(self, item):
2887 """Returns whether the item font is bold or not."""
2888
2889 if not item:
2890 raise "\nERROR: Invalid Tree Item. "
2891
2892 return item.IsBold()
2893
2894
2895 def IsItalic(self, item):
2896 """Returns whether the item font is italic or not."""
2897
2898 if not item:
2899 raise "\nERROR: Invalid Tree Item. "
2900
2901 return item.IsItalic()
2902
2903
2904 # -----------------------------------------------------------------------------
2905 # navigation
2906 # -----------------------------------------------------------------------------
2907
2908 def GetItemParent(self, item):
2909 """Gets the item parent."""
2910
2911 if not item:
2912 raise "\nERROR: Invalid Tree Item. "
2913
2914 return item.GetParent()
2915
2916
2917 def GetFirstChild(self, item):
2918 """Gets the item first child."""
2919
2920 if not item:
2921 raise "\nERROR: Invalid Tree Item. "
2922
2923 cookie = 0
2924 return self.GetNextChild(item, cookie)
2925
2926
2927 def GetNextChild(self, item, cookie):
2928 """
2929 Gets the item next child based on the 'cookie' parameter.
2930 This method has no sense if you do not call GetFirstChild() before.
2931 """
2932
2933 if not item:
2934 raise "\nERROR: Invalid Tree Item. "
2935
2936 children = item.GetChildren()
2937
2938 # it's ok to cast cookie to size_t, we never have indices big enough to
2939 # overflow "void *"
2940
2941 if cookie < len(children):
2942
2943 return children[cookie], cookie+1
2944
2945 else:
2946
2947 # there are no more of them
2948 return None, cookie
2949
2950
2951 def GetLastChild(self, item):
2952 """Gets the item last child."""
2953
2954 if not item:
2955 raise "\nERROR: Invalid Tree Item. "
2956
2957 children = item.GetChildren()
2958 return (len(children) == 0 and [None] or [children[-1]])[0]
2959
2960
2961 def GetNextSibling(self, item):
2962 """Gets the next sibling of an item."""
2963
2964 if not item:
2965 raise "\nERROR: Invalid Tree Item. "
2966
2967 i = item
2968 parent = i.GetParent()
2969
2970 if parent == None:
2971
2972 # root item doesn't have any siblings
2973 return None
2974
2975 siblings = parent.GetChildren()
2976 index = siblings.index(i)
2977
2978 n = index + 1
2979 return (n == len(siblings) and [None] or [siblings[n]])[0]
2980
2981
2982 def GetPrevSibling(self, item):
2983 """Gets the previous sibling of an item."""
2984
2985 if not item:
2986 raise "\nERROR: Invalid Tree Item. "
2987
2988 i = item
2989 parent = i.GetParent()
2990
2991 if parent == None:
2992
2993 # root item doesn't have any siblings
2994 return None
2995
2996 siblings = parent.GetChildren()
2997 index = siblings.index(i)
2998
2999 return (index == 0 and [None] or [siblings[index-1]])[0]
3000
3001
3002 def GetNext(self, item):
3003 """Gets the next item. Only for internal use right now."""
3004
3005 if not item:
3006 raise "\nERROR: Invalid Tree Item. "
3007
3008 i = item
3009
3010 # First see if there are any children.
3011 children = i.GetChildren()
3012 if len(children) > 0:
3013 return children[0]
3014 else:
3015 # Try a sibling of this or ancestor instead
3016 p = item
3017 toFind = None
3018 while p and not toFind:
3019 toFind = self.GetNextSibling(p)
3020 p = self.GetItemParent(p)
3021
3022 return toFind
3023
3024
3025 def GetFirstVisibleItem(self):
3026 """Returns the first visible item."""
3027
3028 id = self.GetRootItem()
3029 if not id:
3030 return id
3031
3032 while id:
3033 if self.IsVisible(id):
3034 return id
3035 id = self.GetNext(id)
3036
3037 return None
3038
3039
3040 def GetNextVisible(self, item):
3041 """Returns the next visible item."""
3042
3043 if not item:
3044 raise "\nERROR: Invalid Tree Item. "
3045
3046 id = item
3047
3048 while id:
3049 id = self.GetNext(id)
3050 if id and self.IsVisible(id):
3051 return id
3052
3053 return None
3054
3055
3056 def GetPrevVisible(self, item):
3057
3058 if not item:
3059 raise "\nERROR: Invalid Tree Item. "
3060
3061 raise "\nERROR: Not Implemented"
3062
3063 return None
3064
3065
3066 def ResetTextControl(self):
3067 """Called by TreeTextCtrl when it marks itself for deletion."""
3068
3069 self._textCtrl.Destroy()
3070 self._textCtrl = None
3071
3072
3073 def FindItem(self, idParent, prefixOrig):
3074 """Finds the first item starting with the given prefix after the given item."""
3075
3076 # match is case insensitive as this is more convenient to the user: having
3077 # to press Shift-letter to go to the item starting with a capital letter
3078 # would be too bothersome
3079 prefix = prefixOrig.lower()
3080
3081 # determine the starting point: we shouldn't take the current item (this
3082 # allows to switch between two items starting with the same letter just by
3083 # pressing it) but we shouldn't jump to the next one if the user is
3084 # continuing to type as otherwise he might easily skip the item he wanted
3085 id = idParent
3086
3087 if len(prefix) == 1:
3088 id = self.GetNext(id)
3089
3090 # look for the item starting with the given prefix after it
3091 while id and not self.GetItemText(id).lower().startswith(prefix):
3092
3093 id = self.GetNext(id)
3094
3095 # if we haven't found anything...
3096 if not id:
3097
3098 # ... wrap to the beginning
3099 id = self.GetRootItem()
3100 if self.HasFlag(TR_HIDE_ROOT):
3101 # can't select virtual root
3102 id = self.GetNext(id)
3103
3104 # and try all the items (stop when we get to the one we started from)
3105 while id != idParent and not self.GetItemText(id).lower().startswith(prefix):
3106 id = self.GetNext(id)
3107
3108 return id
3109
3110
3111 # -----------------------------------------------------------------------------
3112 # operations
3113 # -----------------------------------------------------------------------------
3114
3115 def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3116 """Actually inserts an item in the tree."""
3117
3118 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3119 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3120
3121 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3122 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3123
3124 if ct_type < 0 or ct_type > 2:
3125 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3126
3127 parent = parentId
3128
3129 if not parent:
3130
3131 # should we give a warning here?
3132 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3133
3134 self._dirty = True # do this first so stuff below doesn't cause flicker
3135
3136 item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data)
3137
3138 if wnd is not None:
3139 self._hasWindows = True
3140 self._itemWithWindow.append(item)
3141
3142 parent.Insert(item, previous)
3143
3144 return item
3145
3146
3147 def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3148 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3149
3150 if self._anchor:
3151 raise "\nERROR: Tree Can Have Only One Root"
3152
3153 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3154 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3155
3156 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3157 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3158
3159 if ct_type < 0 or ct_type > 2:
3160 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3161
3162 self._dirty = True # do this first so stuff below doesn't cause flicker
3163
3164 self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data)
3165
3166 if wnd is not None:
3167 self._hasWindows = True
3168 self._itemWithWindow.append(self._anchor)
3169
3170 if self.HasFlag(TR_HIDE_ROOT):
3171
3172 # if root is hidden, make sure we can navigate
3173 # into children
3174 self._anchor.SetHasPlus()
3175 self._anchor.Expand()
3176 self.CalculatePositions()
3177
3178 if not self.HasFlag(TR_MULTIPLE):
3179
3180 self._current = self._key_current = self._anchor
3181 self._current.SetHilight(True)
3182
3183 return self._anchor
3184
3185
3186 def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3187 """Appends an item as a first child of parent."""
3188
3189 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3190 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3191
3192 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3193 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3194
3195 return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data)
3196
3197
3198 def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3199 """Auxiliary function to cope with the C++ hideous multifunction."""
3200
3201 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3202 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3203
3204 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3205 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3206
3207 parent = parentId
3208
3209 if not parent:
3210 # should we give a warning here?
3211 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3212
3213 index = -1
3214 if idPrevious:
3215
3216 try:
3217 index = parent.GetChildren().index(idPrevious)
3218 except:
3219 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3220
3221 return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data)
3222
3223
3224 def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3225 """Auxiliary function to cope with the C++ hideous multifunction."""
3226
3227 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3228 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3229
3230 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3231 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3232
3233 parent = parentId
3234
3235 if not parent:
3236 # should we give a warning here?
3237 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3238
3239 return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data)
3240
3241
3242 def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3243 """Inserts an item after the given previous."""
3244
3245 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3246 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3247
3248 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3249 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3250
3251 if type(input) == type(1):
3252 return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data)
3253 else:
3254 return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data)
3255
3256
3257 def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3258 """Appends an item as a last child of its parent."""
3259
3260 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3261 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3262
3263 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3264 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3265
3266 parent = parentId
3267
3268 if not parent:
3269 # should we give a warning here?
3270 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3271
3272 return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data)
3273
3274
3275 def SendDeleteEvent(self, item):
3276 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3277
3278 event = TreeEvent(wxEVT_TREE_DELETE_ITEM, self.GetId())
3279 event._item = item
3280 event.SetEventObject(self)
3281 self.ProcessEvent(event)
3282
3283
3284 def IsDescendantOf(self, parent, item):
3285 """Checks if the given item is under another one."""
3286
3287 while item:
3288
3289 if item == parent:
3290
3291 # item is a descendant of parent
3292 return True
3293
3294 item = item.GetParent()
3295
3296 return False
3297
3298
3299 # Don't leave edit or selection on a child which is about to disappear
3300 def ChildrenClosing(self, item):
3301 """We are about to destroy the item children."""
3302
3303 if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()):
3304 self._textCtrl.StopEditing()
3305
3306 if item != self._key_current and self.IsDescendantOf(item, self._key_current):
3307 self._key_current = None
3308
3309 if self.IsDescendantOf(item, self._select_me):
3310 self._select_me = item
3311
3312 if item != self._current and self.IsDescendantOf(item, self._current):
3313 self._current.SetHilight(False)
3314 self._current = None
3315 self._select_me = item
3316
3317
3318 def DeleteChildren(self, item):
3319 """Delete item children."""
3320
3321 if not item:
3322 raise "\nERROR: Invalid Tree Item. "
3323
3324 self._dirty = True # do this first so stuff below doesn't cause flicker
3325
3326 self.ChildrenClosing(item)
3327 item.DeleteChildren(self)
3328
3329
3330 def Delete(self, item):
3331 """Delete an item."""
3332
3333 if not item:
3334 raise "\nERROR: Invalid Tree Item. "
3335
3336 self._dirty = True # do this first so stuff below doesn't cause flicker
3337
3338 if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()):
3339 # can't delete the item being edited, cancel editing it first
3340 self._textCtrl.StopEditing()
3341
3342 parent = item.GetParent()
3343
3344 # don't keep stale pointers around!
3345 if self.IsDescendantOf(item, self._key_current):
3346
3347 # Don't silently change the selection:
3348 # do it properly in idle time, so event
3349 # handlers get called.
3350
3351 # self._key_current = parent
3352 self._key_current = None
3353
3354 # self._select_me records whether we need to select
3355 # a different item, in idle time.
3356 if self._select_me and self.IsDescendantOf(item, self._select_me):
3357 self._select_me = parent
3358
3359 if self.IsDescendantOf(item, self._current):
3360
3361 # Don't silently change the selection:
3362 # do it properly in idle time, so event
3363 # handlers get called.
3364
3365 # self._current = parent
3366 self._current = None
3367 self._select_me = parent
3368
3369 # remove the item from the tree
3370 if parent:
3371
3372 parent.GetChildren().remove(item) # remove by value
3373
3374 else: # deleting the root
3375
3376 # nothing will be left in the tree
3377 self._anchor = None
3378
3379 # and delete all of its children and the item itself now
3380 item.DeleteChildren(self)
3381 self.SendDeleteEvent(item)
3382
3383 if item == self._select_me:
3384 self._select_me = None
3385
3386 # Remove the item with window
3387 if item in self._itemWithWindow:
3388 wnd = item.GetWindow()
3389 wnd.Hide()
3390 wnd.Destroy()
3391 item._wnd = None
3392 self._itemWithWindow.remove(item)
3393
3394 del item
3395
3396
3397 def DeleteAllItems(self):
3398 """Delete all items in the CustomTreeCtrl."""
3399
3400 if self._anchor:
3401 self.Delete(self._anchor)
3402
3403
3404 def Expand(self, item):
3405 """
3406 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3407 EVT_TREE_ITEM_EXPANDED events.
3408 """
3409
3410 if not item:
3411 raise "\nERROR: Invalid Tree Item. "
3412
3413 if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
3414 raise "\nERROR: Can't Expand An Hidden Root. "
3415
3416 if not item.HasPlus():
3417 return
3418
3419 if item.IsExpanded():
3420 return
3421
3422 event = TreeEvent(wxEVT_TREE_ITEM_EXPANDING, self.GetId())
3423 event._item = item
3424 event.SetEventObject(self)
3425
3426 if self.ProcessEvent(event) and not event.IsAllowed():
3427 # cancelled by program
3428 return
3429
3430 item.Expand()
3431 self.CalculatePositions()
3432
3433 self.RefreshSubtree(item)
3434
3435 if self._hasWindows:
3436 # We hide the associated window here, we may show it after
3437 self.HideWindows()
3438
3439 event.SetEventType(wxEVT_TREE_ITEM_EXPANDED)
3440 self.ProcessEvent(event)
3441
3442
3443 def ExpandAll(self, item):
3444 """Expands all the items."""
3445
3446 if not item:
3447 raise "\nERROR: Invalid Tree Item. "
3448
3449 if not self.HasFlag(TR_HIDE_ROOT) or item != GetRootItem():
3450 self.Expand(item)
3451 if not self.IsExpanded(item):
3452 return
3453
3454 child, cookie = self.GetFirstChild(item)
3455
3456 while child:
3457 self.ExpandAll(child)
3458 child, cookie = self.GetNextChild(item, cookie)
3459
3460
3461 def Collapse(self, item):
3462 """
3463 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3464 EVT_TREE_ITEM_COLLAPSED events.
3465 """
3466
3467 if not item:
3468 raise "\nERROR: Invalid Tree Item. "
3469
3470 if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
3471 raise "\nERROR: Can't Collapse An Hidden Root. "
3472
3473 if not item.IsExpanded():
3474 return
3475
3476 event = TreeEvent(wxEVT_TREE_ITEM_COLLAPSING, self.GetId())
3477 event._item = item
3478 event.SetEventObject(self)
3479 if self.ProcessEvent(event) and not event.IsAllowed():
3480 # cancelled by program
3481 return
3482
3483 self.ChildrenClosing(item)
3484 item.Collapse()
3485
3486 self.CalculatePositions()
3487 self.RefreshSubtree(item)
3488
3489 if self._hasWindows:
3490 self.HideWindows()
3491
3492 event.SetEventType(wxEVT_TREE_ITEM_COLLAPSED)
3493 self.ProcessEvent(event)
3494
3495
3496 def CollapseAndReset(self, item):
3497 """Collapse the given item and deletes its children."""
3498
3499 self.Collapse(item)
3500 self.DeleteChildren(item)
3501
3502
3503 def Toggle(self, item):
3504 """Toggles the item state (collapsed/expanded)."""
3505
3506 if item.IsExpanded():
3507 self.Collapse(item)
3508 else:
3509 self.Expand(item)
3510
3511
3512 def HideWindows(self):
3513 """Hides the windows associated to the items. Used internally."""
3514
3515 for child in self._itemWithWindow:
3516 if not self.IsVisible(child):
3517 wnd = child.GetWindow()
3518 wnd.Hide()
3519
3520
3521 def Unselect(self):
3522 """Unselects the current selection."""
3523
3524 if self._current:
3525
3526 self._current.SetHilight(False)
3527 self.RefreshLine(self._current)
3528
3529 self._current = None
3530 self._select_me = None
3531
3532
3533 def UnselectAllChildren(self, item):
3534 """Unselects all the children of the given item."""
3535
3536 if item.IsSelected():
3537
3538 item.SetHilight(False)
3539 self.RefreshLine(item)
3540
3541 if item.HasChildren():
3542 for child in item.GetChildren():
3543 self.UnselectAllChildren(child)
3544
3545
3546 def UnselectAll(self):
3547 """Unselect all the items."""
3548
3549 rootItem = self.GetRootItem()
3550
3551 # the tree might not have the root item at all
3552 if rootItem:
3553 self.UnselectAllChildren(rootItem)
3554
3555
3556 # Recursive function !
3557 # To stop we must have crt_item<last_item
3558 # Algorithm :
3559 # Tag all next children, when no more children,
3560 # Move to parent (not to tag)
3561 # Keep going... if we found last_item, we stop.
3562
3563 def TagNextChildren(self, crt_item, last_item, select):
3564 """Used internally."""
3565
3566 parent = crt_item.GetParent()
3567
3568 if parent == None: # This is root item
3569 return self.TagAllChildrenUntilLast(crt_item, last_item, select)
3570
3571 children = parent.GetChildren()
3572 index = children.index(crt_item)
3573
3574 count = len(children)
3575
3576 for n in xrange(index+1, count):
3577 if self.TagAllChildrenUntilLast(children[n], last_item, select):
3578 return True
3579
3580 return self.TagNextChildren(parent, last_item, select)
3581
3582
3583 def TagAllChildrenUntilLast(self, crt_item, last_item, select):
3584 """Used internally."""
3585
3586 crt_item.SetHilight(select)
3587 self.RefreshLine(crt_item)
3588
3589 if crt_item == last_item:
3590 return True
3591
3592 if crt_item.HasChildren():
3593 for child in crt_item.GetChildren():
3594 if self.TagAllChildrenUntilLast(child, last_item, select):
3595 return True
3596
3597 return False
3598
3599
3600 def SelectItemRange(self, item1, item2):
3601 """Selects all the items between item1 and item2."""
3602
3603 self._select_me = None
3604
3605 # item2 is not necessary after item1
3606 # choice first' and 'last' between item1 and item2
3607 first = (item1.GetY() < item2.GetY() and [item1] or [item2])[0]
3608 last = (item1.GetY() < item2.GetY() and [item2] or [item1])[0]
3609
3610 select = self._current.IsSelected()
3611
3612 if self.TagAllChildrenUntilLast(first, last, select):
3613 return
3614
3615 self.TagNextChildren(first, last, select)
3616
3617
3618 def DoSelectItem(self, item, unselect_others=True, extended_select=False):
3619 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3620
3621 if not item:
3622 raise "\nERROR: Invalid Tree Item. "
3623
3624 self._select_me = None
3625
3626 is_single = not (self.GetTreeStyle() & TR_MULTIPLE)
3627
3628 # to keep going anyhow !!!
3629 if is_single:
3630 if item.IsSelected():
3631 return # nothing to do
3632 unselect_others = True
3633 extended_select = False
3634
3635 elif unselect_others and item.IsSelected():
3636
3637 # selection change if there is more than one item currently selected
3638 if len(self.GetSelections()) == 1:
3639 return
3640
3641 event = TreeEvent(wxEVT_TREE_SEL_CHANGING, self.GetId())
3642 event._item = item
3643 event._itemOld = self._current
3644 event.SetEventObject(self)
3645 # TODO : Here we don't send any selection mode yet !
3646
3647 if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed():
3648 return
3649
3650 parent = self.GetItemParent(item)
3651 while parent:
3652 if not self.IsExpanded(parent):
3653 self.Expand(parent)
3654
3655 parent = self.GetItemParent(parent)
3656
3657 # ctrl press
3658 if unselect_others:
3659 if is_single:
3660 self.Unselect() # to speed up thing
3661 else:
3662 self.UnselectAll()
3663
3664 # shift press
3665 if extended_select:
3666 if not self._current:
3667 self._current = self._key_current = self.GetRootItem()
3668
3669 # don't change the mark (self._current)
3670 self.SelectItemRange(self._current, item)
3671
3672 else:
3673
3674 select = True # the default
3675
3676 # Check if we need to toggle hilight (ctrl mode)
3677 if not unselect_others:
3678 select = not item.IsSelected()
3679
3680 self._current = self._key_current = item
3681 self._current.SetHilight(select)
3682 self.RefreshLine(self._current)
3683
3684 # This can cause idle processing to select the root
3685 # if no item is selected, so it must be after the
3686 # selection is set
3687 self.EnsureVisible(item)
3688
3689 event.SetEventType(wxEVT_TREE_SEL_CHANGED)
3690 self.GetEventHandler().ProcessEvent(event)
3691
3692 # Handles hypertext items
3693 if self.IsItemHyperText(item):
3694 event = TreeEvent(wxEVT_TREE_ITEM_HYPERLINK, self.GetId())
3695 event._item = item
3696 self.GetEventHandler().ProcessEvent(event)
3697
3698
3699 def SelectItem(self, item, select=True):
3700 """Selects/deselects an item."""
3701
3702 if not item:
3703 raise "\nERROR: Invalid Tree Item. "
3704
3705 if select:
3706
3707 self.DoSelectItem(item, not self.HasFlag(TR_MULTIPLE))
3708
3709 else: # deselect
3710
3711 item.SetHilight(False)
3712 self.RefreshLine(item)
3713
3714
3715 def FillArray(self, item, array=[]):
3716 """
3717 Internal function. Used to populate an array of selected items when
3718 the style TR_MULTIPLE is used.
3719 """
3720
3721 if not array:
3722 array = []
3723
3724 if item.IsSelected():
3725 array.append(item)
3726
3727 if item.HasChildren():
3728 for child in item.GetChildren():
3729 array = self.FillArray(child, array)
3730
3731 return array
3732
3733
3734 def GetSelections(self):
3735 """
3736 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3737 the TR_MULTIPLE style set.
3738 """
3739
3740 array = []
3741 idRoot = self.GetRootItem()
3742 if idRoot:
3743 array = self.FillArray(idRoot, array)
3744
3745 #else: the tree is empty, so no selections
3746
3747 return array
3748
3749
3750 def EnsureVisible(self, item):
3751 """Ensure that an item is visible in CustomTreeCtrl."""
3752
3753 if not item:
3754 raise "\nERROR: Invalid Tree Item. "
3755
3756 # first expand all parent branches
3757 parent = item.GetParent()
3758
3759 if self.HasFlag(TR_HIDE_ROOT):
3760 while parent and parent != self._anchor:
3761 self.Expand(parent)
3762 parent = parent.GetParent()
3763 else:
3764 while parent:
3765 self.Expand(parent)
3766 parent = parent.GetParent()
3767
3768 self.ScrollTo(item)
3769
3770
3771 def ScrollTo(self, item):
3772 """Scrolls the specified item into view."""
3773
3774 if not item:
3775 return
3776
3777 # We have to call this here because the label in
3778 # question might just have been added and no screen
3779 # update taken place.
3780 if self._dirty:
3781 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
3782 self.Update()
3783 else:
3784 wx.YieldIfNeeded()
3785
3786 # now scroll to the item
3787 item_y = item.GetY()
3788 start_x, start_y = self.GetViewStart()
3789 start_y *= _PIXELS_PER_UNIT
3790
3791 client_w, client_h = self.GetClientSize()
3792
3793 x, y = 0, 0
3794
3795 if item_y < start_y+3:
3796
3797 # going down
3798 x, y = self._anchor.GetSize(x, y, self)
3799 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3800 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3801 x_pos = self.GetScrollPos(wx.HORIZONTAL)
3802 # Item should appear at top
3803 self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, item_y/_PIXELS_PER_UNIT)
3804
3805 elif item_y+self.GetLineHeight(item) > start_y+client_h:
3806
3807 # going up
3808 x, y = self._anchor.GetSize(x, y, self)
3809 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3810 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3811 item_y += _PIXELS_PER_UNIT+2
3812 x_pos = self.GetScrollPos(wx.HORIZONTAL)
3813 # Item should appear at bottom
3814 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 )
3815
3816
3817 def OnCompareItems(self, item1, item2):
3818 """
3819 Returns whether 2 items have the same text.
3820 Override this function in the derived class to change the sort order of the items
3821 in the CustomTreeCtrl. The function should return a negative, zero or positive
3822 value if the first item is less than, equal to or greater than the second one.
3823
3824 The base class version compares items alphabetically.
3825 """
3826
3827 return self.GetItemText(item1) == self.GetItemText(item2)
3828
3829
3830 def SortChildren(self, item):
3831 """
3832 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3833 You should override that method to change the sort order (the default is ascending
3834 case-sensitive alphabetical order).
3835 """
3836
3837 if not item:
3838 raise "\nERROR: Invalid Tree Item. "
3839
3840 children = item.GetChildren()
3841
3842 if len(children) > 1:
3843 self._dirty = True
3844 children.sort(self.OnCompareItems)
3845
3846
3847 def GetImageList(self):
3848 """Returns the normal image list."""
3849
3850 return self._imageListNormal
3851
3852
3853 def GetButtonsImageList(self):
3854 """Returns the buttons image list (from which application-defined button images are taken)."""
3855
3856 return self._imageListButtons
3857
3858
3859 def GetStateImageList(self):
3860 """Returns the state image list (from which application-defined state images are taken)."""
3861
3862 return self._imageListState
3863
3864
3865 def GetImageListCheck(self):
3866 """Returns the image list used to build the check/radio buttons."""
3867
3868 return self._imageListCheck
3869
3870
3871 def CalculateLineHeight(self):
3872 """Calculates the height of a line."""
3873
3874 dc = wx.ClientDC(self)
3875 self._lineHeight = dc.GetCharHeight()
3876
3877 if self._imageListNormal:
3878
3879 # Calculate a self._lineHeight value from the normal Image sizes.
3880 # May be toggle off. Then CustomTreeCtrl will spread when
3881 # necessary (which might look ugly).
3882 n = self._imageListNormal.GetImageCount()
3883
3884 for i in xrange(n):
3885
3886 width, height = self._imageListNormal.GetSize(i)
3887
3888 if height > self._lineHeight:
3889 self._lineHeight = height
3890
3891 if self._imageListButtons:
3892
3893 # Calculate a self._lineHeight value from the Button image sizes.
3894 # May be toggle off. Then CustomTreeCtrl will spread when
3895 # necessary (which might look ugly).
3896 n = self._imageListButtons.GetImageCount()
3897
3898 for i in xrange(n):
3899
3900 width, height = self._imageListButtons.GetSize(i)
3901
3902 if height > self._lineHeight:
3903 self._lineHeight = height
3904
3905 if self._imageListCheck:
3906
3907 # Calculate a self._lineHeight value from the check/radio image sizes.
3908 # May be toggle off. Then CustomTreeCtrl will spread when
3909 # necessary (which might look ugly).
3910 n = self._imageListCheck.GetImageCount()
3911
3912 for i in xrange(n):
3913
3914 width, height = self._imageListCheck.GetSize(i)
3915
3916 if height > self._lineHeight:
3917 self._lineHeight = height
3918
3919 if self._lineHeight < 30:
3920 self._lineHeight += 2 # at least 2 pixels
3921 else:
3922 self._lineHeight += self._lineHeight/10 # otherwise 10% extra spacing
3923
3924
3925 def SetImageList(self, imageList):
3926 """Sets the normal image list."""
3927
3928 if self._ownsImageListNormal:
3929 del self._imageListNormal
3930
3931 self._imageListNormal = imageList
3932 self._ownsImageListNormal = False
3933 self._dirty = True
3934 # Don't do any drawing if we're setting the list to NULL,
3935 # since we may be in the process of deleting the tree control.
3936 if imageList:
3937 self.CalculateLineHeight()
3938
3939 # We gray out the image list to use the grayed icons with disabled items
3940 self._grayedImageList = wx.ImageList(16, 16, True, 0)
3941
3942 for ii in xrange(imageList.GetImageCount()):
3943
3944 bmp = imageList.GetBitmap(ii)
3945 image = wx.ImageFromBitmap(bmp)
3946 image = GrayOut(image)
3947 newbmp = wx.BitmapFromImage(image)
3948 self._grayedImageList.Add(newbmp)
3949
3950
3951 def SetStateImageList(self, imageList):
3952 """Sets the state image list (from which application-defined state images are taken)."""
3953
3954 if self._ownsImageListState:
3955 del self._imageListState
3956
3957 self._imageListState = imageList
3958 self._ownsImageListState = False
3959
3960
3961 def SetButtonsImageList(self, imageList):
3962 """Sets the buttons image list (from which application-defined button images are taken)."""
3963
3964 if self._ownsImageListButtons:
3965 del self._imageListButtons
3966
3967 self._imageListButtons = imageList
3968 self._ownsImageListButtons = False
3969 self._dirty = True
3970 self.CalculateLineHeight()
3971
3972
3973 def SetImageListCheck(self, sizex, sizey, imglist=None):
3974 """Sets the check image list."""
3975
3976 if imglist is None:
3977
3978 self._imageListCheck = wx.ImageList(sizex, sizey)
3979 self._imageListCheck.Add(GetCheckedBitmap())
3980 self._imageListCheck.Add(GetNotCheckedBitmap())
3981 self._imageListCheck.Add(GetFlaggedBitmap())
3982 self._imageListCheck.Add(GetNotFlaggedBitmap())
3983
3984 else:
3985
3986 sizex, sizey = imglist.GetSize(0)
3987 self._imageListCheck = imglist
3988
3989 # We gray out the image list to use the grayed icons with disabled items
3990 self._grayedCheckList = wx.ImageList(sizex, sizey, True, 0)
3991
3992 for ii in xrange(self._imageListCheck.GetImageCount()):
3993
3994 bmp = self._imageListCheck.GetBitmap(ii)
3995 image = wx.ImageFromBitmap(bmp)
3996 image = GrayOut(image)
3997 newbmp = wx.BitmapFromImage(image)
3998 self._grayedCheckList.Add(newbmp)
3999
4000 self._dirty = True
4001
4002 if imglist:
4003 self.CalculateLineHeight()
4004
4005
4006 def AssignImageList(self, imageList):
4007 """Assigns the normal image list."""
4008
4009 self.SetImageList(imageList)
4010 self._ownsImageListNormal = True
4011
4012
4013 def AssignStateImageList(self, imageList):
4014 """Assigns the state image list."""
4015
4016 self.SetStateImageList(imageList)
4017 self._ownsImageListState = True
4018
4019
4020 def AssignButtonsImageList(self, imageList):
4021 """Assigns the button image list."""
4022
4023 self.SetButtonsImageList(imageList)
4024 self._ownsImageListButtons = True
4025
4026
4027 # -----------------------------------------------------------------------------
4028 # helpers
4029 # -----------------------------------------------------------------------------
4030
4031 def AdjustMyScrollbars(self):
4032 """Adjust the wx.ScrolledWindow scrollbars."""
4033
4034 if self._anchor:
4035
4036 x, y = self._anchor.GetSize(0, 0, self)
4037 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
4038 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
4039 x_pos = self.GetScrollPos(wx.HORIZONTAL)
4040 y_pos = self.GetScrollPos(wx.VERTICAL)
4041 self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, y_pos)
4042
4043 else:
4044
4045 self.SetScrollbars(0, 0, 0, 0)
4046
4047
4048 def GetLineHeight(self, item):
4049 """Returns the line height for the given item."""
4050
4051 if self.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT:
4052 return item.GetHeight()
4053 else:
4054 return self._lineHeight
4055
4056
4057 def DrawVerticalGradient(self, dc, rect, hasfocus):
4058 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4059
4060 dc.DrawRectangleRect(rect)
4061 border = self._borderPen.GetWidth()
4062
4063 dc.SetPen(wx.TRANSPARENT_PEN)
4064
4065 # calculate gradient coefficients
4066 if hasfocus:
4067 col2 = self._secondcolour
4068 col1 = self._firstcolour
4069 else:
4070 col2 = self._hilightUnfocusedBrush.GetColour()
4071 col1 = self._hilightUnfocusedBrush2.GetColour()
4072
4073 r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
4074 r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
4075
4076 flrect = float(rect.height)
4077
4078 rstep = float((r2 - r1)) / flrect
4079 gstep = float((g2 - g1)) / flrect
4080 bstep = float((b2 - b1)) / flrect
4081
4082 rf, gf, bf = 0, 0, 0
4083
4084 for y in xrange(rect.y+border, rect.y + rect.height-border):
4085 currCol = (r1 + rf, g1 + gf, b1 + bf)
4086 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4087 dc.DrawRectangle(rect.x+border, y, rect.width-2*border, 1)
4088 rf = rf + rstep
4089 gf = gf + gstep
4090 bf = bf + bstep
4091
4092
4093 def DrawHorizontalGradient(self, dc, rect, hasfocus):
4094 """Gradient fill from colour 1 to colour 2 from left to right."""
4095
4096 dc.DrawRectangleRect(rect)
4097 border = self._borderPen.GetWidth()
4098
4099 dc.SetPen(wx.TRANSPARENT_PEN)
4100
4101 # calculate gradient coefficients
4102
4103 if hasfocus:
4104 col2 = self._secondcolour
4105 col1 = self._firstcolour
4106 else:
4107 col2 = self._hilightUnfocusedBrush.GetColour()
4108 col1 = self._hilightUnfocusedBrush2.GetColour()
4109
4110 r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
4111 r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
4112
4113 flrect = float(rect.width)
4114
4115 rstep = float((r2 - r1)) / flrect
4116 gstep = float((g2 - g1)) / flrect
4117 bstep = float((b2 - b1)) / flrect
4118
4119 rf, gf, bf = 0, 0, 0
4120
4121 for x in xrange(rect.x+border, rect.x + rect.width-border):
4122 currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf))
4123 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4124 dc.DrawRectangle(x, rect.y+border, 1, rect.height-2*border)
4125 rf = rf + rstep
4126 gf = gf + gstep
4127 bf = bf + bstep
4128
4129
4130 def DrawVistaRectangle(self, dc, rect, hasfocus):
4131 """Draw the selected item(s) with the Windows Vista style."""
4132
4133 if hasfocus:
4134
4135 outer = _rgbSelectOuter
4136 inner = _rgbSelectInner
4137 top = _rgbSelectTop
4138 bottom = _rgbSelectBottom
4139
4140 else:
4141
4142 outer = _rgbNoFocusOuter
4143 inner = _rgbNoFocusInner
4144 top = _rgbNoFocusTop
4145 bottom = _rgbNoFocusBottom
4146
4147 oldpen = dc.GetPen()
4148 oldbrush = dc.GetBrush()
4149
4150 dc.SetBrush(wx.TRANSPARENT_BRUSH)
4151 dc.SetPen(wx.Pen(outer))
4152 dc.DrawRoundedRectangleRect(rect, 3)
4153 rect.Deflate(1, 1)
4154 dc.SetPen(wx.Pen(inner))
4155 dc.DrawRoundedRectangleRect(rect, 2)
4156 rect.Deflate(1, 1)
4157
4158 r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue())
4159 r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue())
4160
4161 flrect = float(rect.height)
4162
4163 rstep = float((r2 - r1)) / flrect
4164 gstep = float((g2 - g1)) / flrect
4165 bstep = float((b2 - b1)) / flrect
4166
4167 rf, gf, bf = 0, 0, 0
4168 dc.SetPen(wx.TRANSPARENT_PEN)
4169
4170 for y in xrange(rect.y, rect.y + rect.height):
4171 currCol = (r1 + rf, g1 + gf, b1 + bf)
4172 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4173 dc.DrawRectangle(rect.x, y, rect.width, 1)
4174 rf = rf + rstep
4175 gf = gf + gstep
4176 bf = bf + bstep
4177
4178 dc.SetPen(oldpen)
4179 dc.SetBrush(oldbrush)
4180
4181
4182 def PaintItem(self, item, dc):
4183 """Actually paint an item."""
4184
4185 attr = item.GetAttributes()
4186
4187 if attr and attr.HasFont():
4188 dc.SetFont(attr.GetFont())
4189 elif item.IsBold():
4190 dc.SetFont(self._boldFont)
4191 if item.IsHyperText():
4192 dc.SetFont(self.GetHyperTextFont())
4193 if item.GetVisited():
4194 dc.SetTextForeground(self.GetHyperTextVisitedColour())
4195 else:
4196 dc.SetTextForeground(self.GetHyperTextNewColour())
4197
4198 text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
4199
4200 image = item.GetCurrentImage()
4201 checkimage = item.GetCurrentCheckedImage()
4202 image_w, image_h = 0, 0
4203
4204 if image != _NO_IMAGE:
4205
4206 if self._imageListNormal:
4207
4208 image_w, image_h = self._imageListNormal.GetSize(image)
4209 image_w += 4
4210
4211 else:
4212
4213 image = _NO_IMAGE
4214
4215 if item.GetType() != 0:
4216 wcheck, hcheck = self._imageListCheck.GetSize(item.GetType())
4217 wcheck += 4
4218 else:
4219 wcheck, hcheck = 0, 0
4220
4221 total_h = self.GetLineHeight(item)
4222 drawItemBackground = False
4223
4224 if item.IsSelected():
4225
4226 # under mac selections are only a rectangle in case they don't have the focus
4227 if wx.Platform == "__WXMAC__":
4228 if not self._hasFocus:
4229 dc.SetBrush(wx.TRANSPARENT_BRUSH)
4230 dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), 1, wx.SOLID))
4231 else:
4232 dc.SetBrush(self._hilightBrush)
4233 else:
4234 dc.SetBrush((self._hasFocus and [self._hilightBrush] or [self._hilightUnfocusedBrush])[0])
4235 drawItemBackground = True
4236 else:
4237 if attr and attr.HasBackgroundColour():
4238 drawItemBackground = True
4239 colBg = attr.GetBackgroundColour()
4240 else:
4241 colBg = self._backgroundColour
4242
4243 dc.SetBrush(wx.Brush(colBg, wx.SOLID))
4244 dc.SetPen(wx.TRANSPARENT_PEN)
4245
4246 offset = (self.HasFlag(TR_ROW_LINES) and [1] or [0])[0]
4247
4248 if self.HasFlag(TR_FULL_ROW_HIGHLIGHT):
4249
4250 oldpen = dc.GetPen()
4251 dc.SetPen(wx.TRANSPARENT_PEN)
4252 x, y = self.GetPosition()
4253 w, h = self.GetSize()
4254
4255 itemrect = wx.Rect(x, item.GetY()+offset, w, total_h-offset)
4256
4257 if item.IsSelected():
4258 if self._usegradients:
4259 if self._gradientstyle == 0: # Horizontal
4260 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4261 else: # Vertical
4262 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4263 elif self._vistaselection:
4264 self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
4265 else:
4266 dc.DrawRectangleRect(itemrect)
4267
4268 dc.SetPen(oldpen)
4269
4270 else:
4271
4272 if item.IsSelected() and image != _NO_IMAGE:
4273
4274 # If it's selected, and there's an image, then we should
4275 # take care to leave the area under the image painted in the
4276 # background colour.
4277
4278 wnd = item.GetWindow()
4279 wndx = 0
4280 if wnd:
4281 wndx, wndy = item.GetWindowSize()
4282
4283 itemrect = wx.Rect(item.GetX() + wcheck + image_w - 2, item.GetY()+offset,
4284 item.GetWidth() - image_w - wcheck + 2 - wndx, total_h-offset)
4285
4286 if self._usegradients:
4287 if self._gradientstyle == 0: # Horizontal
4288 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4289 else: # Vertical
4290 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4291 elif self._vistaselection:
4292 self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
4293 else:
4294 dc.DrawRectangleRect(itemrect)
4295
4296 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4297 # don't allow backgrounds to be customized. Not drawing the background,
4298 # except for custom item backgrounds, works for both kinds of theme.
4299 elif drawItemBackground:
4300
4301 minusicon = wcheck + image_w - 2
4302 itemrect = wx.Rect(item.GetX()+minusicon, item.GetY()+offset, item.GetWidth()-minusicon, total_h-offset)
4303
4304 if self._usegradients and self._hasFocus:
4305 if self._gradientstyle == 0: # Horizontal
4306 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4307 else: # Vertical
4308 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4309 else:
4310 dc.DrawRectangleRect(itemrect)
4311
4312 if image != _NO_IMAGE:
4313
4314 dc.SetClippingRegion(item.GetX(), item.GetY(), wcheck+image_w-2, total_h)
4315 if item.IsEnabled():
4316 imglist = self._imageListNormal
4317 else:
4318 imglist = self._grayedImageList
4319
4320 imglist.Draw(image, dc,
4321 item.GetX() + wcheck,
4322 item.GetY() + ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0],
4323 wx.IMAGELIST_DRAW_TRANSPARENT)
4324
4325 dc.DestroyClippingRegion()
4326
4327 if wcheck:
4328 if item.IsEnabled():
4329 imglist = self._imageListCheck
4330 else:
4331 imglist = self._grayedCheckList
4332
4333 imglist.Draw(checkimage, dc,
4334 item.GetX(),
4335 item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0],
4336 wx.IMAGELIST_DRAW_TRANSPARENT)
4337
4338 dc.SetBackgroundMode(wx.TRANSPARENT)
4339 extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0]
4340
4341 textrect = wx.Rect(wcheck + image_w + item.GetX(), item.GetY() + extraH, text_w, text_h)
4342
4343 if not item.IsEnabled():
4344 foreground = dc.GetTextForeground()
4345 dc.SetTextForeground(self._disabledColour)
4346 dc.DrawLabel(item.GetText(), textrect)
4347 dc.SetTextForeground(foreground)
4348 else:
4349 dc.DrawLabel(item.GetText(), textrect)
4350
4351 wnd = item.GetWindow()
4352 if wnd:
4353 wndx = wcheck + image_w + item.GetX() + text_w + 4
4354 xa, ya = self.CalcScrolledPosition((0, item.GetY()))
4355 if not wnd.IsShown():
4356 wnd.Show()
4357 if wnd.GetPosition() != (wndx, ya):
4358 wnd.SetPosition((wndx, ya))
4359
4360 # restore normal font
4361 dc.SetFont(self._normalFont)
4362
4363
4364 # Now y stands for the top of the item, whereas it used to stand for middle !
4365 def PaintLevel(self, item, dc, level, y):
4366 """Paint a level of CustomTreeCtrl."""
4367
4368 x = level*self._indent
4369
4370 if not self.HasFlag(TR_HIDE_ROOT):
4371
4372 x += self._indent
4373
4374 elif level == 0:
4375
4376 # always expand hidden root
4377 origY = y
4378 children = item.GetChildren()
4379 count = len(children)
4380
4381 if count > 0:
4382 n = 0
4383 while n < count:
4384 oldY = y
4385 y = self.PaintLevel(children[n], dc, 1, y)
4386 n = n + 1
4387
4388 if not self.HasFlag(TR_NO_LINES) and self.HasFlag(TR_LINES_AT_ROOT) and count > 0:
4389
4390 # draw line down to last child
4391 origY += self.GetLineHeight(children[0])>>1
4392 oldY += self.GetLineHeight(children[n-1])>>1
4393 dc.DrawLine(3, origY, 3, oldY)
4394
4395 return y
4396
4397 item.SetX(x+self._spacing)
4398 item.SetY(y)
4399
4400 h = self.GetLineHeight(item)
4401 y_top = y
4402 y_mid = y_top + (h>>1)
4403 y += h
4404
4405 exposed_x = dc.LogicalToDeviceX(0)
4406 exposed_y = dc.LogicalToDeviceY(y_top)
4407
4408 if self.IsExposed(exposed_x, exposed_y, 10000, h): # 10000 = very much
4409 if wx.Platform == "__WXMAC__":
4410 # don't draw rect outline if we already have the
4411 # background color under Mac
4412 pen = ((item.IsSelected() and self._hasFocus) and [self._borderPen] or [wx.TRANSPARENT_PEN])[0]
4413 else:
4414 pen = self._borderPen
4415
4416 if item.IsSelected():
4417 if (wx.Platform == "__WXMAC__" and self._hasFocus):
4418 colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
4419 else:
4420 colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
4421 else:
4422 attr = item.GetAttributes()
4423 if attr and attr.HasTextColour():
4424 colText = attr.GetTextColour()
4425 else:
4426 colText = self.GetForegroundColour()
4427
4428 if self._vistaselection:
4429 colText = wx.BLACK
4430
4431 # prepare to draw
4432 dc.SetTextForeground(colText)
4433 dc.SetPen(pen)
4434 oldpen = pen
4435
4436 # draw
4437 self.PaintItem(item, dc)
4438
4439 if self.HasFlag(TR_ROW_LINES):
4440
4441 # if the background colour is white, choose a
4442 # contrasting color for the lines
4443 medium_grey = wx.Pen(wx.Colour(200, 200, 200))
4444 dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0])
4445 dc.DrawLine(0, y_top, 10000, y_top)
4446 dc.DrawLine(0, y, 10000, y)
4447
4448 # restore DC objects
4449 dc.SetBrush(wx.WHITE_BRUSH)
4450 dc.SetTextForeground(wx.BLACK)
4451
4452 if not self.HasFlag(TR_NO_LINES):
4453
4454 # draw the horizontal line here
4455 dc.SetPen(self._dottedPen)
4456 x_start = x
4457 if x > self._indent:
4458 x_start -= self._indent
4459 elif self.HasFlag(TR_LINES_AT_ROOT):
4460 x_start = 3
4461 dc.DrawLine(x_start, y_mid, x + self._spacing, y_mid)
4462 dc.SetPen(oldpen)
4463
4464 # should the item show a button?
4465 if item.HasPlus() and self.HasButtons():
4466
4467 if self._imageListButtons:
4468
4469 # draw the image button here
4470 image_h = 0
4471 image_w = 0
4472 image = (item.IsExpanded() and [TreeItemIcon_Expanded] or [TreeItemIcon_Normal])[0]
4473 if item.IsSelected():
4474 image += TreeItemIcon_Selected - TreeItemIcon_Normal
4475
4476 image_w, image_h = self._imageListButtons.GetSize(image)
4477 xx = x - image_w/2
4478 yy = y_mid - image_h/2
4479
4480 dc.SetClippingRegion(xx, yy, image_w, image_h)
4481 self._imageListButtons.Draw(image, dc, xx, yy,
4482 wx.IMAGELIST_DRAW_TRANSPARENT)
4483 dc.DestroyClippingRegion()
4484
4485 else: # no custom buttons
4486
4487 if self._windowStyle & TR_TWIST_BUTTONS:
4488 # We draw something like the Mac twist buttons
4489
4490 dc.SetPen(wx.BLACK_PEN)
4491 dc.SetBrush(self._hilightBrush)
4492 button = [wx.Point(), wx.Point(), wx.Point()]
4493
4494 if item.IsExpanded():
4495 button[0].x = x - 5
4496 button[0].y = y_mid - 3
4497 button[1].x = x + 5
4498 button[1].y = button[0].y
4499 button[2].x = x
4500 button[2].y = button[0].y + 6
4501 else:
4502 button[0].x = x - 3
4503 button[0].y = y_mid - 5
4504 button[1].x = button[0].x
4505 button[1].y = y_mid + 5
4506 button[2].x = button[0].x + 5
4507 button[2].y = y_mid
4508
4509 dc.DrawPolygon(button)
4510
4511 else:
4512 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4513
4514 wImage = 9
4515 hImage = 9
4516
4517 flag = 0
4518
4519 if item.IsExpanded():
4520 flag |= _CONTROL_EXPANDED
4521 if item == self._underMouse:
4522 flag |= _CONTROL_CURRENT
4523
4524 self._drawingfunction(self, dc, wx.Rect(x - wImage/2, y_mid - hImage/2,wImage, hImage), flag)
4525
4526 if item.IsExpanded():
4527
4528 children = item.GetChildren()
4529 count = len(children)
4530
4531 if count > 0:
4532
4533 n = 0
4534 level = level + 1
4535
4536 while n < count:
4537 oldY = y
4538 y = self.PaintLevel(children[n], dc, level, y)
4539 n = n + 1
4540
4541 if not self.HasFlag(TR_NO_LINES) and count > 0:
4542
4543 # draw line down to last child
4544 oldY += self.GetLineHeight(children[n-1])>>1
4545 if self.HasButtons():
4546 y_mid += 5
4547
4548 # Only draw the portion of the line that is visible, in case it is huge
4549 xOrigin, yOrigin = dc.GetDeviceOrigin()
4550 yOrigin = abs(yOrigin)
4551 width, height = self.GetClientSize()
4552
4553 # Move end points to the begining/end of the view?
4554 if y_mid < yOrigin:
4555 y_mid = yOrigin
4556 if oldY > yOrigin + height:
4557 oldY = yOrigin + height
4558
4559 # after the adjustments if y_mid is larger than oldY then the line
4560 # isn't visible at all so don't draw anything
4561 if y_mid < oldY:
4562 dc.SetPen(self._dottedPen)
4563 dc.DrawLine(x, y_mid, x, oldY)
4564
4565 return y
4566
4567
4568 # -----------------------------------------------------------------------------
4569 # wxWidgets callbacks
4570 # -----------------------------------------------------------------------------
4571
4572 def OnPaint(self, event):
4573 """Handles the wx.EVT_PAINT event."""
4574
4575 dc = wx.PaintDC(self)
4576 self.PrepareDC(dc)
4577
4578 if not self._anchor:
4579 return
4580
4581 dc.SetFont(self._normalFont)
4582 dc.SetPen(self._dottedPen)
4583
4584 y = 2
4585 self.PaintLevel(self._anchor, dc, 0, y)
4586
4587
4588 def OnEraseBackground(self, event):
4589 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4590
4591 # Can we actually do something here (or in OnPaint()) To Handle
4592 # background images that are stretchable or always centered?
4593 # I tried but I get enormous flickering...
4594
4595 if not self._backgroundImage:
4596 event.Skip()
4597 return
4598
4599 if self._imageStretchStyle == _StyleTile:
4600 dc = event.GetDC()
4601
4602 if not dc:
4603 dc = wx.ClientDC(self)
4604 rect = self.GetUpdateRegion().GetBox()
4605 dc.SetClippingRect(rect)
4606
4607 self.TileBackground(dc)
4608
4609
4610 def TileBackground(self, dc):
4611 """Tiles the background image to fill all the available area."""
4612
4613 sz = self.GetClientSize()
4614 w = self._backgroundImage.GetWidth()
4615 h = self._backgroundImage.GetHeight()
4616
4617 x = 0
4618
4619 while x < sz.width:
4620 y = 0
4621
4622 while y < sz.height:
4623 dc.DrawBitmap(self._backgroundImage, x, y, True)
4624 y = y + h
4625
4626 x = x + w
4627
4628
4629 def OnSetFocus(self, event):
4630 """Handles the wx.EVT_SET_FOCUS event."""
4631
4632 self._hasFocus = True
4633 self.RefreshSelected()
4634 event.Skip()
4635
4636
4637 def OnKillFocus(self, event):
4638 """Handles the wx.EVT_KILL_FOCUS event."""
4639
4640 self._hasFocus = False
4641 self.RefreshSelected()
4642 event.Skip()
4643
4644
4645 def OnKeyDown(self, event):
4646 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4647
4648 te = TreeEvent(wxEVT_TREE_KEY_DOWN, self.GetId())
4649 te._evtKey = event
4650 te.SetEventObject(self)
4651
4652 if self.GetEventHandler().ProcessEvent(te):
4653 # intercepted by the user code
4654 return
4655
4656 if self._current is None or self._key_current is None:
4657
4658 event.Skip()
4659 return
4660
4661 # how should the selection work for this event?
4662 is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), event.ShiftDown(), event.CmdDown())
4663
4664 # + : Expand
4665 # - : Collaspe
4666 # * : Expand all/Collapse all
4667 # ' ' | return : activate
4668 # up : go up (not last children!)
4669 # down : go down
4670 # left : go to parent
4671 # right : open if parent and go next
4672 # home : go to root
4673 # end : go to last item without opening parents
4674 # alnum : start or continue searching for the item with this prefix
4675
4676 keyCode = event.GetKeyCode()
4677
4678 if keyCode in [ord("+"), wx.WXK_ADD]: # "+"
4679 if self._current.HasPlus() and not self.IsExpanded(self._current) and self.IsEnabled(self._current):
4680 self.Expand(self._current)
4681
4682 elif keyCode in [ord("*"), wx.WXK_MULTIPLY]: # "*"
4683 if not self.IsExpanded(self._current) and self.IsEnabled(self._current):
4684 # expand all
4685 self.ExpandAll(self._current)
4686
4687 elif keyCode in [ord("-"), wx.WXK_SUBTRACT]: # "-"
4688 if self.IsExpanded(self._current):
4689 self.Collapse(self._current)
4690
4691 elif keyCode == wx.WXK_MENU:
4692 # Use the item's bounding rectangle to determine position for the event
4693 itemRect = self.GetBoundingRect(self._current, True)
4694 event = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
4695 event._item = self._current
4696 # Use the left edge, vertical middle
4697 event._pointDrag = wx.Point(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight()/2)
4698 event.SetEventObject(self)
4699 self.GetEventHandler().ProcessEvent(event)
4700
4701 elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]:
4702
4703 if not self.IsEnabled(self._current):
4704 event.Skip()
4705 return
4706
4707 if not event.HasModifiers():
4708 event = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
4709 event._item = self._current
4710 event.SetEventObject(self)
4711 self.GetEventHandler().ProcessEvent(event)
4712
4713 if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0:
4714 checked = not self.IsItemChecked(self._current)
4715 self.CheckItem(self._current, checked)
4716
4717 # in any case, also generate the normal key event for this key,
4718 # even if we generated the ACTIVATED event above: this is what
4719 # wxMSW does and it makes sense because you might not want to
4720 # process ACTIVATED event at all and handle Space and Return
4721 # directly (and differently) which would be impossible otherwise
4722 event.Skip()
4723
4724 # up goes to the previous sibling or to the last
4725 # of its children if it's expanded
4726 elif keyCode == wx.WXK_UP:
4727 prev = self.GetPrevSibling(self._key_current)
4728 if not prev:
4729 prev = self.GetItemParent(self._key_current)
4730 if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
4731 return
4732
4733 if prev:
4734 current = self._key_current
4735 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4736 if current == self.GetFirstChild(prev)[0] and self.IsEnabled(prev):
4737 # otherwise we return to where we came from
4738 self.DoSelectItem(prev, unselect_others, extended_select)
4739 self._key_current = prev
4740
4741 else:
4742 current = self._key_current
4743
4744 # We are going to another parent node
4745 while self.IsExpanded(prev) and self.HasChildren(prev):
4746 child = self.GetLastChild(prev)
4747 if child:
4748 prev = child
4749 current = prev
4750
4751 # Try to get the previous siblings and see if they are active
4752 while prev and not self.IsEnabled(prev):
4753 prev = self.GetPrevSibling(prev)
4754
4755 if not prev:
4756 # No previous siblings active: go to the parent and up
4757 prev = self.GetItemParent(current)
4758 while prev and not self.IsEnabled(prev):
4759 prev = self.GetItemParent(prev)
4760
4761 if prev:
4762 self.DoSelectItem(prev, unselect_others, extended_select)
4763 self._key_current = prev
4764
4765 # left arrow goes to the parent
4766 elif keyCode == wx.WXK_LEFT:
4767
4768 prev = self.GetItemParent(self._current)
4769 if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
4770 # don't go to root if it is hidden
4771 prev = self.GetPrevSibling(self._current)
4772
4773 if self.IsExpanded(self._current):
4774 self.Collapse(self._current)
4775 else:
4776 if prev and self.IsEnabled(prev):
4777 self.DoSelectItem(prev, unselect_others, extended_select)
4778
4779 elif keyCode == wx.WXK_RIGHT:
4780 # this works the same as the down arrow except that we
4781 # also expand the item if it wasn't expanded yet
4782 if self.IsExpanded(self._current) and self.HasChildren(self._current):
4783 child, cookie = self.GetFirstChild(self._key_current)
4784 if self.IsEnabled(child):
4785 self.DoSelectItem(child, unselect_others, extended_select)
4786 self._key_current = child
4787 else:
4788 self.Expand(self._current)
4789 # fall through
4790
4791 elif keyCode == wx.WXK_DOWN:
4792 if self.IsExpanded(self._key_current) and self.HasChildren(self._key_current):
4793
4794 child = self.GetNextActiveItem(self._key_current)
4795
4796 if child:
4797 self.DoSelectItem(child, unselect_others, extended_select)
4798 self._key_current = child
4799
4800 else:
4801
4802 next = self.GetNextSibling(self._key_current)
4803
4804 if not next:
4805 current = self._key_current
4806 while current and not next:
4807 current = self.GetItemParent(current)
4808 if current:
4809 next = self.GetNextSibling(current)
4810 if not self.IsEnabled(next):
4811 next = None
4812
4813 else:
4814 while next and not self.IsEnabled(next):
4815 next = self.GetNext(next)
4816
4817 if next:
4818 self.DoSelectItem(next, unselect_others, extended_select)
4819 self._key_current = next
4820
4821
4822 # <End> selects the last visible tree item
4823 elif keyCode == wx.WXK_END:
4824
4825 last = self.GetRootItem()
4826
4827 while last and self.IsExpanded(last):
4828
4829 lastChild = self.GetLastChild(last)
4830
4831 # it may happen if the item was expanded but then all of
4832 # its children have been deleted - so IsExpanded() returned
4833 # true, but GetLastChild() returned invalid item
4834 if not lastChild:
4835 break
4836
4837 last = lastChild
4838
4839 if last and self.IsEnabled(last):
4840
4841 self.DoSelectItem(last, unselect_others, extended_select)
4842
4843 # <Home> selects the root item
4844 elif keyCode == wx.WXK_HOME:
4845
4846 prev = self.GetRootItem()
4847
4848 if not prev:
4849 return
4850
4851 if self.HasFlag(TR_HIDE_ROOT):
4852 prev, cookie = self.GetFirstChild(prev)
4853 if not prev:
4854 return
4855
4856 if self.IsEnabled(prev):
4857 self.DoSelectItem(prev, unselect_others, extended_select)
4858
4859 else:
4860
4861 if not event.HasModifiers() and ((keyCode >= ord('0') and keyCode <= ord('9')) or \
4862 (keyCode >= ord('a') and keyCode <= ord('z')) or \
4863 (keyCode >= ord('A') and keyCode <= ord('Z'))):
4864
4865 # find the next item starting with the given prefix
4866 ch = chr(keyCode)
4867 id = self.FindItem(self._current, self._findPrefix + ch)
4868
4869 if not id:
4870 # no such item
4871 return
4872
4873 if self.IsEnabled(id):
4874 self.SelectItem(id)
4875 self._findPrefix += ch
4876
4877 # also start the timer to reset the current prefix if the user
4878 # doesn't press any more alnum keys soon -- we wouldn't want
4879 # to use this prefix for a new item search
4880 if not self._findTimer:
4881 self._findTimer = TreeFindTimer(self)
4882
4883 self._findTimer.Start(_DELAY, wx.TIMER_ONE_SHOT)
4884
4885 else:
4886
4887 event.Skip()
4888
4889
4890 def GetNextActiveItem(self, item, down=True):
4891 """Returns the next active item. Used Internally at present. """
4892
4893 if down:
4894 sibling = self.GetNextSibling
4895 else:
4896 sibling = self.GetPrevSibling
4897
4898 if self.GetItemType(item) == 2 and not self.IsItemChecked(item):
4899 # Is an unchecked radiobutton... all its children are inactive
4900 # try to get the next/previous sibling
4901 found = 0
4902
4903 while 1:
4904 child = sibling(item)
4905 if (child and self.IsEnabled(child)) or not child:
4906 break
4907 item = child
4908
4909 else:
4910 # Tha's not a radiobutton... but some of its children can be
4911 # inactive
4912 child, cookie = self.GetFirstChild(item)
4913 while child and not self.IsEnabled(child):
4914 child, cookie = self.GetNextChild(item, cookie)
4915
4916 if child and self.IsEnabled(child):
4917 return child
4918
4919 return None
4920
4921
4922 def HitTest(self, point, flags=0):
4923 """
4924 Calculates which (if any) item is under the given point, returning the tree item
4925 at this point plus extra information flags. Flags is a bitlist of the following:
4926
4927 TREE_HITTEST_ABOVE above the client area
4928 TREE_HITTEST_BELOW below the client area
4929 TREE_HITTEST_NOWHERE no item has been hit
4930 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4931 TREE_HITTEST_ONITEMICON on the icon associated to an item
4932 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4933 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4934 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4935 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4936 TREE_HITTEST_TOLEFT on the left of the client area
4937 TREE_HITTEST_TORIGHT on the right of the client area
4938 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4939 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4940 TREE_HITTEST_ONITEM anywhere on the item
4941
4942 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4943 """
4944
4945 w, h = self.GetSize()
4946 flags = 0
4947
4948 if point.x < 0:
4949 flags |= TREE_HITTEST_TOLEFT
4950 if point.x > w:
4951 flags |= TREE_HITTEST_TORIGHT
4952 if point.y < 0:
4953 flags |= TREE_HITTEST_ABOVE
4954 if point.y > h:
4955 flags |= TREE_HITTEST_BELOW
4956
4957 if flags:
4958 return None, flags
4959
4960 if self._anchor == None:
4961 flags = TREE_HITTEST_NOWHERE
4962 return None, flags
4963
4964 hit, flags = self._anchor.HitTest(self.CalcUnscrolledPosition(point), self, flags, 0)
4965
4966 if hit == None:
4967 flags = TREE_HITTEST_NOWHERE
4968 return None, flags
4969
4970 if not self.IsEnabled(hit):
4971 return None, flags
4972
4973 return hit, flags
4974
4975
4976 def GetBoundingRect(self, item, textOnly=False):
4977 """Gets the bounding rectangle of the item."""
4978
4979 if not item:
4980 raise "\nERROR: Invalid Tree Item. "
4981
4982 i = item
4983
4984 startX, startY = self.GetViewStart()
4985 rect = wx.Rect()
4986
4987 rect.x = i.GetX() - startX*_PIXELS_PER_UNIT
4988 rect.y = i.GetY() - startY*_PIXELS_PER_UNIT
4989 rect.width = i.GetWidth()
4990 rect.height = self.GetLineHeight(i)
4991
4992 return rect
4993
4994
4995 def Edit(self, item):
4996 """
4997 Internal function. Starts the editing of an item label, sending a
4998 EVT_TREE_BEGIN_LABEL_EDIT event.
4999 """
5000
5001 te = TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT, self.GetId())
5002 te._item = item
5003 te.SetEventObject(self)
5004 if self.GetEventHandler().ProcessEvent(te) and not te.IsAllowed():
5005 # vetoed by user
5006 return
5007
5008 # We have to call this here because the label in
5009 # question might just have been added and no screen
5010 # update taken place.
5011 if self._dirty:
5012 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
5013 self.Update()
5014 else:
5015 wx.YieldIfNeeded()
5016
5017 if self._textCtrl != None and item != self._textCtrl.item():
5018 self._textCtrl.StopEditing()
5019
5020 self._textCtrl = TreeTextCtrl(self, item=item)
5021 self._textCtrl.SetFocus()
5022
5023
5024 def GetEditControl(self):
5025 """
5026 Returns a pointer to the edit TextCtrl if the item is being edited or
5027 None otherwise (it is assumed that no more than one item may be edited
5028 simultaneously).
5029 """
5030
5031 return self._textCtrl
5032
5033
5034 def OnRenameAccept(self, item, value):
5035 """
5036 Called by TreeTextCtrl, to accept the changes and to send the
5037 EVT_TREE_END_LABEL_EDIT event.
5038 """
5039
5040 le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
5041 le._item = item
5042 le.SetEventObject(self)
5043 le._label = value
5044 le._editCancelled = False
5045
5046 return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed()
5047
5048
5049 def OnRenameCancelled(self, item):
5050 """
5051 Called by TreeTextCtrl, to cancel the changes and to send the
5052 EVT_TREE_END_LABEL_EDIT event.
5053 """
5054
5055 # let owner know that the edit was cancelled
5056 le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
5057 le._item = item
5058 le.SetEventObject(self)
5059 le._label = ""
5060 le._editCancelled = True
5061
5062 self.GetEventHandler().ProcessEvent(le)
5063
5064
5065 def OnRenameTimer(self):
5066 """The timer for renaming has expired. Start editing."""
5067
5068 self.Edit(self._current)
5069
5070
5071 def OnMouse(self, event):
5072 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5073
5074 if not self._anchor:
5075 return
5076
5077 pt = self.CalcUnscrolledPosition(event.GetPosition())
5078
5079 # Is the mouse over a tree item button?
5080 flags = 0
5081 thisItem, flags = self._anchor.HitTest(pt, self, flags, 0)
5082 underMouse = thisItem
5083 underMouseChanged = underMouse != self._underMouse
5084
5085 if underMouse and (flags & TREE_HITTEST_ONITEMBUTTON) and not event.LeftIsDown() and \
5086 not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
5087 underMouse = underMouse
5088 else:
5089 underMouse = None
5090
5091 if underMouse != self._underMouse:
5092 if self._underMouse:
5093 # unhighlight old item
5094 self._underMouse = None
5095
5096 self._underMouse = underMouse
5097
5098 # Determines what item we are hovering over and need a tooltip for
5099 hoverItem = thisItem
5100
5101 # We do not want a tooltip if we are dragging, or if the rename timer is running
5102 if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
5103
5104 if hoverItem is not None:
5105 # Ask the tree control what tooltip (if any) should be shown
5106 hevent = TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP, self.GetId())
5107 hevent._item = hoverItem
5108 hevent.SetEventObject(self)
5109
5110 if self.GetEventHandler().ProcessEvent(hevent) and hevent.IsAllowed():
5111 self.SetToolTip(hevent._label)
5112
5113 if hoverItem.IsHyperText() and (flags & TREE_HITTEST_ONITEMLABEL) and hoverItem.IsEnabled():
5114 self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
5115 self._isonhyperlink = True
5116 else:
5117 if self._isonhyperlink:
5118 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
5119 self._isonhyperlink = False
5120
5121 # we process left mouse up event (enables in-place edit), right down
5122 # (pass to the user code), left dbl click (activate item) and
5123 # dragging/moving events for items drag-and-drop
5124
5125 if not (event.LeftDown() or event.LeftUp() or event.RightDown() or event.LeftDClick() or \
5126 event.Dragging() or ((event.Moving() or event.RightUp()) and self._isDragging)):
5127
5128 event.Skip()
5129 return
5130
5131 flags = 0
5132 item, flags = self._anchor.HitTest(pt, self, flags, 0)
5133
5134 if event.Dragging() and not self._isDragging and ((flags & TREE_HITTEST_ONITEMICON) or (flags & TREE_HITTEST_ONITEMLABEL)):
5135
5136 if self._dragCount == 0:
5137 self._dragStart = pt
5138
5139 self._countDrag = 0
5140 self._dragCount = self._dragCount + 1
5141
5142 if self._dragCount != 3:
5143 # wait until user drags a bit further...
5144 return
5145
5146 command = (event.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG] or [wxEVT_TREE_BEGIN_DRAG])[0]
5147
5148 nevent = TreeEvent(command, self.GetId())
5149 nevent._item = self._current
5150 nevent.SetEventObject(self)
5151 newpt = self.CalcScrolledPosition(pt)
5152 nevent.SetPoint(newpt)
5153
5154 # by default the dragging is not supported, the user code must
5155 # explicitly allow the event for it to take place
5156 nevent.Veto()
5157
5158 if self.GetEventHandler().ProcessEvent(nevent) and nevent.IsAllowed():
5159
5160 # we're going to drag this item
5161 self._isDragging = True
5162
5163 # remember the old cursor because we will change it while
5164 # dragging
5165 self._oldCursor = self._cursor
5166
5167 # in a single selection control, hide the selection temporarily
5168 if not (self.GetTreeStyle() & TR_MULTIPLE):
5169 self._oldSelection = self.GetSelection()
5170
5171 if self._oldSelection:
5172
5173 self._oldSelection.SetHilight(False)
5174 self.RefreshLine(self._oldSelection)
5175 else:
5176 selections = self.GetSelections()
5177 if len(selections) == 1:
5178 self._oldSelection = selections[0]
5179 self._oldSelection.SetHilight(False)
5180 self.RefreshLine(self._oldSelection)
5181
5182 if self._dragImage:
5183 del self._dragImage
5184
5185 # Create the custom draw image from the icons and the text of the item
5186 self._dragImage = DragImage(self, self._current)
5187 self._dragImage.BeginDrag(wx.Point(0,0), self)
5188 self._dragImage.Show()
5189 self._dragImage.Move(self.CalcScrolledPosition(pt))
5190
5191 elif event.Dragging() and self._isDragging:
5192
5193 self._dragImage.Move(self.CalcScrolledPosition(pt))
5194
5195 if self._countDrag == 0 and item:
5196 self._oldItem = item
5197
5198 if item != self._dropTarget:
5199
5200 # unhighlight the previous drop target
5201 if self._dropTarget:
5202 self._dropTarget.SetHilight(False)
5203 self.RefreshLine(self._dropTarget)
5204 if item:
5205 item.SetHilight(True)
5206 self.RefreshLine(item)
5207 self._countDrag = self._countDrag + 1
5208 self._dropTarget = item
5209
5210 self.Update()
5211
5212 if self._countDrag >= 3:
5213 # Here I am trying to avoid ugly repainting problems... hope it works
5214 self.RefreshLine(self._oldItem)
5215 self._countDrag = 0
5216
5217 elif (event.LeftUp() or event.RightUp()) and self._isDragging:
5218
5219 if self._dragImage:
5220 self._dragImage.EndDrag()
5221
5222 if self._dropTarget:
5223 self._dropTarget.SetHilight(False)
5224
5225 if self._oldSelection:
5226
5227 self._oldSelection.SetHilight(True)
5228 self.RefreshLine(self._oldSelection)
5229 self._oldSelection = None
5230
5231 # generate the drag end event
5232 event = TreeEvent(wxEVT_TREE_END_DRAG, self.GetId())
5233 event._item = item
5234 event._pointDrag = self.CalcScrolledPosition(pt)
5235 event.SetEventObject(self)
5236
5237 self.GetEventHandler().ProcessEvent(event)
5238
5239 self._isDragging = False
5240 self._dropTarget = None
5241
5242 self.SetCursor(self._oldCursor)
5243
5244 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
5245 self.Refresh()
5246 else:
5247 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5248 wx.YieldIfNeeded()
5249
5250 else:
5251
5252 # If we got to this point, we are not dragging or moving the mouse.
5253 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5254 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5255 # We skip even if we didn't hit an item because we still should
5256 # restore focus to the tree control even if we didn't exactly hit an item.
5257 if event.LeftDown():
5258 self._hasFocus = True
5259 self.SetFocusIgnoringChildren()
5260 event.Skip()
5261
5262 # here we process only the messages which happen on tree items
5263
5264 self._dragCount = 0
5265
5266 if item == None:
5267 if self._textCtrl != None and item != self._textCtrl.item():
5268 self._textCtrl.StopEditing()
5269 return # we hit the blank area
5270
5271 if event.RightDown():
5272
5273 if self._textCtrl != None and item != self._textCtrl.item():
5274 self._textCtrl.StopEditing()
5275
5276 self._hasFocus = True
5277 self.SetFocusIgnoringChildren()
5278
5279 # If the item is already selected, do not update the selection.
5280 # Multi-selections should not be cleared if a selected item is clicked.
5281 if not self.IsSelected(item):
5282
5283 self.DoSelectItem(item, True, False)
5284
5285 nevent = TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, self.GetId())
5286 nevent._item = item
5287 nevent._pointDrag = self.CalcScrolledPosition(pt)
5288 nevent.SetEventObject(self)
5289 event.Skip(not self.GetEventHandler().ProcessEvent(nevent))
5290
5291 # Consistent with MSW (for now), send the ITEM_MENU *after*
5292 # the RIGHT_CLICK event. TODO: This behaviour may change.
5293 nevent2 = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
5294 nevent2._item = item
5295 nevent2._pointDrag = self.CalcScrolledPosition(pt)
5296 nevent2.SetEventObject(self)
5297 self.GetEventHandler().ProcessEvent(nevent2)
5298
5299 elif event.LeftUp():
5300
5301 # this facilitates multiple-item drag-and-drop
5302
5303 if self.HasFlag(TR_MULTIPLE):
5304
5305 selections = self.GetSelections()
5306
5307 if len(selections) > 1 and not event.CmdDown() and not event.ShiftDown():
5308
5309 self.DoSelectItem(item, True, False)
5310
5311 if self._lastOnSame:
5312
5313 if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasFlag(TR_EDIT_LABELS):
5314
5315 if self._renameTimer:
5316
5317 if self._renameTimer.IsRunning():
5318
5319 self._renameTimer.Stop()
5320
5321 else:
5322
5323 self._renameTimer = TreeRenameTimer(self)
5324
5325 self._renameTimer.Start(_DELAY, True)
5326
5327 self._lastOnSame = False
5328
5329
5330 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5331
5332 if not item or not item.IsEnabled():
5333 if self._textCtrl != None and item != self._textCtrl.item():
5334 self._textCtrl.StopEditing()
5335 return
5336
5337 if self._textCtrl != None and item != self._textCtrl.item():
5338 self._textCtrl.StopEditing()
5339
5340 self._hasFocus = True
5341 self.SetFocusIgnoringChildren()
5342
5343 if event.LeftDown():
5344
5345 self._lastOnSame = item == self._current
5346
5347 if flags & TREE_HITTEST_ONITEMBUTTON:
5348
5349 # only toggle the item for a single click, double click on
5350 # the button doesn't do anything (it toggles the item twice)
5351 if event.LeftDown():
5352
5353 self.Toggle(item)
5354
5355 # don't select the item if the button was clicked
5356 return
5357
5358 if item.GetType() > 0 and (flags & TREE_HITTEST_ONITEMCHECKICON):
5359
5360 if event.LeftDown():
5361
5362 self.CheckItem(item, not self.IsItemChecked(item))
5363
5364 return
5365
5366 # clear the previously selected items, if the
5367 # user clicked outside of the present selection.
5368 # otherwise, perform the deselection on mouse-up.
5369 # this allows multiple drag and drop to work.
5370 # but if Cmd is down, toggle selection of the clicked item
5371 if not self.IsSelected(item) or event.CmdDown():
5372
5373 if flags & TREE_HITTEST_ONITEM:
5374 # how should the selection work for this event?
5375 if item.IsHyperText():
5376 self.SetItemVisited(item, True)
5377
5378 is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(),
5379 event.ShiftDown(),
5380 event.CmdDown())
5381
5382 self.DoSelectItem(item, unselect_others, extended_select)
5383
5384 # For some reason, Windows isn't recognizing a left double-click,
5385 # so we need to simulate it here. Allow 200 milliseconds for now.
5386 if event.LeftDClick():
5387
5388 # double clicking should not start editing the item label
5389 if self._renameTimer:
5390 self._renameTimer.Stop()
5391
5392 self._lastOnSame = False
5393
5394 # send activate event first
5395 nevent = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
5396 nevent._item = item
5397 nevent._pointDrag = self.CalcScrolledPosition(pt)
5398 nevent.SetEventObject(self)
5399 if not self.GetEventHandler().ProcessEvent(nevent):
5400
5401 # if the user code didn't process the activate event,
5402 # handle it ourselves by toggling the item when it is
5403 # double clicked
5404 ## if item.HasPlus():
5405 self.Toggle(item)
5406
5407
5408 def OnInternalIdle(self, event):
5409 """Performs operations in idle time (essentially drawing)."""
5410
5411 # Check if we need to select the root item
5412 # because nothing else has been selected.
5413 # Delaying it means that we can invoke event handlers
5414 # as required, when a first item is selected.
5415 if not self.HasFlag(TR_MULTIPLE) and not self.GetSelection():
5416
5417 if self._select_me:
5418 self.SelectItem(self._select_me)
5419 elif self.GetRootItem():
5420 self.SelectItem(self.GetRootItem())
5421
5422 # after all changes have been done to the tree control,
5423 # we actually redraw the tree when everything is over
5424
5425 if not self._dirty:
5426 return
5427 if self._freezeCount:
5428 return
5429
5430 self._dirty = False
5431
5432 self.CalculatePositions()
5433 self.Refresh()
5434 self.AdjustMyScrollbars()
5435
5436 # event.Skip()
5437
5438
5439 def CalculateSize(self, item, dc):
5440 """Calculates overall position and size of an item."""
5441
5442 attr = item.GetAttributes()
5443
5444 if attr and attr.HasFont():
5445 dc.SetFont(attr.GetFont())
5446 elif item.IsBold():
5447 dc.SetFont(self._boldFont)
5448 else:
5449 dc.SetFont(self._normalFont)
5450
5451 text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
5452 text_h+=2
5453
5454 # restore normal font
5455 dc.SetFont(self._normalFont)
5456
5457 image_w, image_h = 0, 0
5458 image = item.GetCurrentImage()
5459
5460 if image != _NO_IMAGE:
5461
5462 if self._imageListNormal:
5463
5464 image_w, image_h = self._imageListNormal.GetSize(image)
5465 image_w += 4
5466
5467 total_h = ((image_h > text_h) and [image_h] or [text_h])[0]
5468
5469 checkimage = item.GetCurrentCheckedImage()
5470 if checkimage is not None:
5471 wcheck, hcheck = self._imageListCheck.GetSize(checkimage)
5472 wcheck += 4
5473 else:
5474 wcheck = 0
5475
5476 if total_h < 30:
5477 total_h += 2 # at least 2 pixels
5478 else:
5479 total_h += total_h/10 # otherwise 10% extra spacing
5480
5481 if total_h > self._lineHeight:
5482 self._lineHeight = total_h
5483
5484 if not item.GetWindow():
5485 item.SetWidth(image_w+text_w+wcheck+2)
5486 item.SetHeight(total_h)
5487 else:
5488 item.SetWidth(item.GetWindowSize()[0]+image_w+text_w+wcheck+2)
5489
5490
5491 def CalculateLevel(self, item, dc, level, y):
5492 """Calculates the level of an item."""
5493
5494 x = level*self._indent
5495
5496 if not self.HasFlag(TR_HIDE_ROOT):
5497
5498 x += self._indent
5499
5500 elif level == 0:
5501
5502 # a hidden root is not evaluated, but its
5503 # children are always calculated
5504 children = item.GetChildren()
5505 count = len(children)
5506 level = level + 1
5507 for n in xrange(count):
5508 y = self.CalculateLevel(children[n], dc, level, y) # recurse
5509
5510 return y
5511
5512 self.CalculateSize(item, dc)
5513
5514 # set its position
5515 item.SetX(x+self._spacing)
5516 item.SetY(y)
5517 y += self.GetLineHeight(item)
5518
5519 if not item.IsExpanded():
5520 # we don't need to calculate collapsed branches
5521 return y
5522
5523 children = item.GetChildren()
5524 count = len(children)
5525 level = level + 1
5526 for n in xrange(count):
5527 y = self.CalculateLevel(children[n], dc, level, y) # recurse
5528
5529 return y
5530
5531
5532 def CalculatePositions(self):
5533 """Calculates all the positions of the visible items."""
5534
5535 if not self._anchor:
5536 return
5537
5538 dc = wx.ClientDC(self)
5539 self.PrepareDC(dc)
5540
5541 dc.SetFont(self._normalFont)
5542 dc.SetPen(self._dottedPen)
5543 y = 2
5544 y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion
5545
5546
5547 def RefreshSubtree(self, item):
5548 """Refreshes a damaged subtree of an item."""
5549
5550 if self._dirty:
5551 return
5552 if self._freezeCount:
5553 return
5554
5555 client = self.GetClientSize()
5556
5557 rect = wx.Rect()
5558 x, rect.y = self.CalcScrolledPosition(0, item.GetY())
5559 rect.width = client.x
5560 rect.height = client.y
5561
5562 self.Refresh(True, rect)
5563 self.AdjustMyScrollbars()
5564
5565
5566 def RefreshLine(self, item):
5567 """Refreshes a damaged item line."""
5568
5569 if self._dirty:
5570 return
5571 if self._freezeCount:
5572 return
5573
5574 rect = wx.Rect()
5575 x, rect.y = self.CalcScrolledPosition(0, item.GetY())
5576 rect.width = self.GetClientSize().x
5577 rect.height = self.GetLineHeight(item)
5578
5579 self.Refresh(True, rect)
5580
5581
5582 def RefreshSelected(self):
5583 """Refreshes a damaged selected item line."""
5584
5585 if self._freezeCount:
5586 return
5587
5588 # TODO: this is awfully inefficient, we should keep the list of all
5589 # selected items internally, should be much faster
5590 if self._anchor:
5591 self.RefreshSelectedUnder(self._anchor)
5592
5593
5594 def RefreshSelectedUnder(self, item):
5595 """Refreshes the selected items under the given item."""
5596
5597 if self._freezeCount:
5598 return
5599
5600 if item.IsSelected():
5601 self.RefreshLine(item)
5602
5603 children = item.GetChildren()
5604 for child in children:
5605 self.RefreshSelectedUnder(child)
5606
5607
5608 def Freeze(self):
5609 """Freeze CustomTreeCtrl."""
5610
5611 self._freezeCount = self._freezeCount + 1
5612
5613
5614 def Thaw(self):
5615 """Thaw CustomTreeCtrl."""
5616
5617 if self._freezeCount == 0:
5618 raise "\nERROR: Thawing Unfrozen Tree Control?"
5619
5620 self._freezeCount = self._freezeCount - 1
5621
5622 if not self._freezeCount:
5623 self.Refresh()
5624
5625
5626 # ----------------------------------------------------------------------------
5627 # changing colours: we need to refresh the tree control
5628 # ----------------------------------------------------------------------------
5629
5630 def SetBackgroundColour(self, colour):
5631 """Changes the background colour of CustomTreeCtrl."""
5632
5633 if not wx.Window.SetBackgroundColour(self, colour):
5634 return False
5635
5636 if self._freezeCount:
5637 return True
5638
5639 self.Refresh()
5640
5641 return True
5642
5643
5644 def SetForegroundColour(self, colour):
5645 """Changes the foreground colour of CustomTreeCtrl."""
5646
5647 if not wx.Window.SetForegroundColour(self, colour):
5648 return False
5649
5650 if self._freezeCount:
5651 return True
5652
5653 self.Refresh()
5654
5655 return True
5656
5657
5658 def OnGetToolTip(self, event):
5659 """
5660 Process the tooltip event, to speed up event processing. Does not actually
5661 get a tooltip.
5662 """
5663
5664 event.Veto()
5665
5666
5667 def DoGetBestSize(self):
5668 """Something is better than nothing..."""
5669
5670 # something is better than nothing...
5671 # 100x80 is what the MSW version will get from the default
5672 # wxControl::DoGetBestSize
5673
5674 return wx.Size(100, 80)
5675
5676
5677 def GetClassDefaultAttributes(self):
5678 """Gets the class default attributes."""
5679
5680 attr = wx.VisualAttributes()
5681 attr.colFg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
5682 attr.colBg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_LISTBOX)
5683 attr.font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
5684 return attr
5685
5686