]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/customtreectrl.py
inserted missing back quotes around echo lost by previous change
[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
1933
1934 def OnDestroy(self, event):
1935 """Handles the wx.EVT_WINDOW_DESTROY event."""
1936
1937 # Here there may be something I miss... do I have to destroy
1938 # something else?
1939 if self._renameTimer and self._renameTimer.IsRunning():
1940 self._renameTimer.Stop()
1941 del self._renameTimer
1942
1943 if self._findTimer and self._findTimer.IsRunning():
1944 self._findTimer.Stop()
1945 del self._findTimer
1946
1947 event.Skip()
1948
1949
1950 def GetCount(self):
1951 """Returns the global number of items in the tree."""
1952
1953 if not self._anchor:
1954 # the tree is empty
1955 return 0
1956
1957 count = self._anchor.GetChildrenCount()
1958
1959 if not self.HasFlag(TR_HIDE_ROOT):
1960 # take the root itself into account
1961 count = count + 1
1962
1963 return count
1964
1965
1966 def GetIndent(self):
1967 """Returns the item indentation."""
1968
1969 return self._indent
1970
1971
1972 def GetSpacing(self):
1973 """Returns the spacing between the start and the text."""
1974
1975 return self._spacing
1976
1977
1978 def GetRootItem(self):
1979 """Returns the root item."""
1980
1981 return self._anchor
1982
1983
1984 def GetSelection(self):
1985 """Returns the current selection: TR_SINGLE only."""
1986
1987 return self._current
1988
1989
1990 def ToggleItemSelection(self, item):
1991 """Toggles the item selection."""
1992
1993 if not item:
1994 raise "\nERROR: Invalid Tree Item. "
1995
1996 self.SelectItem(item, not self.IsSelected(item))
1997
1998
1999 def EnableChildren(self, item, enable=True):
2000 """Enables/disables item children. Used internally."""
2001
2002 torefresh = False
2003 if item.IsExpanded():
2004 torefresh = True
2005
2006 if item.GetType() == 2 and enable and not item.IsChecked():
2007 # We hit a radiobutton item not checked, we don't want to
2008 # enable the children
2009 return
2010
2011 child, cookie = self.GetFirstChild(item)
2012 while child:
2013 self.EnableItem(child, enable, torefresh=torefresh)
2014 # Recurse on tree
2015 if child.GetType != 2 or (child.GetType() == 2 and item.IsChecked()):
2016 self.EnableChildren(child, enable)
2017 (child, cookie) = self.GetNextChild(item, cookie)
2018
2019
2020 def EnableItem(self, item, enable=True, torefresh=True):
2021 """Enables/disables an item."""
2022
2023 if not item:
2024 raise "\nERROR: Invalid Tree Item. "
2025
2026 if item.IsEnabled() == enable:
2027 return
2028
2029 if not enable and item.IsSelected():
2030 self.SelectItem(item, False)
2031
2032 item.Enable(enable)
2033 wnd = item.GetWindow()
2034
2035 # Handles the eventual window associated to the item
2036 if wnd:
2037 wndenable = item.GetWindowEnabled()
2038 if enable:
2039 if wndenable:
2040 wnd.Enable(enable)
2041 else:
2042 wnd.Enable(enable)
2043
2044 if torefresh:
2045 # We have to refresh the item line
2046 dc = wx.ClientDC(self)
2047 self.CalculateSize(item, dc)
2048 self.RefreshLine(item)
2049
2050
2051 def IsEnabled(self, item):
2052 """Returns whether an item is enabled or disabled."""
2053
2054 if not item:
2055 raise "\nERROR: Invalid Tree Item. "
2056
2057 return item.IsEnabled()
2058
2059
2060 def SetDisabledColour(self, colour):
2061 """Sets the items disabled colour."""
2062
2063 self._disabledColour = colour
2064 self._dirty = True
2065
2066
2067 def GetDisabledColour(self):
2068 """Returns the items disabled colour."""
2069
2070 return self._disabledColour
2071
2072
2073 def IsItemChecked(self, item):
2074 """Returns whether an item is checked or not."""
2075
2076 if not item:
2077 raise "\nERROR: Invalid Tree Item. "
2078
2079 return item.IsChecked()
2080
2081
2082 def CheckItem2(self, item, checked=True, torefresh=False):
2083 """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
2084
2085 if item.GetType() == 0:
2086 return
2087
2088 item.Check(checked)
2089
2090 if torefresh:
2091 dc = wx.ClientDC(self)
2092 self.CalculateSize(item, dc)
2093 self.RefreshLine(item)
2094
2095
2096 def UnCheckRadioParent(self, item, checked=False):
2097 """Used internally to handle radio node parent correctly."""
2098
2099 e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
2100 e.SetItem(item)
2101 e.SetEventObject(self)
2102
2103 if self.GetEventHandler().ProcessEvent(e):
2104 return False
2105
2106 item.Check(checked)
2107 dc = wx.ClientDC(self)
2108 self.RefreshLine(item)
2109 self.EnableChildren(item, checked)
2110 e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
2111 e.SetItem(item)
2112 e.SetEventObject(self)
2113 self.GetEventHandler().ProcessEvent(e)
2114
2115 return True
2116
2117
2118 def CheckItem(self, item, checked=True):
2119 """
2120 Actually checks/uncheks an item, sending (eventually) the two
2121 events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
2122 """
2123
2124 if not item:
2125 raise "\nERROR: Invalid Tree Item. "
2126
2127 # Should we raise an error here?!?
2128 if item.GetType() == 0:
2129 return
2130
2131 if item.GetType() == 2: # it's a radio button
2132 if not checked and item.IsChecked(): # Try To Unckeck?
2133 if item.HasChildren():
2134 self.UnCheckRadioParent(item, checked)
2135 return
2136 else:
2137 if not self.UnCheckRadioParent(item, checked):
2138 return
2139
2140 self.CheckSameLevel(item, False)
2141 return
2142
2143 # Radiobuttons are done, let's handle checkbuttons...
2144 e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
2145 e.SetItem(item)
2146 e.SetEventObject(self)
2147
2148 if self.GetEventHandler().ProcessEvent(e):
2149 # Blocked by user
2150 return
2151
2152 item.Check(checked)
2153 dc = wx.ClientDC(self)
2154 self.RefreshLine(item)
2155
2156 if self._windowStyle & TR_AUTO_CHECK_CHILD:
2157 ischeck = self.IsItemChecked(item)
2158 self.AutoCheckChild(item, ischeck)
2159 elif self._windowStyle & TR_AUTO_TOGGLE_CHILD:
2160 self.AutoToggleChild(item)
2161
2162 e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
2163 e.SetItem(item)
2164 e.SetEventObject(self)
2165 self.GetEventHandler().ProcessEvent(e)
2166
2167
2168 def AutoToggleChild(self, item):
2169 """Transverses the tree and toggles the items. Meaningful only for check items."""
2170
2171 if not item:
2172 raise "\nERROR: Invalid Tree Item. "
2173
2174 child, cookie = self.GetFirstChild(item)
2175
2176 torefresh = False
2177 if item.IsExpanded():
2178 torefresh = True
2179
2180 # Recurse on tree
2181 while child:
2182 if child.GetType() == 1 and child.IsEnabled():
2183 self.CheckItem2(child, not child.IsChecked(), torefresh=torefresh)
2184 self.AutoToggleChild(child)
2185 (child, cookie) = self.GetNextChild(item, cookie)
2186
2187
2188 def AutoCheckChild(self, item, checked):
2189 """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
2190
2191 if not item:
2192 raise "\nERROR: Invalid Tree Item. "
2193
2194 (child, cookie) = self.GetFirstChild(item)
2195
2196 torefresh = False
2197 if item.IsExpanded():
2198 torefresh = True
2199
2200 while child:
2201 if child.GetType() == 1 and child.IsEnabled():
2202 self.CheckItem2(child, checked, torefresh=torefresh)
2203 self.AutoCheckChild(child, checked)
2204 (child, cookie) = self.GetNextChild(item, cookie)
2205
2206
2207 def CheckChilds(self, item, checked=True):
2208 """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
2209
2210 if not item:
2211 raise "\nERROR: Invalid Tree Item. "
2212
2213 if checked == None:
2214 self.AutoToggleChild(item)
2215 else:
2216 self.AutoCheckChild(item, checked)
2217
2218
2219 def CheckSameLevel(self, item, checked=False):
2220 """
2221 Uncheck radio items which are on the same level of the checked one.
2222 Used internally.
2223 """
2224
2225 parent = item.GetParent()
2226
2227 if not parent:
2228 return
2229
2230 torefresh = False
2231 if parent.IsExpanded():
2232 torefresh = True
2233
2234 (child, cookie) = self.GetFirstChild(parent)
2235 while child:
2236 if child.GetType() == 2 and child != item:
2237 self.CheckItem2(child, checked, torefresh=torefresh)
2238 if child.GetType != 2 or (child.GetType() == 2 and child.IsChecked()):
2239 self.EnableChildren(child, checked)
2240 (child, cookie) = self.GetNextChild(parent, cookie)
2241
2242
2243 def EditLabel(self, item):
2244 """Starts editing an item label."""
2245
2246 if not item:
2247 raise "\nERROR: Invalid Tree Item. "
2248
2249 self.Edit(item)
2250
2251
2252 def ShouldInheritColours(self):
2253 """We don't inherit colours from anyone."""
2254
2255 return False
2256
2257
2258 def SetIndent(self, indent):
2259 """Sets item indentation."""
2260
2261 self._indent = indent
2262 self._dirty = True
2263
2264
2265 def SetSpacing(self, spacing):
2266 """Sets item spacing."""
2267
2268 self._spacing = spacing
2269 self._dirty = True
2270
2271
2272 def HasFlag(self, flag):
2273 """Returns whether CustomTreeCtrl has a flag."""
2274
2275 return self._windowStyle & flag
2276
2277
2278 def HasChildren(self, item):
2279 """Returns whether an item has children or not."""
2280
2281 if not item:
2282 raise "\nERROR: Invalid Tree Item. "
2283
2284 return len(item.GetChildren()) > 0
2285
2286
2287 def GetChildrenCount(self, item, recursively=True):
2288 """Gets the item children count."""
2289
2290 if not item:
2291 raise "\nERROR: Invalid Tree Item. "
2292
2293 return item.GetChildrenCount(recursively)
2294
2295
2296 def SetTreeStyle(self, styles):
2297 """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
2298
2299 # Do not try to expand the root node if it hasn't been created yet
2300 if self._anchor and not self.HasFlag(TR_HIDE_ROOT) and styles & TR_HIDE_ROOT:
2301
2302 # if we will hide the root, make sure children are visible
2303 self._anchor.SetHasPlus()
2304 self._anchor.Expand()
2305 self.CalculatePositions()
2306
2307 # right now, just sets the styles. Eventually, we may
2308 # want to update the inherited styles, but right now
2309 # none of the parents has updatable styles
2310
2311 if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE):
2312 selections = self.GetSelections()
2313 for select in selections[0:-1]:
2314 self.SelectItem(select, False)
2315
2316 self._windowStyle = styles
2317 self._dirty = True
2318
2319
2320 def GetTreeStyle(self):
2321 """Returns the CustomTreeCtrl style."""
2322
2323 return self._windowStyle
2324
2325
2326 def HasButtons(self):
2327 """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
2328
2329 return self.HasFlag(TR_HAS_BUTTONS)
2330
2331
2332 # -----------------------------------------------------------------------------
2333 # functions to work with tree items
2334 # -----------------------------------------------------------------------------
2335
2336 def GetItemText(self, item):
2337 """Returns the item text."""
2338
2339 if not item:
2340 raise "\nERROR: Invalid Tree Item. "
2341
2342 return item.GetText()
2343
2344
2345 def GetItemImage(self, item, which):
2346 """Returns the item image."""
2347
2348 if not item:
2349 raise "\nERROR: Invalid Tree Item. "
2350
2351 return item.GetImage(which)
2352
2353
2354 def GetPyData(self, item):
2355 """Returns the data associated to an item."""
2356
2357 if not item:
2358 raise "\nERROR: Invalid Tree Item. "
2359
2360 return item.GetData()
2361
2362 GetItemPyData = GetPyData
2363
2364
2365 def GetItemTextColour(self, item):
2366 """Returns the item text colour."""
2367
2368 if not item:
2369 raise "\nERROR: Invalid Tree Item. "
2370
2371 return item.Attr().GetTextColour()
2372
2373
2374 def GetItemBackgroundColour(self, item):
2375 """Returns the item background colour."""
2376
2377 if not item:
2378 raise "\nERROR: Invalid Tree Item. "
2379
2380 return item.Attr().GetBackgroundColour()
2381
2382
2383 def GetItemFont(self, item):
2384 """Returns the item font."""
2385
2386 if not item:
2387 raise "\nERROR: Invalid Tree Item. "
2388
2389 return item.Attr().GetFont()
2390
2391
2392 def IsItemHyperText(self, item):
2393 """Returns whether an item is hypertext or not."""
2394
2395 if not item:
2396 raise "\nERROR: Invalid Tree Item. "
2397
2398 return item.IsHyperText()
2399
2400
2401 def SetItemText(self, item, text):
2402 """Sets the item text."""
2403
2404 if not item:
2405 raise "\nERROR: Invalid Tree Item. "
2406
2407 dc = wx.ClientDC(self)
2408 item.SetText(text)
2409 self.CalculateSize(item, dc)
2410 self.RefreshLine(item)
2411
2412
2413 def SetItemImage(self, item, image, which=TreeItemIcon_Normal):
2414 """Sets the item image, depending on the item state."""
2415
2416 if not item:
2417 raise "\nERROR: Invalid Tree Item. "
2418
2419 item.SetImage(image, which)
2420
2421 dc = wx.ClientDC(self)
2422 self.CalculateSize(item, dc)
2423 self.RefreshLine(item)
2424
2425
2426 def SetPyData(self, item, data):
2427 """Sets the data associated to an item."""
2428
2429 if not item:
2430 raise "\nERROR: Invalid Tree Item. "
2431
2432 item.SetData(data)
2433
2434 SetItemPyData = SetPyData
2435
2436
2437 def SetItemHasChildren(self, item, has=True):
2438 """Forces the appearance of the button next to the item."""
2439
2440 if not item:
2441 raise "\nERROR: Invalid Tree Item. "
2442
2443 item.SetHasPlus(has)
2444 self.RefreshLine(item)
2445
2446
2447 def SetItemBold(self, item, bold=True):
2448 """Sets the item font bold/unbold."""
2449
2450 if not item:
2451 raise "\nERROR: Invalid Tree Item. "
2452
2453 # avoid redrawing the tree if no real change
2454 if item.IsBold() != bold:
2455 item.SetBold(bold)
2456 self._dirty = True
2457
2458
2459 def SetItemItalic(self, item, italic=True):
2460 """Sets the item font italic/non-italic."""
2461
2462 if not item:
2463 raise "\nERROR: Invalid Tree Item. "
2464
2465 if item.IsItalic() != italic:
2466 itemFont = self.GetItemFont(item)
2467 if itemFont != wx.NullFont:
2468 style = wx.ITALIC
2469 if not italic:
2470 style = ~style
2471
2472 item.SetItalic(italic)
2473 itemFont.SetStyle(style)
2474 self.SetItemFont(item, itemFont)
2475 self._dirty = True
2476
2477
2478 def SetItemDropHighlight(self, item, highlight=True):
2479 """
2480 Gives the item the visual feedback for drag and drop operations.
2481 This is useful when something is dragged from outside the CustomTreeCtrl.
2482 """
2483
2484 if not item:
2485 raise "\nERROR: Invalid Tree Item. "
2486
2487 if highlight:
2488 bg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
2489 fg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
2490
2491 item.Attr().SetTextColour(fg)
2492 item.Attr.SetBackgroundColour(bg)
2493 self.RefreshLine(item)
2494
2495
2496 def SetItemTextColour(self, item, col):
2497 """Sets the item text colour."""
2498
2499 if not item:
2500 raise "\nERROR: Invalid Tree Item. "
2501
2502 if self.GetItemTextColour(item) == col:
2503 return
2504
2505 item.Attr().SetTextColour(col)
2506 self.RefreshLine(item)
2507
2508
2509 def SetItemBackgroundColour(self, item, col):
2510 """Sets the item background colour."""
2511
2512 if not item:
2513 raise "\nERROR: Invalid Tree Item. "
2514
2515 item.Attr().SetBackgroundColour(col)
2516 self.RefreshLine(item)
2517
2518
2519 def SetItemHyperText(self, item, hyper=True):
2520 """Sets whether the item is hypertext or not."""
2521
2522 if not item:
2523 raise "\nERROR: Invalid Tree Item. "
2524
2525 item.SetHyperText(hyper)
2526 self.RefreshLine(item)
2527
2528
2529 def SetItemFont(self, item, font):
2530 """Sets the item font."""
2531
2532 if not item:
2533 raise "\nERROR: Invalid Tree Item. "
2534
2535 if self.GetItemFont(item) == font:
2536 return
2537
2538 item.Attr().SetFont(font)
2539 self._dirty = True
2540
2541
2542 def SetFont(self, font):
2543 """Sets the CustomTreeCtrl font."""
2544
2545 wx.ScrolledWindow.SetFont(self, font)
2546
2547 self._normalFont = font
2548 self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
2549 self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(),
2550 self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
2551
2552 return True
2553
2554
2555 def GetHyperTextFont(self):
2556 """Returns the font used to render an hypertext item."""
2557
2558 return self._hypertextfont
2559
2560
2561 def SetHyperTextFont(self, font):
2562 """Sets the font used to render an hypertext item."""
2563
2564 self._hypertextfont = font
2565 self._dirty = True
2566
2567
2568 def SetHyperTextNewColour(self, colour):
2569 """Sets the colour used to render a non-visited hypertext item."""
2570
2571 self._hypertextnewcolour = colour
2572 self._dirty = True
2573
2574
2575 def GetHyperTextNewColour(self):
2576 """Returns the colour used to render a non-visited hypertext item."""
2577
2578 return self._hypertextnewcolour
2579
2580
2581 def SetHyperTextVisitedColour(self, colour):
2582 """Sets the colour used to render a visited hypertext item."""
2583
2584 self._hypertextvisitedcolour = colour
2585 self._dirty = True
2586
2587
2588 def GetHyperTextVisitedColour(self):
2589 """Returns the colour used to render a visited hypertext item."""
2590
2591 return self._hypertextvisitedcolour
2592
2593
2594 def SetItemVisited(self, item, visited=True):
2595 """Sets whether an hypertext item was visited."""
2596
2597 if not item:
2598 raise "\nERROR: Invalid Tree Item. "
2599
2600 item.SetVisited(visited)
2601 self.RefreshLine(item)
2602
2603
2604 def GetItemVisited(self, item):
2605 """Returns whether an hypertext item was visited."""
2606
2607 if not item:
2608 raise "\nERROR: Invalid Tree Item. "
2609
2610 return item.GetVisited()
2611
2612
2613 def SetHilightFocusColour(self, colour):
2614 """
2615 Sets the colour used to highlight focused selected items.
2616 This is applied only if gradient and Windows Vista styles are disabled.
2617 """
2618
2619 self._hilightBrush = wx.Brush(colour)
2620 self.RefreshSelected()
2621
2622
2623 def SetHilightNonFocusColour(self, colour):
2624 """
2625 Sets the colour used to highlight unfocused selected items.
2626 This is applied only if gradient and Windows Vista styles are disabled.
2627 """
2628
2629 self._hilightUnfocusedBrush = wx.Brush(colour)
2630 self.RefreshSelected()
2631
2632
2633 def GetHilightFocusColour(self):
2634 """
2635 Returns the colour used to highlight focused selected items.
2636 This is applied only if gradient and Windows Vista styles are disabled.
2637 """
2638
2639 return self._hilightBrush.GetColour()
2640
2641
2642 def GetHilightNonFocusColour(self):
2643 """
2644 Returns the colour used to highlight unfocused selected items.
2645 This is applied only if gradient and Windows Vista styles are disabled.
2646 """
2647
2648 return self._hilightUnfocusedBrush.GetColour()
2649
2650
2651 def SetFirstGradientColour(self, colour=None):
2652 """Sets the first gradient colour."""
2653
2654 if colour is None:
2655 colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
2656
2657 self._firstcolour = colour
2658 if self._usegradients:
2659 self.RefreshSelected()
2660
2661
2662 def SetSecondGradientColour(self, colour=None):
2663 """Sets the second gradient colour."""
2664
2665 if colour is None:
2666 # No colour given, generate a slightly darker from the
2667 # CustomTreeCtrl background colour
2668 color = self.GetBackgroundColour()
2669 r, g, b = int(color.Red()), int(color.Green()), int(color.Blue())
2670 color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
2671 colour = wx.Colour(color[0], color[1], color[2])
2672
2673 self._secondcolour = colour
2674
2675 if self._usegradients:
2676 self.RefreshSelected()
2677
2678
2679 def GetFirstGradientColour(self):
2680 """Returns the first gradient colour."""
2681
2682 return self._firstcolour
2683
2684
2685 def GetSecondGradientColour(self):
2686 """Returns the second gradient colour."""
2687
2688 return self._secondcolour
2689
2690
2691 def EnableSelectionGradient(self, enable=True):
2692 """Globally enables/disables drawing of gradient selection."""
2693
2694 self._usegradients = enable
2695 self._vistaselection = False
2696 self.RefreshSelected()
2697
2698
2699 def SetGradientStyle(self, vertical=0):
2700 """
2701 Sets the gradient style:
2702 0: horizontal gradient
2703 1: vertical gradient
2704 """
2705
2706 # 0 = Horizontal, 1 = Vertical
2707 self._gradientstyle = vertical
2708
2709 if self._usegradients:
2710 self.RefreshSelected()
2711
2712
2713 def GetGradientStyle(self):
2714 """
2715 Returns the gradient style:
2716 0: horizontal gradient
2717 1: vertical gradient
2718 """
2719
2720 return self._gradientstyle
2721
2722
2723 def EnableSelectionVista(self, enable=True):
2724 """Globally enables/disables drawing of Windows Vista selection."""
2725
2726 self._usegradients = False
2727 self._vistaselection = enable
2728 self.RefreshSelected()
2729
2730
2731 def SetBorderPen(self, pen):
2732 """
2733 Sets the pen used to draw the selected item border.
2734 The border pen is not used if the Windows Vista style is applied.
2735 """
2736
2737 self._borderPen = pen
2738 self.RefreshSelected()
2739
2740
2741 def GetBorderPen(self):
2742 """
2743 Returns the pen used to draw the selected item border.
2744 The border pen is not used if the Windows Vista style is applied.
2745 """
2746
2747 return self._borderPen
2748
2749
2750 def SetConnectionPen(self, pen):
2751 """Sets the pen used to draw the connecting lines between items."""
2752
2753 self._dottedPen = pen
2754 self._dirty = True
2755
2756
2757 def GetConnectionPen(self):
2758 """Returns the pen used to draw the connecting lines between items."""
2759
2760 return self._dottedPen
2761
2762
2763 def SetBackgroundImage(self, image):
2764 """Sets the CustomTreeCtrl background image (can be none)."""
2765
2766 self._backgroundImage = image
2767 self.Refresh()
2768
2769
2770 def GetBackgroundImage(self):
2771 """Returns the CustomTreeCtrl background image (can be none)."""
2772
2773 return self._backgroundImage
2774
2775
2776 def GetItemWindow(self, item):
2777 """Returns the window associated to the item (if any)."""
2778
2779 if not item:
2780 raise "\nERROR: Invalid Item"
2781
2782 return item.GetWindow()
2783
2784
2785 def GetItemWindowEnabled(self, item):
2786 """Returns whether the window associated to the item is enabled."""
2787
2788 if not item:
2789 raise "\nERROR: Invalid Item"
2790
2791 return item.GetWindowEnabled()
2792
2793
2794 def SetItemWindowEnabled(self, item, enable=True):
2795 """Enables/disables the window associated to the item."""
2796
2797 if not item:
2798 raise "\nERROR: Invalid Item"
2799
2800 item.SetWindowEnabled(enable)
2801
2802
2803 def GetItemType(self, item):
2804 """
2805 Returns the item type:
2806 0: normal
2807 1: checkbox item
2808 2: radiobutton item
2809 """
2810
2811 if not item:
2812 raise "\nERROR: Invalid Item"
2813
2814 return item.GetType()
2815
2816 # -----------------------------------------------------------------------------
2817 # item status inquiries
2818 # -----------------------------------------------------------------------------
2819
2820 def IsVisible(self, item):
2821 """Returns whether the item is visible or not."""
2822
2823 if not item:
2824 raise "\nERROR: Invalid Tree Item. "
2825
2826 # An item is only visible if it's not a descendant of a collapsed item
2827 parent = item.GetParent()
2828
2829 while parent:
2830
2831 if not parent.IsExpanded():
2832 return False
2833
2834 parent = parent.GetParent()
2835
2836 startX, startY = self.GetViewStart()
2837 clientSize = self.GetClientSize()
2838
2839 rect = self.GetBoundingRect(item)
2840
2841 if not rect:
2842 return False
2843 if rect.GetWidth() == 0 or rect.GetHeight() == 0:
2844 return False
2845 if rect.GetBottom() < 0 or rect.GetTop() > clientSize.y:
2846 return False
2847 if rect.GetRight() < 0 or rect.GetLeft() > clientSize.x:
2848 return False
2849
2850 return True
2851
2852
2853 def ItemHasChildren(self, item):
2854 """Returns whether the item has children or not."""
2855
2856 if not item:
2857 raise "\nERROR: Invalid Tree Item. "
2858
2859 # consider that the item does have children if it has the "+" button: it
2860 # might not have them (if it had never been expanded yet) but then it
2861 # could have them as well and it's better to err on this side rather than
2862 # disabling some operations which are restricted to the items with
2863 # children for an item which does have them
2864 return item.HasPlus()
2865
2866
2867 def IsExpanded(self, item):
2868 """Returns whether the item is expanded or not."""
2869
2870 if not item:
2871 raise "\nERROR: Invalid Tree Item. "
2872
2873 return item.IsExpanded()
2874
2875
2876 def IsSelected(self, item):
2877 """Returns whether the item is selected or not."""
2878
2879 if not item:
2880 raise "\nERROR: Invalid Tree Item. "
2881
2882 return item.IsSelected()
2883
2884
2885 def IsBold(self, item):
2886 """Returns whether the item font is bold or not."""
2887
2888 if not item:
2889 raise "\nERROR: Invalid Tree Item. "
2890
2891 return item.IsBold()
2892
2893
2894 def IsItalic(self, item):
2895 """Returns whether the item font is italic or not."""
2896
2897 if not item:
2898 raise "\nERROR: Invalid Tree Item. "
2899
2900 return item.IsItalic()
2901
2902
2903 # -----------------------------------------------------------------------------
2904 # navigation
2905 # -----------------------------------------------------------------------------
2906
2907 def GetItemParent(self, item):
2908 """Gets the item parent."""
2909
2910 if not item:
2911 raise "\nERROR: Invalid Tree Item. "
2912
2913 return item.GetParent()
2914
2915
2916 def GetFirstChild(self, item):
2917 """Gets the item first child."""
2918
2919 if not item:
2920 raise "\nERROR: Invalid Tree Item. "
2921
2922 cookie = 0
2923 return self.GetNextChild(item, cookie)
2924
2925
2926 def GetNextChild(self, item, cookie):
2927 """
2928 Gets the item next child based on the 'cookie' parameter.
2929 This method has no sense if you do not call GetFirstChild() before.
2930 """
2931
2932 if not item:
2933 raise "\nERROR: Invalid Tree Item. "
2934
2935 children = item.GetChildren()
2936
2937 # it's ok to cast cookie to size_t, we never have indices big enough to
2938 # overflow "void *"
2939
2940 if cookie < len(children):
2941
2942 return children[cookie], cookie+1
2943
2944 else:
2945
2946 # there are no more of them
2947 return None, cookie
2948
2949
2950 def GetLastChild(self, item):
2951 """Gets the item last child."""
2952
2953 if not item:
2954 raise "\nERROR: Invalid Tree Item. "
2955
2956 children = item.GetChildren()
2957 return (len(children) == 0 and [None] or [children[-1]])[0]
2958
2959
2960 def GetNextSibling(self, item):
2961 """Gets the next sibling of an item."""
2962
2963 if not item:
2964 raise "\nERROR: Invalid Tree Item. "
2965
2966 i = item
2967 parent = i.GetParent()
2968
2969 if parent == None:
2970
2971 # root item doesn't have any siblings
2972 return None
2973
2974 siblings = parent.GetChildren()
2975 index = siblings.index(i)
2976
2977 n = index + 1
2978 return (n == len(siblings) and [None] or [siblings[n]])[0]
2979
2980
2981 def GetPrevSibling(self, item):
2982 """Gets the previous sibling of an item."""
2983
2984 if not item:
2985 raise "\nERROR: Invalid Tree Item. "
2986
2987 i = item
2988 parent = i.GetParent()
2989
2990 if parent == None:
2991
2992 # root item doesn't have any siblings
2993 return None
2994
2995 siblings = parent.GetChildren()
2996 index = siblings.index(i)
2997
2998 return (index == 0 and [None] or [siblings[index-1]])[0]
2999
3000
3001 def GetNext(self, item):
3002 """Gets the next item. Only for internal use right now."""
3003
3004 if not item:
3005 raise "\nERROR: Invalid Tree Item. "
3006
3007 i = item
3008
3009 # First see if there are any children.
3010 children = i.GetChildren()
3011 if len(children) > 0:
3012 return children[0]
3013 else:
3014 # Try a sibling of this or ancestor instead
3015 p = item
3016 toFind = None
3017 while p and not toFind:
3018 toFind = self.GetNextSibling(p)
3019 p = self.GetItemParent(p)
3020
3021 return toFind
3022
3023
3024 def GetFirstVisibleItem(self):
3025 """Returns the first visible item."""
3026
3027 id = self.GetRootItem()
3028 if not id:
3029 return id
3030
3031 while id:
3032 if self.IsVisible(id):
3033 return id
3034 id = self.GetNext(id)
3035
3036 return None
3037
3038
3039 def GetNextVisible(self, item):
3040 """Returns the next visible item."""
3041
3042 if not item:
3043 raise "\nERROR: Invalid Tree Item. "
3044
3045 id = item
3046
3047 while id:
3048 id = self.GetNext(id)
3049 if id and self.IsVisible(id):
3050 return id
3051
3052 return None
3053
3054
3055 def GetPrevVisible(self, item):
3056
3057 if not item:
3058 raise "\nERROR: Invalid Tree Item. "
3059
3060 raise "\nERROR: Not Implemented"
3061
3062 return None
3063
3064
3065 def ResetTextControl(self):
3066 """Called by TreeTextCtrl when it marks itself for deletion."""
3067
3068 self._textCtrl.Destroy()
3069 self._textCtrl = None
3070
3071
3072 def FindItem(self, idParent, prefixOrig):
3073 """Finds the first item starting with the given prefix after the given item."""
3074
3075 # match is case insensitive as this is more convenient to the user: having
3076 # to press Shift-letter to go to the item starting with a capital letter
3077 # would be too bothersome
3078 prefix = prefixOrig.lower()
3079
3080 # determine the starting point: we shouldn't take the current item (this
3081 # allows to switch between two items starting with the same letter just by
3082 # pressing it) but we shouldn't jump to the next one if the user is
3083 # continuing to type as otherwise he might easily skip the item he wanted
3084 id = idParent
3085
3086 if len(prefix) == 1:
3087 id = self.GetNext(id)
3088
3089 # look for the item starting with the given prefix after it
3090 while id and not self.GetItemText(id).lower().startswith(prefix):
3091
3092 id = self.GetNext(id)
3093
3094 # if we haven't found anything...
3095 if not id:
3096
3097 # ... wrap to the beginning
3098 id = self.GetRootItem()
3099 if self.HasFlag(TR_HIDE_ROOT):
3100 # can't select virtual root
3101 id = self.GetNext(id)
3102
3103 # and try all the items (stop when we get to the one we started from)
3104 while id != idParent and not self.GetItemText(id).lower().startswith(prefix):
3105 id = self.GetNext(id)
3106
3107 return id
3108
3109
3110 # -----------------------------------------------------------------------------
3111 # operations
3112 # -----------------------------------------------------------------------------
3113
3114 def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3115 """Actually inserts an item in the tree."""
3116
3117 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3118 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3119
3120 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3121 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3122
3123 if ct_type < 0 or ct_type > 2:
3124 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3125
3126 parent = parentId
3127
3128 if not parent:
3129
3130 # should we give a warning here?
3131 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3132
3133 self._dirty = True # do this first so stuff below doesn't cause flicker
3134
3135 item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data)
3136
3137 if wnd is not None:
3138 self._hasWindows = True
3139 self._itemWithWindow.append(item)
3140
3141 parent.Insert(item, previous)
3142
3143 return item
3144
3145
3146 def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3147 """Adds a root to the CustomTreeCtrl. Only one root must exist."""
3148
3149 if self._anchor:
3150 raise "\nERROR: Tree Can Have Only One Root"
3151
3152 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3153 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3154
3155 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3156 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3157
3158 if ct_type < 0 or ct_type > 2:
3159 raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). "
3160
3161 self._dirty = True # do this first so stuff below doesn't cause flicker
3162
3163 self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data)
3164
3165 if wnd is not None:
3166 self._hasWindows = True
3167 self._itemWithWindow.append(self._anchor)
3168
3169 if self.HasFlag(TR_HIDE_ROOT):
3170
3171 # if root is hidden, make sure we can navigate
3172 # into children
3173 self._anchor.SetHasPlus()
3174 self._anchor.Expand()
3175 self.CalculatePositions()
3176
3177 if not self.HasFlag(TR_MULTIPLE):
3178
3179 self._current = self._key_current = self._anchor
3180 self._current.SetHilight(True)
3181
3182 return self._anchor
3183
3184
3185 def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3186 """Appends an item as a first child of parent."""
3187
3188 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3189 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3190
3191 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3192 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3193
3194 return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data)
3195
3196
3197 def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3198 """Auxiliary function to cope with the C++ hideous multifunction."""
3199
3200 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3201 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3202
3203 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3204 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3205
3206 parent = parentId
3207
3208 if not parent:
3209 # should we give a warning here?
3210 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3211
3212 index = -1
3213 if idPrevious:
3214
3215 try:
3216 index = parent.GetChildren().index(idPrevious)
3217 except:
3218 raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling"
3219
3220 return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data)
3221
3222
3223 def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3224 """Auxiliary function to cope with the C++ hideous multifunction."""
3225
3226 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3227 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3228
3229 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3230 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3231
3232 parent = parentId
3233
3234 if not parent:
3235 # should we give a warning here?
3236 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3237
3238 return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data)
3239
3240
3241 def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3242 """Inserts an item after the given previous."""
3243
3244 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3245 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3246
3247 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3248 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3249
3250 if type(input) == type(1):
3251 return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data)
3252 else:
3253 return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data)
3254
3255
3256 def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
3257 """Appends an item as a last child of its parent."""
3258
3259 if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3260 raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3261
3262 if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
3263 raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT"
3264
3265 parent = parentId
3266
3267 if not parent:
3268 # should we give a warning here?
3269 return self.AddRoot(text, ct_type, wnd, image, selImage, data)
3270
3271 return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data)
3272
3273
3274 def SendDeleteEvent(self, item):
3275 """Actully sends the EVT_TREE_DELETE_ITEM event."""
3276
3277 event = TreeEvent(wxEVT_TREE_DELETE_ITEM, self.GetId())
3278 event._item = item
3279 event.SetEventObject(self)
3280 self.ProcessEvent(event)
3281
3282
3283 def IsDescendantOf(self, parent, item):
3284 """Checks if the given item is under another one."""
3285
3286 while item:
3287
3288 if item == parent:
3289
3290 # item is a descendant of parent
3291 return True
3292
3293 item = item.GetParent()
3294
3295 return False
3296
3297
3298 # Don't leave edit or selection on a child which is about to disappear
3299 def ChildrenClosing(self, item):
3300 """We are about to destroy the item children."""
3301
3302 if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()):
3303 self._textCtrl.StopEditing()
3304
3305 if item != self._key_current and self.IsDescendantOf(item, self._key_current):
3306 self._key_current = None
3307
3308 if self.IsDescendantOf(item, self._select_me):
3309 self._select_me = item
3310
3311 if item != self._current and self.IsDescendantOf(item, self._current):
3312 self._current.SetHilight(False)
3313 self._current = None
3314 self._select_me = item
3315
3316
3317 def DeleteChildren(self, item):
3318 """Delete item children."""
3319
3320 if not item:
3321 raise "\nERROR: Invalid Tree Item. "
3322
3323 self._dirty = True # do this first so stuff below doesn't cause flicker
3324
3325 self.ChildrenClosing(item)
3326 item.DeleteChildren(self)
3327
3328
3329 def Delete(self, item):
3330 """Delete an item."""
3331
3332 if not item:
3333 raise "\nERROR: Invalid Tree Item. "
3334
3335 self._dirty = True # do this first so stuff below doesn't cause flicker
3336
3337 if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()):
3338 # can't delete the item being edited, cancel editing it first
3339 self._textCtrl.StopEditing()
3340
3341 parent = item.GetParent()
3342
3343 # don't keep stale pointers around!
3344 if self.IsDescendantOf(item, self._key_current):
3345
3346 # Don't silently change the selection:
3347 # do it properly in idle time, so event
3348 # handlers get called.
3349
3350 # self._key_current = parent
3351 self._key_current = None
3352
3353 # self._select_me records whether we need to select
3354 # a different item, in idle time.
3355 if self._select_me and self.IsDescendantOf(item, self._select_me):
3356 self._select_me = parent
3357
3358 if self.IsDescendantOf(item, self._current):
3359
3360 # Don't silently change the selection:
3361 # do it properly in idle time, so event
3362 # handlers get called.
3363
3364 # self._current = parent
3365 self._current = None
3366 self._select_me = parent
3367
3368 # remove the item from the tree
3369 if parent:
3370
3371 parent.GetChildren().remove(item) # remove by value
3372
3373 else: # deleting the root
3374
3375 # nothing will be left in the tree
3376 self._anchor = None
3377
3378 # and delete all of its children and the item itself now
3379 item.DeleteChildren(self)
3380 self.SendDeleteEvent(item)
3381
3382 if item == self._select_me:
3383 self._select_me = None
3384
3385 # Remove the item with window
3386 if item in self._itemWithWindow:
3387 wnd = item.GetWindow()
3388 wnd.Hide()
3389 wnd.Destroy()
3390 item._wnd = None
3391 self._itemWithWindow.remove(item)
3392
3393 del item
3394
3395
3396 def DeleteAllItems(self):
3397 """Delete all items in the CustomTreeCtrl."""
3398
3399 if self._anchor:
3400 self.Delete(self._anchor)
3401
3402
3403 def Expand(self, item):
3404 """
3405 Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
3406 EVT_TREE_ITEM_EXPANDED events.
3407 """
3408
3409 if not item:
3410 raise "\nERROR: Invalid Tree Item. "
3411
3412 if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
3413 raise "\nERROR: Can't Expand An Hidden Root. "
3414
3415 if not item.HasPlus():
3416 return
3417
3418 if item.IsExpanded():
3419 return
3420
3421 event = TreeEvent(wxEVT_TREE_ITEM_EXPANDING, self.GetId())
3422 event._item = item
3423 event.SetEventObject(self)
3424
3425 if self.ProcessEvent(event) and not event.IsAllowed():
3426 # cancelled by program
3427 return
3428
3429 item.Expand()
3430 self.CalculatePositions()
3431
3432 self.RefreshSubtree(item)
3433
3434 if self._hasWindows:
3435 # We hide the associated window here, we may show it after
3436 self.HideWindows()
3437
3438 event.SetEventType(wxEVT_TREE_ITEM_EXPANDED)
3439 self.ProcessEvent(event)
3440
3441
3442 def ExpandAll(self, item):
3443 """Expands all the items."""
3444
3445 if not item:
3446 raise "\nERROR: Invalid Tree Item. "
3447
3448 if not self.HasFlag(TR_HIDE_ROOT) or item != GetRootItem():
3449 self.Expand(item)
3450 if not self.IsExpanded(item):
3451 return
3452
3453 child, cookie = self.GetFirstChild(item)
3454
3455 while child:
3456 self.ExpandAll(child)
3457 child, cookie = self.GetNextChild(item, cookie)
3458
3459
3460 def Collapse(self, item):
3461 """
3462 Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
3463 EVT_TREE_ITEM_COLLAPSED events.
3464 """
3465
3466 if not item:
3467 raise "\nERROR: Invalid Tree Item. "
3468
3469 if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
3470 raise "\nERROR: Can't Collapse An Hidden Root. "
3471
3472 if not item.IsExpanded():
3473 return
3474
3475 event = TreeEvent(wxEVT_TREE_ITEM_COLLAPSING, self.GetId())
3476 event._item = item
3477 event.SetEventObject(self)
3478 if self.ProcessEvent(event) and not event.IsAllowed():
3479 # cancelled by program
3480 return
3481
3482 self.ChildrenClosing(item)
3483 item.Collapse()
3484
3485 self.CalculatePositions()
3486 self.RefreshSubtree(item)
3487
3488 if self._hasWindows:
3489 self.HideWindows()
3490
3491 event.SetEventType(wxEVT_TREE_ITEM_COLLAPSED)
3492 self.ProcessEvent(event)
3493
3494
3495 def CollapseAndReset(self, item):
3496 """Collapse the given item and deletes its children."""
3497
3498 self.Collapse(item)
3499 self.DeleteChildren(item)
3500
3501
3502 def Toggle(self, item):
3503 """Toggles the item state (collapsed/expanded)."""
3504
3505 if item.IsExpanded():
3506 self.Collapse(item)
3507 else:
3508 self.Expand(item)
3509
3510
3511 def HideWindows(self):
3512 """Hides the windows associated to the items. Used internally."""
3513
3514 for child in self._itemWithWindow:
3515 if not self.IsVisible(child):
3516 wnd = child.GetWindow()
3517 wnd.Hide()
3518
3519
3520 def Unselect(self):
3521 """Unselects the current selection."""
3522
3523 if self._current:
3524
3525 self._current.SetHilight(False)
3526 self.RefreshLine(self._current)
3527
3528 self._current = None
3529 self._select_me = None
3530
3531
3532 def UnselectAllChildren(self, item):
3533 """Unselects all the children of the given item."""
3534
3535 if item.IsSelected():
3536
3537 item.SetHilight(False)
3538 self.RefreshLine(item)
3539
3540 if item.HasChildren():
3541 for child in item.GetChildren():
3542 self.UnselectAllChildren(child)
3543
3544
3545 def UnselectAll(self):
3546 """Unselect all the items."""
3547
3548 rootItem = self.GetRootItem()
3549
3550 # the tree might not have the root item at all
3551 if rootItem:
3552 self.UnselectAllChildren(rootItem)
3553
3554
3555 # Recursive function !
3556 # To stop we must have crt_item<last_item
3557 # Algorithm :
3558 # Tag all next children, when no more children,
3559 # Move to parent (not to tag)
3560 # Keep going... if we found last_item, we stop.
3561
3562 def TagNextChildren(self, crt_item, last_item, select):
3563 """Used internally."""
3564
3565 parent = crt_item.GetParent()
3566
3567 if parent == None: # This is root item
3568 return self.TagAllChildrenUntilLast(crt_item, last_item, select)
3569
3570 children = parent.GetChildren()
3571 index = children.index(crt_item)
3572
3573 count = len(children)
3574
3575 for n in xrange(index+1, count):
3576 if self.TagAllChildrenUntilLast(children[n], last_item, select):
3577 return True
3578
3579 return self.TagNextChildren(parent, last_item, select)
3580
3581
3582 def TagAllChildrenUntilLast(self, crt_item, last_item, select):
3583 """Used internally."""
3584
3585 crt_item.SetHilight(select)
3586 self.RefreshLine(crt_item)
3587
3588 if crt_item == last_item:
3589 return True
3590
3591 if crt_item.HasChildren():
3592 for child in crt_item.GetChildren():
3593 if self.TagAllChildrenUntilLast(child, last_item, select):
3594 return True
3595
3596 return False
3597
3598
3599 def SelectItemRange(self, item1, item2):
3600 """Selects all the items between item1 and item2."""
3601
3602 self._select_me = None
3603
3604 # item2 is not necessary after item1
3605 # choice first' and 'last' between item1 and item2
3606 first = (item1.GetY() < item2.GetY() and [item1] or [item2])[0]
3607 last = (item1.GetY() < item2.GetY() and [item2] or [item1])[0]
3608
3609 select = self._current.IsSelected()
3610
3611 if self.TagAllChildrenUntilLast(first, last, select):
3612 return
3613
3614 self.TagNextChildren(first, last, select)
3615
3616
3617 def DoSelectItem(self, item, unselect_others=True, extended_select=False):
3618 """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
3619
3620 if not item:
3621 raise "\nERROR: Invalid Tree Item. "
3622
3623 self._select_me = None
3624
3625 is_single = not (self.GetTreeStyle() & TR_MULTIPLE)
3626
3627 # to keep going anyhow !!!
3628 if is_single:
3629 if item.IsSelected():
3630 return # nothing to do
3631 unselect_others = True
3632 extended_select = False
3633
3634 elif unselect_others and item.IsSelected():
3635
3636 # selection change if there is more than one item currently selected
3637 if len(self.GetSelections()) == 1:
3638 return
3639
3640 event = TreeEvent(wxEVT_TREE_SEL_CHANGING, self.GetId())
3641 event._item = item
3642 event._itemOld = self._current
3643 event.SetEventObject(self)
3644 # TODO : Here we don't send any selection mode yet !
3645
3646 if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed():
3647 return
3648
3649 parent = self.GetItemParent(item)
3650 while parent:
3651 if not self.IsExpanded(parent):
3652 self.Expand(parent)
3653
3654 parent = self.GetItemParent(parent)
3655
3656 # ctrl press
3657 if unselect_others:
3658 if is_single:
3659 self.Unselect() # to speed up thing
3660 else:
3661 self.UnselectAll()
3662
3663 # shift press
3664 if extended_select:
3665 if not self._current:
3666 self._current = self._key_current = self.GetRootItem()
3667
3668 # don't change the mark (self._current)
3669 self.SelectItemRange(self._current, item)
3670
3671 else:
3672
3673 select = True # the default
3674
3675 # Check if we need to toggle hilight (ctrl mode)
3676 if not unselect_others:
3677 select = not item.IsSelected()
3678
3679 self._current = self._key_current = item
3680 self._current.SetHilight(select)
3681 self.RefreshLine(self._current)
3682
3683 # This can cause idle processing to select the root
3684 # if no item is selected, so it must be after the
3685 # selection is set
3686 self.EnsureVisible(item)
3687
3688 event.SetEventType(wxEVT_TREE_SEL_CHANGED)
3689 self.GetEventHandler().ProcessEvent(event)
3690
3691 # Handles hypertext items
3692 if self.IsItemHyperText(item):
3693 event = TreeEvent(wxEVT_TREE_ITEM_HYPERLINK, self.GetId())
3694 event._item = item
3695 self.GetEventHandler().ProcessEvent(event)
3696
3697
3698 def SelectItem(self, item, select=True):
3699 """Selects/deselects an item."""
3700
3701 if not item:
3702 raise "\nERROR: Invalid Tree Item. "
3703
3704 if select:
3705
3706 self.DoSelectItem(item, not self.HasFlag(TR_MULTIPLE))
3707
3708 else: # deselect
3709
3710 item.SetHilight(False)
3711 self.RefreshLine(item)
3712
3713
3714 def FillArray(self, item, array=[]):
3715 """
3716 Internal function. Used to populate an array of selected items when
3717 the style TR_MULTIPLE is used.
3718 """
3719
3720 if not array:
3721 array = []
3722
3723 if item.IsSelected():
3724 array.append(item)
3725
3726 if item.HasChildren():
3727 for child in item.GetChildren():
3728 array = self.FillArray(child, array)
3729
3730 return array
3731
3732
3733 def GetSelections(self):
3734 """
3735 Returns a list of selected items. This can be used only if CustomTreeCtrl has
3736 the TR_MULTIPLE style set.
3737 """
3738
3739 array = []
3740 idRoot = self.GetRootItem()
3741 if idRoot:
3742 array = self.FillArray(idRoot, array)
3743
3744 #else: the tree is empty, so no selections
3745
3746 return array
3747
3748
3749 def EnsureVisible(self, item):
3750 """Ensure that an item is visible in CustomTreeCtrl."""
3751
3752 if not item:
3753 raise "\nERROR: Invalid Tree Item. "
3754
3755 # first expand all parent branches
3756 parent = item.GetParent()
3757
3758 if self.HasFlag(TR_HIDE_ROOT):
3759 while parent and parent != self._anchor:
3760 self.Expand(parent)
3761 parent = parent.GetParent()
3762 else:
3763 while parent:
3764 self.Expand(parent)
3765 parent = parent.GetParent()
3766
3767 self.ScrollTo(item)
3768
3769
3770 def ScrollTo(self, item):
3771 """Scrolls the specified item into view."""
3772
3773 if not item:
3774 return
3775
3776 # We have to call this here because the label in
3777 # question might just have been added and no screen
3778 # update taken place.
3779 if self._dirty:
3780 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
3781 self.Update()
3782 else:
3783 wx.YieldIfNeeded()
3784
3785 # now scroll to the item
3786 item_y = item.GetY()
3787 start_x, start_y = self.GetViewStart()
3788 start_y *= _PIXELS_PER_UNIT
3789
3790 client_w, client_h = self.GetClientSize()
3791
3792 x, y = 0, 0
3793
3794 if item_y < start_y+3:
3795
3796 # going down
3797 x, y = self._anchor.GetSize(x, y, self)
3798 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3799 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3800 x_pos = self.GetScrollPos(wx.HORIZONTAL)
3801 # Item should appear at top
3802 self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, item_y/_PIXELS_PER_UNIT)
3803
3804 elif item_y+self.GetLineHeight(item) > start_y+client_h:
3805
3806 # going up
3807 x, y = self._anchor.GetSize(x, y, self)
3808 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3809 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
3810 item_y += _PIXELS_PER_UNIT+2
3811 x_pos = self.GetScrollPos(wx.HORIZONTAL)
3812 # Item should appear at bottom
3813 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 )
3814
3815
3816 def OnCompareItems(self, item1, item2):
3817 """
3818 Returns whether 2 items have the same text.
3819 Override this function in the derived class to change the sort order of the items
3820 in the CustomTreeCtrl. The function should return a negative, zero or positive
3821 value if the first item is less than, equal to or greater than the second one.
3822
3823 The base class version compares items alphabetically.
3824 """
3825
3826 return self.GetItemText(item1) == self.GetItemText(item2)
3827
3828
3829 def SortChildren(self, item):
3830 """
3831 Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
3832 You should override that method to change the sort order (the default is ascending
3833 case-sensitive alphabetical order).
3834 """
3835
3836 if not item:
3837 raise "\nERROR: Invalid Tree Item. "
3838
3839 children = item.GetChildren()
3840
3841 if len(children) > 1:
3842 self._dirty = True
3843 children.sort(self.OnCompareItems)
3844
3845
3846 def GetImageList(self):
3847 """Returns the normal image list."""
3848
3849 return self._imageListNormal
3850
3851
3852 def GetButtonsImageList(self):
3853 """Returns the buttons image list (from which application-defined button images are taken)."""
3854
3855 return self._imageListButtons
3856
3857
3858 def GetStateImageList(self):
3859 """Returns the state image list (from which application-defined state images are taken)."""
3860
3861 return self._imageListState
3862
3863
3864 def GetImageListCheck(self):
3865 """Returns the image list used to build the check/radio buttons."""
3866
3867 return self._imageListCheck
3868
3869
3870 def CalculateLineHeight(self):
3871 """Calculates the height of a line."""
3872
3873 dc = wx.ClientDC(self)
3874 self._lineHeight = dc.GetCharHeight()
3875
3876 if self._imageListNormal:
3877
3878 # Calculate a self._lineHeight value from the normal Image sizes.
3879 # May be toggle off. Then CustomTreeCtrl will spread when
3880 # necessary (which might look ugly).
3881 n = self._imageListNormal.GetImageCount()
3882
3883 for i in xrange(n):
3884
3885 width, height = self._imageListNormal.GetSize(i)
3886
3887 if height > self._lineHeight:
3888 self._lineHeight = height
3889
3890 if self._imageListButtons:
3891
3892 # Calculate a self._lineHeight value from the Button image sizes.
3893 # May be toggle off. Then CustomTreeCtrl will spread when
3894 # necessary (which might look ugly).
3895 n = self._imageListButtons.GetImageCount()
3896
3897 for i in xrange(n):
3898
3899 width, height = self._imageListButtons.GetSize(i)
3900
3901 if height > self._lineHeight:
3902 self._lineHeight = height
3903
3904 if self._imageListCheck:
3905
3906 # Calculate a self._lineHeight value from the check/radio image sizes.
3907 # May be toggle off. Then CustomTreeCtrl will spread when
3908 # necessary (which might look ugly).
3909 n = self._imageListCheck.GetImageCount()
3910
3911 for i in xrange(n):
3912
3913 width, height = self._imageListCheck.GetSize(i)
3914
3915 if height > self._lineHeight:
3916 self._lineHeight = height
3917
3918 if self._lineHeight < 30:
3919 self._lineHeight += 2 # at least 2 pixels
3920 else:
3921 self._lineHeight += self._lineHeight/10 # otherwise 10% extra spacing
3922
3923
3924 def SetImageList(self, imageList):
3925 """Sets the normal image list."""
3926
3927 if self._ownsImageListNormal:
3928 del self._imageListNormal
3929
3930 self._imageListNormal = imageList
3931 self._ownsImageListNormal = False
3932 self._dirty = True
3933 # Don't do any drawing if we're setting the list to NULL,
3934 # since we may be in the process of deleting the tree control.
3935 if imageList:
3936 self.CalculateLineHeight()
3937
3938 # We gray out the image list to use the grayed icons with disabled items
3939 self._grayedImageList = wx.ImageList(16, 16, True, 0)
3940
3941 for ii in xrange(imageList.GetImageCount()):
3942
3943 bmp = imageList.GetBitmap(ii)
3944 image = wx.ImageFromBitmap(bmp)
3945 image = GrayOut(image)
3946 newbmp = wx.BitmapFromImage(image)
3947 self._grayedImageList.Add(newbmp)
3948
3949
3950 def SetStateImageList(self, imageList):
3951 """Sets the state image list (from which application-defined state images are taken)."""
3952
3953 if self._ownsImageListState:
3954 del self._imageListState
3955
3956 self._imageListState = imageList
3957 self._ownsImageListState = False
3958
3959
3960 def SetButtonsImageList(self, imageList):
3961 """Sets the buttons image list (from which application-defined button images are taken)."""
3962
3963 if self._ownsImageListButtons:
3964 del self._imageListButtons
3965
3966 self._imageListButtons = imageList
3967 self._ownsImageListButtons = False
3968 self._dirty = True
3969 self.CalculateLineHeight()
3970
3971
3972 def SetImageListCheck(self, sizex, sizey, imglist=None):
3973 """Sets the check image list."""
3974
3975 if imglist is None:
3976
3977 self._imageListCheck = wx.ImageList(sizex, sizey)
3978 self._imageListCheck.Add(GetCheckedBitmap())
3979 self._imageListCheck.Add(GetNotCheckedBitmap())
3980 self._imageListCheck.Add(GetFlaggedBitmap())
3981 self._imageListCheck.Add(GetNotFlaggedBitmap())
3982
3983 else:
3984
3985 sizex, sizey = imglist.GetSize(0)
3986 self._imageListCheck = imglist
3987
3988 # We gray out the image list to use the grayed icons with disabled items
3989 self._grayedCheckList = wx.ImageList(sizex, sizey, True, 0)
3990
3991 for ii in xrange(self._imageListCheck.GetImageCount()):
3992
3993 bmp = self._imageListCheck.GetBitmap(ii)
3994 image = wx.ImageFromBitmap(bmp)
3995 image = GrayOut(image)
3996 newbmp = wx.BitmapFromImage(image)
3997 self._grayedCheckList.Add(newbmp)
3998
3999 self._dirty = True
4000
4001 if imglist:
4002 self.CalculateLineHeight()
4003
4004
4005 def AssignImageList(self, imageList):
4006 """Assigns the normal image list."""
4007
4008 self.SetImageList(imageList)
4009 self._ownsImageListNormal = True
4010
4011
4012 def AssignStateImageList(self, imageList):
4013 """Assigns the state image list."""
4014
4015 self.SetStateImageList(imageList)
4016 self._ownsImageListState = True
4017
4018
4019 def AssignButtonsImageList(self, imageList):
4020 """Assigns the button image list."""
4021
4022 self.SetButtonsImageList(imageList)
4023 self._ownsImageListButtons = True
4024
4025
4026 # -----------------------------------------------------------------------------
4027 # helpers
4028 # -----------------------------------------------------------------------------
4029
4030 def AdjustMyScrollbars(self):
4031 """Adjust the wx.ScrolledWindow scrollbars."""
4032
4033 if self._anchor:
4034
4035 x, y = self._anchor.GetSize(0, 0, self)
4036 y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
4037 x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
4038 x_pos = self.GetScrollPos(wx.HORIZONTAL)
4039 y_pos = self.GetScrollPos(wx.VERTICAL)
4040 self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, y_pos)
4041
4042 else:
4043
4044 self.SetScrollbars(0, 0, 0, 0)
4045
4046
4047 def GetLineHeight(self, item):
4048 """Returns the line height for the given item."""
4049
4050 if self.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT:
4051 return item.GetHeight()
4052 else:
4053 return self._lineHeight
4054
4055
4056 def DrawVerticalGradient(self, dc, rect, hasfocus):
4057 """Gradient fill from colour 1 to colour 2 from top to bottom."""
4058
4059 dc.DrawRectangleRect(rect)
4060 border = self._borderPen.GetWidth()
4061
4062 dc.SetPen(wx.TRANSPARENT_PEN)
4063
4064 # calculate gradient coefficients
4065 if hasfocus:
4066 col2 = self._secondcolour
4067 col1 = self._firstcolour
4068 else:
4069 col2 = self._hilightUnfocusedBrush.GetColour()
4070 col1 = self._hilightUnfocusedBrush2.GetColour()
4071
4072 r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
4073 r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
4074
4075 flrect = float(rect.height)
4076
4077 rstep = float((r2 - r1)) / flrect
4078 gstep = float((g2 - g1)) / flrect
4079 bstep = float((b2 - b1)) / flrect
4080
4081 rf, gf, bf = 0, 0, 0
4082
4083 for y in xrange(rect.y+border, rect.y + rect.height-border):
4084 currCol = (r1 + rf, g1 + gf, b1 + bf)
4085 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4086 dc.DrawRectangle(rect.x+border, y, rect.width-2*border, 1)
4087 rf = rf + rstep
4088 gf = gf + gstep
4089 bf = bf + bstep
4090
4091
4092 def DrawHorizontalGradient(self, dc, rect, hasfocus):
4093 """Gradient fill from colour 1 to colour 2 from left to right."""
4094
4095 dc.DrawRectangleRect(rect)
4096 border = self._borderPen.GetWidth()
4097
4098 dc.SetPen(wx.TRANSPARENT_PEN)
4099
4100 # calculate gradient coefficients
4101
4102 if hasfocus:
4103 col2 = self._secondcolour
4104 col1 = self._firstcolour
4105 else:
4106 col2 = self._hilightUnfocusedBrush.GetColour()
4107 col1 = self._hilightUnfocusedBrush2.GetColour()
4108
4109 r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
4110 r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
4111
4112 flrect = float(rect.width)
4113
4114 rstep = float((r2 - r1)) / flrect
4115 gstep = float((g2 - g1)) / flrect
4116 bstep = float((b2 - b1)) / flrect
4117
4118 rf, gf, bf = 0, 0, 0
4119
4120 for x in xrange(rect.x+border, rect.x + rect.width-border):
4121 currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf))
4122 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4123 dc.DrawRectangle(x, rect.y+border, 1, rect.height-2*border)
4124 rf = rf + rstep
4125 gf = gf + gstep
4126 bf = bf + bstep
4127
4128
4129 def DrawVistaRectangle(self, dc, rect, hasfocus):
4130 """Draw the selected item(s) with the Windows Vista style."""
4131
4132 if hasfocus:
4133
4134 outer = _rgbSelectOuter
4135 inner = _rgbSelectInner
4136 top = _rgbSelectTop
4137 bottom = _rgbSelectBottom
4138
4139 else:
4140
4141 outer = _rgbNoFocusOuter
4142 inner = _rgbNoFocusInner
4143 top = _rgbNoFocusTop
4144 bottom = _rgbNoFocusBottom
4145
4146 oldpen = dc.GetPen()
4147 oldbrush = dc.GetBrush()
4148
4149 dc.SetBrush(wx.TRANSPARENT_BRUSH)
4150 dc.SetPen(wx.Pen(outer))
4151 dc.DrawRoundedRectangleRect(rect, 3)
4152 rect.Deflate(1, 1)
4153 dc.SetPen(wx.Pen(inner))
4154 dc.DrawRoundedRectangleRect(rect, 2)
4155 rect.Deflate(1, 1)
4156
4157 r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue())
4158 r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue())
4159
4160 flrect = float(rect.height)
4161
4162 rstep = float((r2 - r1)) / flrect
4163 gstep = float((g2 - g1)) / flrect
4164 bstep = float((b2 - b1)) / flrect
4165
4166 rf, gf, bf = 0, 0, 0
4167 dc.SetPen(wx.TRANSPARENT_PEN)
4168
4169 for y in xrange(rect.y, rect.y + rect.height):
4170 currCol = (r1 + rf, g1 + gf, b1 + bf)
4171 dc.SetBrush(wx.Brush(currCol, wx.SOLID))
4172 dc.DrawRectangle(rect.x, y, rect.width, 1)
4173 rf = rf + rstep
4174 gf = gf + gstep
4175 bf = bf + bstep
4176
4177 dc.SetPen(oldpen)
4178 dc.SetBrush(oldbrush)
4179
4180
4181 def PaintItem(self, item, dc):
4182 """Actually paint an item."""
4183
4184 attr = item.GetAttributes()
4185
4186 if attr and attr.HasFont():
4187 dc.SetFont(attr.GetFont())
4188 elif item.IsBold():
4189 dc.SetFont(self._boldFont)
4190 if item.IsHyperText():
4191 dc.SetFont(self.GetHyperTextFont())
4192 if item.GetVisited():
4193 dc.SetTextForeground(self.GetHyperTextVisitedColour())
4194 else:
4195 dc.SetTextForeground(self.GetHyperTextNewColour())
4196
4197 text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
4198
4199 image = item.GetCurrentImage()
4200 checkimage = item.GetCurrentCheckedImage()
4201 image_w, image_h = 0, 0
4202
4203 if image != _NO_IMAGE:
4204
4205 if self._imageListNormal:
4206
4207 image_w, image_h = self._imageListNormal.GetSize(image)
4208 image_w += 4
4209
4210 else:
4211
4212 image = _NO_IMAGE
4213
4214 if item.GetType() != 0:
4215 wcheck, hcheck = self._imageListCheck.GetSize(item.GetType())
4216 wcheck += 4
4217 else:
4218 wcheck, hcheck = 0, 0
4219
4220 total_h = self.GetLineHeight(item)
4221 drawItemBackground = False
4222
4223 if item.IsSelected():
4224
4225 # under mac selections are only a rectangle in case they don't have the focus
4226 if wx.Platform == "__WXMAC__":
4227 if not self._hasFocus:
4228 dc.SetBrush(wx.TRANSPARENT_BRUSH)
4229 dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), 1, wx.SOLID))
4230 else:
4231 dc.SetBrush(self._hilightBrush)
4232 else:
4233 dc.SetBrush((self._hasFocus and [self._hilightBrush] or [self._hilightUnfocusedBrush])[0])
4234 drawItemBackground = True
4235 else:
4236 if attr and attr.HasBackgroundColour():
4237 drawItemBackground = True
4238 colBg = attr.GetBackgroundColour()
4239 else:
4240 colBg = self._backgroundColour
4241
4242 dc.SetBrush(wx.Brush(colBg, wx.SOLID))
4243 dc.SetPen(wx.TRANSPARENT_PEN)
4244
4245 offset = (self.HasFlag(TR_ROW_LINES) and [1] or [0])[0]
4246
4247 if self.HasFlag(TR_FULL_ROW_HIGHLIGHT):
4248
4249 oldpen = dc.GetPen()
4250 dc.SetPen(wx.TRANSPARENT_PEN)
4251 x = 0
4252 w, h = self.GetSize()
4253
4254 itemrect = wx.Rect(x, item.GetY()+offset, w, total_h-offset)
4255
4256 if item.IsSelected():
4257 if self._usegradients:
4258 if self._gradientstyle == 0: # Horizontal
4259 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4260 else: # Vertical
4261 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4262 elif self._vistaselection:
4263 self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
4264 else:
4265 dc.DrawRectangleRect(itemrect)
4266
4267 dc.SetPen(oldpen)
4268
4269 else:
4270
4271 if item.IsSelected() and image != _NO_IMAGE:
4272
4273 # If it's selected, and there's an image, then we should
4274 # take care to leave the area under the image painted in the
4275 # background colour.
4276
4277 wnd = item.GetWindow()
4278 wndx = 0
4279 if wnd:
4280 wndx, wndy = item.GetWindowSize()
4281
4282 itemrect = wx.Rect(item.GetX() + wcheck + image_w - 2, item.GetY()+offset,
4283 item.GetWidth() - image_w - wcheck + 2 - wndx, total_h-offset)
4284
4285 if self._usegradients:
4286 if self._gradientstyle == 0: # Horizontal
4287 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4288 else: # Vertical
4289 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4290 elif self._vistaselection:
4291 self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
4292 else:
4293 dc.DrawRectangleRect(itemrect)
4294
4295 # On GTK+ 2, drawing a 'normal' background is wrong for themes that
4296 # don't allow backgrounds to be customized. Not drawing the background,
4297 # except for custom item backgrounds, works for both kinds of theme.
4298 elif drawItemBackground:
4299
4300 minusicon = wcheck + image_w - 2
4301 itemrect = wx.Rect(item.GetX()+minusicon, item.GetY()+offset, item.GetWidth()-minusicon, total_h-offset)
4302
4303 if self._usegradients and self._hasFocus:
4304 if self._gradientstyle == 0: # Horizontal
4305 self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
4306 else: # Vertical
4307 self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
4308 else:
4309 dc.DrawRectangleRect(itemrect)
4310
4311 if image != _NO_IMAGE:
4312
4313 dc.SetClippingRegion(item.GetX(), item.GetY(), wcheck+image_w-2, total_h)
4314 if item.IsEnabled():
4315 imglist = self._imageListNormal
4316 else:
4317 imglist = self._grayedImageList
4318
4319 imglist.Draw(image, dc,
4320 item.GetX() + wcheck,
4321 item.GetY() + ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0],
4322 wx.IMAGELIST_DRAW_TRANSPARENT)
4323
4324 dc.DestroyClippingRegion()
4325
4326 if wcheck:
4327 if item.IsEnabled():
4328 imglist = self._imageListCheck
4329 else:
4330 imglist = self._grayedCheckList
4331
4332 imglist.Draw(checkimage, dc,
4333 item.GetX(),
4334 item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0],
4335 wx.IMAGELIST_DRAW_TRANSPARENT)
4336
4337 dc.SetBackgroundMode(wx.TRANSPARENT)
4338 extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0]
4339
4340 textrect = wx.Rect(wcheck + image_w + item.GetX(), item.GetY() + extraH, text_w, text_h)
4341
4342 if not item.IsEnabled():
4343 foreground = dc.GetTextForeground()
4344 dc.SetTextForeground(self._disabledColour)
4345 dc.DrawLabel(item.GetText(), textrect)
4346 dc.SetTextForeground(foreground)
4347 else:
4348 dc.DrawLabel(item.GetText(), textrect)
4349
4350 wnd = item.GetWindow()
4351 if wnd:
4352 wndx = wcheck + image_w + item.GetX() + text_w + 4
4353 xa, ya = self.CalcScrolledPosition((0, item.GetY()))
4354 if not wnd.IsShown():
4355 wnd.Show()
4356 if wnd.GetPosition() != (wndx, ya):
4357 wnd.SetPosition((wndx, ya))
4358
4359 # restore normal font
4360 dc.SetFont(self._normalFont)
4361
4362
4363 # Now y stands for the top of the item, whereas it used to stand for middle !
4364 def PaintLevel(self, item, dc, level, y):
4365 """Paint a level of CustomTreeCtrl."""
4366
4367 x = level*self._indent
4368
4369 if not self.HasFlag(TR_HIDE_ROOT):
4370
4371 x += self._indent
4372
4373 elif level == 0:
4374
4375 # always expand hidden root
4376 origY = y
4377 children = item.GetChildren()
4378 count = len(children)
4379
4380 if count > 0:
4381 n = 0
4382 while n < count:
4383 oldY = y
4384 y = self.PaintLevel(children[n], dc, 1, y)
4385 n = n + 1
4386
4387 if not self.HasFlag(TR_NO_LINES) and self.HasFlag(TR_LINES_AT_ROOT) and count > 0:
4388
4389 # draw line down to last child
4390 origY += self.GetLineHeight(children[0])>>1
4391 oldY += self.GetLineHeight(children[n-1])>>1
4392 dc.DrawLine(3, origY, 3, oldY)
4393
4394 return y
4395
4396 item.SetX(x+self._spacing)
4397 item.SetY(y)
4398
4399 h = self.GetLineHeight(item)
4400 y_top = y
4401 y_mid = y_top + (h>>1)
4402 y += h
4403
4404 exposed_x = dc.LogicalToDeviceX(0)
4405 exposed_y = dc.LogicalToDeviceY(y_top)
4406
4407 if self.IsExposed(exposed_x, exposed_y, 10000, h): # 10000 = very much
4408 if wx.Platform == "__WXMAC__":
4409 # don't draw rect outline if we already have the
4410 # background color under Mac
4411 pen = ((item.IsSelected() and self._hasFocus) and [self._borderPen] or [wx.TRANSPARENT_PEN])[0]
4412 else:
4413 pen = self._borderPen
4414
4415 if item.IsSelected():
4416 if (wx.Platform == "__WXMAC__" and self._hasFocus):
4417 colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
4418 else:
4419 colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
4420 else:
4421 attr = item.GetAttributes()
4422 if attr and attr.HasTextColour():
4423 colText = attr.GetTextColour()
4424 else:
4425 colText = self.GetForegroundColour()
4426
4427 if self._vistaselection:
4428 colText = wx.BLACK
4429
4430 # prepare to draw
4431 dc.SetTextForeground(colText)
4432 dc.SetPen(pen)
4433 oldpen = pen
4434
4435 # draw
4436 self.PaintItem(item, dc)
4437
4438 if self.HasFlag(TR_ROW_LINES):
4439
4440 # if the background colour is white, choose a
4441 # contrasting color for the lines
4442 medium_grey = wx.Pen(wx.Colour(200, 200, 200))
4443 dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0])
4444 dc.DrawLine(0, y_top, 10000, y_top)
4445 dc.DrawLine(0, y, 10000, y)
4446
4447 # restore DC objects
4448 dc.SetBrush(wx.WHITE_BRUSH)
4449 dc.SetTextForeground(wx.BLACK)
4450
4451 if not self.HasFlag(TR_NO_LINES):
4452
4453 # draw the horizontal line here
4454 dc.SetPen(self._dottedPen)
4455 x_start = x
4456 if x > self._indent:
4457 x_start -= self._indent
4458 elif self.HasFlag(TR_LINES_AT_ROOT):
4459 x_start = 3
4460 dc.DrawLine(x_start, y_mid, x + self._spacing, y_mid)
4461 dc.SetPen(oldpen)
4462
4463 # should the item show a button?
4464 if item.HasPlus() and self.HasButtons():
4465
4466 if self._imageListButtons:
4467
4468 # draw the image button here
4469 image_h = 0
4470 image_w = 0
4471 image = (item.IsExpanded() and [TreeItemIcon_Expanded] or [TreeItemIcon_Normal])[0]
4472 if item.IsSelected():
4473 image += TreeItemIcon_Selected - TreeItemIcon_Normal
4474
4475 image_w, image_h = self._imageListButtons.GetSize(image)
4476 xx = x - image_w/2
4477 yy = y_mid - image_h/2
4478
4479 dc.SetClippingRegion(xx, yy, image_w, image_h)
4480 self._imageListButtons.Draw(image, dc, xx, yy,
4481 wx.IMAGELIST_DRAW_TRANSPARENT)
4482 dc.DestroyClippingRegion()
4483
4484 else: # no custom buttons
4485
4486 if self._windowStyle & TR_TWIST_BUTTONS:
4487 # We draw something like the Mac twist buttons
4488
4489 dc.SetPen(wx.BLACK_PEN)
4490 dc.SetBrush(self._hilightBrush)
4491 button = [wx.Point(), wx.Point(), wx.Point()]
4492
4493 if item.IsExpanded():
4494 button[0].x = x - 5
4495 button[0].y = y_mid - 3
4496 button[1].x = x + 5
4497 button[1].y = button[0].y
4498 button[2].x = x
4499 button[2].y = button[0].y + 6
4500 else:
4501 button[0].x = x - 3
4502 button[0].y = y_mid - 5
4503 button[1].x = button[0].x
4504 button[1].y = y_mid + 5
4505 button[2].x = button[0].x + 5
4506 button[2].y = y_mid
4507
4508 dc.DrawPolygon(button)
4509
4510 else:
4511 # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
4512
4513 wImage = 9
4514 hImage = 9
4515
4516 flag = 0
4517
4518 if item.IsExpanded():
4519 flag |= _CONTROL_EXPANDED
4520 if item == self._underMouse:
4521 flag |= _CONTROL_CURRENT
4522
4523 self._drawingfunction(self, dc, wx.Rect(x - wImage/2, y_mid - hImage/2,wImage, hImage), flag)
4524
4525 if item.IsExpanded():
4526
4527 children = item.GetChildren()
4528 count = len(children)
4529
4530 if count > 0:
4531
4532 n = 0
4533 level = level + 1
4534
4535 while n < count:
4536 oldY = y
4537 y = self.PaintLevel(children[n], dc, level, y)
4538 n = n + 1
4539
4540 if not self.HasFlag(TR_NO_LINES) and count > 0:
4541
4542 # draw line down to last child
4543 oldY += self.GetLineHeight(children[n-1])>>1
4544 if self.HasButtons():
4545 y_mid += 5
4546
4547 # Only draw the portion of the line that is visible, in case it is huge
4548 xOrigin, yOrigin = dc.GetDeviceOrigin()
4549 yOrigin = abs(yOrigin)
4550 width, height = self.GetClientSize()
4551
4552 # Move end points to the begining/end of the view?
4553 if y_mid < yOrigin:
4554 y_mid = yOrigin
4555 if oldY > yOrigin + height:
4556 oldY = yOrigin + height
4557
4558 # after the adjustments if y_mid is larger than oldY then the line
4559 # isn't visible at all so don't draw anything
4560 if y_mid < oldY:
4561 dc.SetPen(self._dottedPen)
4562 dc.DrawLine(x, y_mid, x, oldY)
4563
4564 return y
4565
4566
4567 # -----------------------------------------------------------------------------
4568 # wxWidgets callbacks
4569 # -----------------------------------------------------------------------------
4570
4571 def OnPaint(self, event):
4572 """Handles the wx.EVT_PAINT event."""
4573
4574 dc = wx.PaintDC(self)
4575 self.PrepareDC(dc)
4576
4577 if not self._anchor:
4578 return
4579
4580 dc.SetFont(self._normalFont)
4581 dc.SetPen(self._dottedPen)
4582
4583 y = 2
4584 self.PaintLevel(self._anchor, dc, 0, y)
4585
4586
4587 def OnEraseBackground(self, event):
4588 """Handles the wx.EVT_ERASE_BACKGROUND event."""
4589
4590 # Can we actually do something here (or in OnPaint()) To Handle
4591 # background images that are stretchable or always centered?
4592 # I tried but I get enormous flickering...
4593
4594 if not self._backgroundImage:
4595 event.Skip()
4596 return
4597
4598 if self._imageStretchStyle == _StyleTile:
4599 dc = event.GetDC()
4600
4601 if not dc:
4602 dc = wx.ClientDC(self)
4603 rect = self.GetUpdateRegion().GetBox()
4604 dc.SetClippingRect(rect)
4605
4606 self.TileBackground(dc)
4607
4608
4609 def TileBackground(self, dc):
4610 """Tiles the background image to fill all the available area."""
4611
4612 sz = self.GetClientSize()
4613 w = self._backgroundImage.GetWidth()
4614 h = self._backgroundImage.GetHeight()
4615
4616 x = 0
4617
4618 while x < sz.width:
4619 y = 0
4620
4621 while y < sz.height:
4622 dc.DrawBitmap(self._backgroundImage, x, y, True)
4623 y = y + h
4624
4625 x = x + w
4626
4627
4628 def OnSetFocus(self, event):
4629 """Handles the wx.EVT_SET_FOCUS event."""
4630
4631 self._hasFocus = True
4632 self.RefreshSelected()
4633 event.Skip()
4634
4635
4636 def OnKillFocus(self, event):
4637 """Handles the wx.EVT_KILL_FOCUS event."""
4638
4639 self._hasFocus = False
4640 self.RefreshSelected()
4641 event.Skip()
4642
4643
4644 def OnKeyDown(self, event):
4645 """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
4646
4647 te = TreeEvent(wxEVT_TREE_KEY_DOWN, self.GetId())
4648 te._evtKey = event
4649 te.SetEventObject(self)
4650
4651 if self.GetEventHandler().ProcessEvent(te):
4652 # intercepted by the user code
4653 return
4654
4655 if self._current is None or self._key_current is None:
4656
4657 event.Skip()
4658 return
4659
4660 # how should the selection work for this event?
4661 is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), event.ShiftDown(), event.CmdDown())
4662
4663 # + : Expand
4664 # - : Collaspe
4665 # * : Expand all/Collapse all
4666 # ' ' | return : activate
4667 # up : go up (not last children!)
4668 # down : go down
4669 # left : go to parent
4670 # right : open if parent and go next
4671 # home : go to root
4672 # end : go to last item without opening parents
4673 # alnum : start or continue searching for the item with this prefix
4674
4675 keyCode = event.GetKeyCode()
4676
4677 if keyCode in [ord("+"), wx.WXK_ADD]: # "+"
4678 if self._current.HasPlus() and not self.IsExpanded(self._current) and self.IsEnabled(self._current):
4679 self.Expand(self._current)
4680
4681 elif keyCode in [ord("*"), wx.WXK_MULTIPLY]: # "*"
4682 if not self.IsExpanded(self._current) and self.IsEnabled(self._current):
4683 # expand all
4684 self.ExpandAll(self._current)
4685
4686 elif keyCode in [ord("-"), wx.WXK_SUBTRACT]: # "-"
4687 if self.IsExpanded(self._current):
4688 self.Collapse(self._current)
4689
4690 elif keyCode == wx.WXK_MENU:
4691 # Use the item's bounding rectangle to determine position for the event
4692 itemRect = self.GetBoundingRect(self._current, True)
4693 event = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
4694 event._item = self._current
4695 # Use the left edge, vertical middle
4696 event._pointDrag = wx.Point(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight()/2)
4697 event.SetEventObject(self)
4698 self.GetEventHandler().ProcessEvent(event)
4699
4700 elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]:
4701
4702 if not self.IsEnabled(self._current):
4703 event.Skip()
4704 return
4705
4706 if not event.HasModifiers():
4707 event = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
4708 event._item = self._current
4709 event.SetEventObject(self)
4710 self.GetEventHandler().ProcessEvent(event)
4711
4712 if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0:
4713 checked = not self.IsItemChecked(self._current)
4714 self.CheckItem(self._current, checked)
4715
4716 # in any case, also generate the normal key event for this key,
4717 # even if we generated the ACTIVATED event above: this is what
4718 # wxMSW does and it makes sense because you might not want to
4719 # process ACTIVATED event at all and handle Space and Return
4720 # directly (and differently) which would be impossible otherwise
4721 event.Skip()
4722
4723 # up goes to the previous sibling or to the last
4724 # of its children if it's expanded
4725 elif keyCode == wx.WXK_UP:
4726 prev = self.GetPrevSibling(self._key_current)
4727 if not prev:
4728 prev = self.GetItemParent(self._key_current)
4729 if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
4730 return
4731
4732 if prev:
4733 current = self._key_current
4734 # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
4735 if current == self.GetFirstChild(prev)[0] and self.IsEnabled(prev):
4736 # otherwise we return to where we came from
4737 self.DoSelectItem(prev, unselect_others, extended_select)
4738 self._key_current = prev
4739
4740 else:
4741 current = self._key_current
4742
4743 # We are going to another parent node
4744 while self.IsExpanded(prev) and self.HasChildren(prev):
4745 child = self.GetLastChild(prev)
4746 if child:
4747 prev = child
4748 current = prev
4749
4750 # Try to get the previous siblings and see if they are active
4751 while prev and not self.IsEnabled(prev):
4752 prev = self.GetPrevSibling(prev)
4753
4754 if not prev:
4755 # No previous siblings active: go to the parent and up
4756 prev = self.GetItemParent(current)
4757 while prev and not self.IsEnabled(prev):
4758 prev = self.GetItemParent(prev)
4759
4760 if prev:
4761 self.DoSelectItem(prev, unselect_others, extended_select)
4762 self._key_current = prev
4763
4764 # left arrow goes to the parent
4765 elif keyCode == wx.WXK_LEFT:
4766
4767 prev = self.GetItemParent(self._current)
4768 if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
4769 # don't go to root if it is hidden
4770 prev = self.GetPrevSibling(self._current)
4771
4772 if self.IsExpanded(self._current):
4773 self.Collapse(self._current)
4774 else:
4775 if prev and self.IsEnabled(prev):
4776 self.DoSelectItem(prev, unselect_others, extended_select)
4777
4778 elif keyCode == wx.WXK_RIGHT:
4779 # this works the same as the down arrow except that we
4780 # also expand the item if it wasn't expanded yet
4781 if self.IsExpanded(self._current) and self.HasChildren(self._current):
4782 child, cookie = self.GetFirstChild(self._key_current)
4783 if self.IsEnabled(child):
4784 self.DoSelectItem(child, unselect_others, extended_select)
4785 self._key_current = child
4786 else:
4787 self.Expand(self._current)
4788 # fall through
4789
4790 elif keyCode == wx.WXK_DOWN:
4791 if self.IsExpanded(self._key_current) and self.HasChildren(self._key_current):
4792
4793 child = self.GetNextActiveItem(self._key_current)
4794
4795 if child:
4796 self.DoSelectItem(child, unselect_others, extended_select)
4797 self._key_current = child
4798
4799 else:
4800
4801 next = self.GetNextSibling(self._key_current)
4802
4803 if not next:
4804 current = self._key_current
4805 while current and not next:
4806 current = self.GetItemParent(current)
4807 if current:
4808 next = self.GetNextSibling(current)
4809 if not self.IsEnabled(next):
4810 next = None
4811
4812 else:
4813 while next and not self.IsEnabled(next):
4814 next = self.GetNext(next)
4815
4816 if next:
4817 self.DoSelectItem(next, unselect_others, extended_select)
4818 self._key_current = next
4819
4820
4821 # <End> selects the last visible tree item
4822 elif keyCode == wx.WXK_END:
4823
4824 last = self.GetRootItem()
4825
4826 while last and self.IsExpanded(last):
4827
4828 lastChild = self.GetLastChild(last)
4829
4830 # it may happen if the item was expanded but then all of
4831 # its children have been deleted - so IsExpanded() returned
4832 # true, but GetLastChild() returned invalid item
4833 if not lastChild:
4834 break
4835
4836 last = lastChild
4837
4838 if last and self.IsEnabled(last):
4839
4840 self.DoSelectItem(last, unselect_others, extended_select)
4841
4842 # <Home> selects the root item
4843 elif keyCode == wx.WXK_HOME:
4844
4845 prev = self.GetRootItem()
4846
4847 if not prev:
4848 return
4849
4850 if self.HasFlag(TR_HIDE_ROOT):
4851 prev, cookie = self.GetFirstChild(prev)
4852 if not prev:
4853 return
4854
4855 if self.IsEnabled(prev):
4856 self.DoSelectItem(prev, unselect_others, extended_select)
4857
4858 else:
4859
4860 if not event.HasModifiers() and ((keyCode >= ord('0') and keyCode <= ord('9')) or \
4861 (keyCode >= ord('a') and keyCode <= ord('z')) or \
4862 (keyCode >= ord('A') and keyCode <= ord('Z'))):
4863
4864 # find the next item starting with the given prefix
4865 ch = chr(keyCode)
4866 id = self.FindItem(self._current, self._findPrefix + ch)
4867
4868 if not id:
4869 # no such item
4870 return
4871
4872 if self.IsEnabled(id):
4873 self.SelectItem(id)
4874 self._findPrefix += ch
4875
4876 # also start the timer to reset the current prefix if the user
4877 # doesn't press any more alnum keys soon -- we wouldn't want
4878 # to use this prefix for a new item search
4879 if not self._findTimer:
4880 self._findTimer = TreeFindTimer(self)
4881
4882 self._findTimer.Start(_DELAY, wx.TIMER_ONE_SHOT)
4883
4884 else:
4885
4886 event.Skip()
4887
4888
4889 def GetNextActiveItem(self, item, down=True):
4890 """Returns the next active item. Used Internally at present. """
4891
4892 if down:
4893 sibling = self.GetNextSibling
4894 else:
4895 sibling = self.GetPrevSibling
4896
4897 if self.GetItemType(item) == 2 and not self.IsItemChecked(item):
4898 # Is an unchecked radiobutton... all its children are inactive
4899 # try to get the next/previous sibling
4900 found = 0
4901
4902 while 1:
4903 child = sibling(item)
4904 if (child and self.IsEnabled(child)) or not child:
4905 break
4906 item = child
4907
4908 else:
4909 # Tha's not a radiobutton... but some of its children can be
4910 # inactive
4911 child, cookie = self.GetFirstChild(item)
4912 while child and not self.IsEnabled(child):
4913 child, cookie = self.GetNextChild(item, cookie)
4914
4915 if child and self.IsEnabled(child):
4916 return child
4917
4918 return None
4919
4920
4921 def HitTest(self, point, flags=0):
4922 """
4923 Calculates which (if any) item is under the given point, returning the tree item
4924 at this point plus extra information flags. Flags is a bitlist of the following:
4925
4926 TREE_HITTEST_ABOVE above the client area
4927 TREE_HITTEST_BELOW below the client area
4928 TREE_HITTEST_NOWHERE no item has been hit
4929 TREE_HITTEST_ONITEMBUTTON on the button associated to an item
4930 TREE_HITTEST_ONITEMICON on the icon associated to an item
4931 TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
4932 TREE_HITTEST_ONITEMINDENT on the indent associated to an item
4933 TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
4934 TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
4935 TREE_HITTEST_TOLEFT on the left of the client area
4936 TREE_HITTEST_TORIGHT on the right of the client area
4937 TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
4938 TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
4939 TREE_HITTEST_ONITEM anywhere on the item
4940
4941 Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
4942 """
4943
4944 w, h = self.GetSize()
4945 flags = 0
4946
4947 if point.x < 0:
4948 flags |= TREE_HITTEST_TOLEFT
4949 if point.x > w:
4950 flags |= TREE_HITTEST_TORIGHT
4951 if point.y < 0:
4952 flags |= TREE_HITTEST_ABOVE
4953 if point.y > h:
4954 flags |= TREE_HITTEST_BELOW
4955
4956 if flags:
4957 return None, flags
4958
4959 if self._anchor == None:
4960 flags = TREE_HITTEST_NOWHERE
4961 return None, flags
4962
4963 hit, flags = self._anchor.HitTest(self.CalcUnscrolledPosition(point), self, flags, 0)
4964
4965 if hit == None:
4966 flags = TREE_HITTEST_NOWHERE
4967 return None, flags
4968
4969 if not self.IsEnabled(hit):
4970 return None, flags
4971
4972 return hit, flags
4973
4974
4975 def GetBoundingRect(self, item, textOnly=False):
4976 """Gets the bounding rectangle of the item."""
4977
4978 if not item:
4979 raise "\nERROR: Invalid Tree Item. "
4980
4981 i = item
4982
4983 startX, startY = self.GetViewStart()
4984 rect = wx.Rect()
4985
4986 rect.x = i.GetX() - startX*_PIXELS_PER_UNIT
4987 rect.y = i.GetY() - startY*_PIXELS_PER_UNIT
4988 rect.width = i.GetWidth()
4989 rect.height = self.GetLineHeight(i)
4990
4991 return rect
4992
4993
4994 def Edit(self, item):
4995 """
4996 Internal function. Starts the editing of an item label, sending a
4997 EVT_TREE_BEGIN_LABEL_EDIT event.
4998 """
4999
5000 te = TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT, self.GetId())
5001 te._item = item
5002 te.SetEventObject(self)
5003 if self.GetEventHandler().ProcessEvent(te) and not te.IsAllowed():
5004 # vetoed by user
5005 return
5006
5007 # We have to call this here because the label in
5008 # question might just have been added and no screen
5009 # update taken place.
5010 if self._dirty:
5011 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
5012 self.Update()
5013 else:
5014 wx.YieldIfNeeded()
5015
5016 if self._textCtrl != None and item != self._textCtrl.item():
5017 self._textCtrl.StopEditing()
5018
5019 self._textCtrl = TreeTextCtrl(self, item=item)
5020 self._textCtrl.SetFocus()
5021
5022
5023 def GetEditControl(self):
5024 """
5025 Returns a pointer to the edit TextCtrl if the item is being edited or
5026 None otherwise (it is assumed that no more than one item may be edited
5027 simultaneously).
5028 """
5029
5030 return self._textCtrl
5031
5032
5033 def OnRenameAccept(self, item, value):
5034 """
5035 Called by TreeTextCtrl, to accept the changes and to send the
5036 EVT_TREE_END_LABEL_EDIT event.
5037 """
5038
5039 le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
5040 le._item = item
5041 le.SetEventObject(self)
5042 le._label = value
5043 le._editCancelled = False
5044
5045 return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed()
5046
5047
5048 def OnRenameCancelled(self, item):
5049 """
5050 Called by TreeTextCtrl, to cancel the changes and to send the
5051 EVT_TREE_END_LABEL_EDIT event.
5052 """
5053
5054 # let owner know that the edit was cancelled
5055 le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
5056 le._item = item
5057 le.SetEventObject(self)
5058 le._label = ""
5059 le._editCancelled = True
5060
5061 self.GetEventHandler().ProcessEvent(le)
5062
5063
5064 def OnRenameTimer(self):
5065 """The timer for renaming has expired. Start editing."""
5066
5067 self.Edit(self._current)
5068
5069
5070 def OnMouse(self, event):
5071 """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
5072
5073 if not self._anchor:
5074 return
5075
5076 pt = self.CalcUnscrolledPosition(event.GetPosition())
5077
5078 # Is the mouse over a tree item button?
5079 flags = 0
5080 thisItem, flags = self._anchor.HitTest(pt, self, flags, 0)
5081 underMouse = thisItem
5082 underMouseChanged = underMouse != self._underMouse
5083
5084 if underMouse and (flags & TREE_HITTEST_ONITEMBUTTON) and not event.LeftIsDown() and \
5085 not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
5086 underMouse = underMouse
5087 else:
5088 underMouse = None
5089
5090 if underMouse != self._underMouse:
5091 if self._underMouse:
5092 # unhighlight old item
5093 self._underMouse = None
5094
5095 self._underMouse = underMouse
5096
5097 # Determines what item we are hovering over and need a tooltip for
5098 hoverItem = thisItem
5099
5100 # We do not want a tooltip if we are dragging, or if the rename timer is running
5101 if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
5102
5103 if hoverItem is not None:
5104 # Ask the tree control what tooltip (if any) should be shown
5105 hevent = TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP, self.GetId())
5106 hevent._item = hoverItem
5107 hevent.SetEventObject(self)
5108
5109 if self.GetEventHandler().ProcessEvent(hevent) and hevent.IsAllowed():
5110 self.SetToolTip(hevent._label)
5111
5112 if hoverItem.IsHyperText() and (flags & TREE_HITTEST_ONITEMLABEL) and hoverItem.IsEnabled():
5113 self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
5114 self._isonhyperlink = True
5115 else:
5116 if self._isonhyperlink:
5117 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
5118 self._isonhyperlink = False
5119
5120 # we process left mouse up event (enables in-place edit), right down
5121 # (pass to the user code), left dbl click (activate item) and
5122 # dragging/moving events for items drag-and-drop
5123
5124 if not (event.LeftDown() or event.LeftUp() or event.RightDown() or event.LeftDClick() or \
5125 event.Dragging() or ((event.Moving() or event.RightUp()) and self._isDragging)):
5126
5127 event.Skip()
5128 return
5129
5130 flags = 0
5131 item, flags = self._anchor.HitTest(pt, self, flags, 0)
5132
5133 if event.Dragging() and not self._isDragging and ((flags & TREE_HITTEST_ONITEMICON) or (flags & TREE_HITTEST_ONITEMLABEL)):
5134
5135 if self._dragCount == 0:
5136 self._dragStart = pt
5137
5138 self._countDrag = 0
5139 self._dragCount = self._dragCount + 1
5140
5141 if self._dragCount != 3:
5142 # wait until user drags a bit further...
5143 return
5144
5145 command = (event.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG] or [wxEVT_TREE_BEGIN_DRAG])[0]
5146
5147 nevent = TreeEvent(command, self.GetId())
5148 nevent._item = self._current
5149 nevent.SetEventObject(self)
5150 newpt = self.CalcScrolledPosition(pt)
5151 nevent.SetPoint(newpt)
5152
5153 # by default the dragging is not supported, the user code must
5154 # explicitly allow the event for it to take place
5155 nevent.Veto()
5156
5157 if self.GetEventHandler().ProcessEvent(nevent) and nevent.IsAllowed():
5158
5159 # we're going to drag this item
5160 self._isDragging = True
5161
5162 # remember the old cursor because we will change it while
5163 # dragging
5164 self._oldCursor = self._cursor
5165
5166 # in a single selection control, hide the selection temporarily
5167 if not (self.GetTreeStyle() & TR_MULTIPLE):
5168 self._oldSelection = self.GetSelection()
5169
5170 if self._oldSelection:
5171
5172 self._oldSelection.SetHilight(False)
5173 self.RefreshLine(self._oldSelection)
5174 else:
5175 selections = self.GetSelections()
5176 if len(selections) == 1:
5177 self._oldSelection = selections[0]
5178 self._oldSelection.SetHilight(False)
5179 self.RefreshLine(self._oldSelection)
5180
5181 if self._dragImage:
5182 del self._dragImage
5183
5184 # Create the custom draw image from the icons and the text of the item
5185 self._dragImage = DragImage(self, self._current)
5186 self._dragImage.BeginDrag(wx.Point(0,0), self)
5187 self._dragImage.Show()
5188 self._dragImage.Move(self.CalcScrolledPosition(pt))
5189
5190 elif event.Dragging() and self._isDragging:
5191
5192 self._dragImage.Move(self.CalcScrolledPosition(pt))
5193
5194 if self._countDrag == 0 and item:
5195 self._oldItem = item
5196
5197 if item != self._dropTarget:
5198
5199 # unhighlight the previous drop target
5200 if self._dropTarget:
5201 self._dropTarget.SetHilight(False)
5202 self.RefreshLine(self._dropTarget)
5203 if item:
5204 item.SetHilight(True)
5205 self.RefreshLine(item)
5206 self._countDrag = self._countDrag + 1
5207 self._dropTarget = item
5208
5209 self.Update()
5210
5211 if self._countDrag >= 3:
5212 # Here I am trying to avoid ugly repainting problems... hope it works
5213 self.RefreshLine(self._oldItem)
5214 self._countDrag = 0
5215
5216 elif (event.LeftUp() or event.RightUp()) and self._isDragging:
5217
5218 if self._dragImage:
5219 self._dragImage.EndDrag()
5220
5221 if self._dropTarget:
5222 self._dropTarget.SetHilight(False)
5223
5224 if self._oldSelection:
5225
5226 self._oldSelection.SetHilight(True)
5227 self.RefreshLine(self._oldSelection)
5228 self._oldSelection = None
5229
5230 # generate the drag end event
5231 event = TreeEvent(wxEVT_TREE_END_DRAG, self.GetId())
5232 event._item = item
5233 event._pointDrag = self.CalcScrolledPosition(pt)
5234 event.SetEventObject(self)
5235
5236 self.GetEventHandler().ProcessEvent(event)
5237
5238 self._isDragging = False
5239 self._dropTarget = None
5240
5241 self.SetCursor(self._oldCursor)
5242
5243 if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
5244 self.Refresh()
5245 else:
5246 # Probably this is not enough on GTK. Try a Refresh() if it does not work.
5247 wx.YieldIfNeeded()
5248
5249 else:
5250
5251 # If we got to this point, we are not dragging or moving the mouse.
5252 # Because the code in carbon/toplevel.cpp will only set focus to the tree
5253 # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
5254 # We skip even if we didn't hit an item because we still should
5255 # restore focus to the tree control even if we didn't exactly hit an item.
5256 if event.LeftDown():
5257 self._hasFocus = True
5258 self.SetFocusIgnoringChildren()
5259 event.Skip()
5260
5261 # here we process only the messages which happen on tree items
5262
5263 self._dragCount = 0
5264
5265 if item == None:
5266 if self._textCtrl != None and item != self._textCtrl.item():
5267 self._textCtrl.StopEditing()
5268 return # we hit the blank area
5269
5270 if event.RightDown():
5271
5272 if self._textCtrl != None and item != self._textCtrl.item():
5273 self._textCtrl.StopEditing()
5274
5275 self._hasFocus = True
5276 self.SetFocusIgnoringChildren()
5277
5278 # If the item is already selected, do not update the selection.
5279 # Multi-selections should not be cleared if a selected item is clicked.
5280 if not self.IsSelected(item):
5281
5282 self.DoSelectItem(item, True, False)
5283
5284 nevent = TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, self.GetId())
5285 nevent._item = item
5286 nevent._pointDrag = self.CalcScrolledPosition(pt)
5287 nevent.SetEventObject(self)
5288 event.Skip(not self.GetEventHandler().ProcessEvent(nevent))
5289
5290 # Consistent with MSW (for now), send the ITEM_MENU *after*
5291 # the RIGHT_CLICK event. TODO: This behaviour may change.
5292 nevent2 = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
5293 nevent2._item = item
5294 nevent2._pointDrag = self.CalcScrolledPosition(pt)
5295 nevent2.SetEventObject(self)
5296 self.GetEventHandler().ProcessEvent(nevent2)
5297
5298 elif event.LeftUp():
5299
5300 # this facilitates multiple-item drag-and-drop
5301
5302 if self.HasFlag(TR_MULTIPLE):
5303
5304 selections = self.GetSelections()
5305
5306 if len(selections) > 1 and not event.CmdDown() and not event.ShiftDown():
5307
5308 self.DoSelectItem(item, True, False)
5309
5310 if self._lastOnSame:
5311
5312 if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasFlag(TR_EDIT_LABELS):
5313
5314 if self._renameTimer:
5315
5316 if self._renameTimer.IsRunning():
5317
5318 self._renameTimer.Stop()
5319
5320 else:
5321
5322 self._renameTimer = TreeRenameTimer(self)
5323
5324 self._renameTimer.Start(_DELAY, True)
5325
5326 self._lastOnSame = False
5327
5328
5329 else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
5330
5331 if not item or not item.IsEnabled():
5332 if self._textCtrl != None and item != self._textCtrl.item():
5333 self._textCtrl.StopEditing()
5334 return
5335
5336 if self._textCtrl != None and item != self._textCtrl.item():
5337 self._textCtrl.StopEditing()
5338
5339 self._hasFocus = True
5340 self.SetFocusIgnoringChildren()
5341
5342 if event.LeftDown():
5343
5344 self._lastOnSame = item == self._current
5345
5346 if flags & TREE_HITTEST_ONITEMBUTTON:
5347
5348 # only toggle the item for a single click, double click on
5349 # the button doesn't do anything (it toggles the item twice)
5350 if event.LeftDown():
5351
5352 self.Toggle(item)
5353
5354 # don't select the item if the button was clicked
5355 return
5356
5357 if item.GetType() > 0 and (flags & TREE_HITTEST_ONITEMCHECKICON):
5358
5359 if event.LeftDown():
5360
5361 self.CheckItem(item, not self.IsItemChecked(item))
5362
5363 return
5364
5365 # clear the previously selected items, if the
5366 # user clicked outside of the present selection.
5367 # otherwise, perform the deselection on mouse-up.
5368 # this allows multiple drag and drop to work.
5369 # but if Cmd is down, toggle selection of the clicked item
5370 if not self.IsSelected(item) or event.CmdDown():
5371
5372 if flags & TREE_HITTEST_ONITEM:
5373 # how should the selection work for this event?
5374 if item.IsHyperText():
5375 self.SetItemVisited(item, True)
5376
5377 is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(),
5378 event.ShiftDown(),
5379 event.CmdDown())
5380
5381 self.DoSelectItem(item, unselect_others, extended_select)
5382
5383 # For some reason, Windows isn't recognizing a left double-click,
5384 # so we need to simulate it here. Allow 200 milliseconds for now.
5385 if event.LeftDClick():
5386
5387 # double clicking should not start editing the item label
5388 if self._renameTimer:
5389 self._renameTimer.Stop()
5390
5391 self._lastOnSame = False
5392
5393 # send activate event first
5394 nevent = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
5395 nevent._item = item
5396 nevent._pointDrag = self.CalcScrolledPosition(pt)
5397 nevent.SetEventObject(self)
5398 if not self.GetEventHandler().ProcessEvent(nevent):
5399
5400 # if the user code didn't process the activate event,
5401 # handle it ourselves by toggling the item when it is
5402 # double clicked
5403 ## if item.HasPlus():
5404 self.Toggle(item)
5405
5406
5407 def OnInternalIdle(self, event):
5408 """Performs operations in idle time (essentially drawing)."""
5409
5410 # Check if we need to select the root item
5411 # because nothing else has been selected.
5412 # Delaying it means that we can invoke event handlers
5413 # as required, when a first item is selected.
5414 if not self.HasFlag(TR_MULTIPLE) and not self.GetSelection():
5415
5416 if self._select_me:
5417 self.SelectItem(self._select_me)
5418 elif self.GetRootItem():
5419 self.SelectItem(self.GetRootItem())
5420
5421 # after all changes have been done to the tree control,
5422 # we actually redraw the tree when everything is over
5423
5424 if not self._dirty:
5425 return
5426 if self._freezeCount:
5427 return
5428
5429 self._dirty = False
5430
5431 self.CalculatePositions()
5432 self.Refresh()
5433 self.AdjustMyScrollbars()
5434
5435 # event.Skip()
5436
5437
5438 def CalculateSize(self, item, dc):
5439 """Calculates overall position and size of an item."""
5440
5441 attr = item.GetAttributes()
5442
5443 if attr and attr.HasFont():
5444 dc.SetFont(attr.GetFont())
5445 elif item.IsBold():
5446 dc.SetFont(self._boldFont)
5447 else:
5448 dc.SetFont(self._normalFont)
5449
5450 text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
5451 text_h+=2
5452
5453 # restore normal font
5454 dc.SetFont(self._normalFont)
5455
5456 image_w, image_h = 0, 0
5457 image = item.GetCurrentImage()
5458
5459 if image != _NO_IMAGE:
5460
5461 if self._imageListNormal:
5462
5463 image_w, image_h = self._imageListNormal.GetSize(image)
5464 image_w += 4
5465
5466 total_h = ((image_h > text_h) and [image_h] or [text_h])[0]
5467
5468 checkimage = item.GetCurrentCheckedImage()
5469 if checkimage is not None:
5470 wcheck, hcheck = self._imageListCheck.GetSize(checkimage)
5471 wcheck += 4
5472 else:
5473 wcheck = 0
5474
5475 if total_h < 30:
5476 total_h += 2 # at least 2 pixels
5477 else:
5478 total_h += total_h/10 # otherwise 10% extra spacing
5479
5480 if total_h > self._lineHeight:
5481 self._lineHeight = total_h
5482
5483 if not item.GetWindow():
5484 item.SetWidth(image_w+text_w+wcheck+2)
5485 item.SetHeight(total_h)
5486 else:
5487 item.SetWidth(item.GetWindowSize()[0]+image_w+text_w+wcheck+2)
5488
5489
5490 def CalculateLevel(self, item, dc, level, y):
5491 """Calculates the level of an item."""
5492
5493 x = level*self._indent
5494
5495 if not self.HasFlag(TR_HIDE_ROOT):
5496
5497 x += self._indent
5498
5499 elif level == 0:
5500
5501 # a hidden root is not evaluated, but its
5502 # children are always calculated
5503 children = item.GetChildren()
5504 count = len(children)
5505 level = level + 1
5506 for n in xrange(count):
5507 y = self.CalculateLevel(children[n], dc, level, y) # recurse
5508
5509 return y
5510
5511 self.CalculateSize(item, dc)
5512
5513 # set its position
5514 item.SetX(x+self._spacing)
5515 item.SetY(y)
5516 y += self.GetLineHeight(item)
5517
5518 if not item.IsExpanded():
5519 # we don't need to calculate collapsed branches
5520 return y
5521
5522 children = item.GetChildren()
5523 count = len(children)
5524 level = level + 1
5525 for n in xrange(count):
5526 y = self.CalculateLevel(children[n], dc, level, y) # recurse
5527
5528 return y
5529
5530
5531 def CalculatePositions(self):
5532 """Calculates all the positions of the visible items."""
5533
5534 if not self._anchor:
5535 return
5536
5537 dc = wx.ClientDC(self)
5538 self.PrepareDC(dc)
5539
5540 dc.SetFont(self._normalFont)
5541 dc.SetPen(self._dottedPen)
5542 y = 2
5543 y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion
5544
5545
5546 def RefreshSubtree(self, item):
5547 """Refreshes a damaged subtree of an item."""
5548
5549 if self._dirty:
5550 return
5551 if self._freezeCount:
5552 return
5553
5554 client = self.GetClientSize()
5555
5556 rect = wx.Rect()
5557 x, rect.y = self.CalcScrolledPosition(0, item.GetY())
5558 rect.width = client.x
5559 rect.height = client.y
5560
5561 self.Refresh(True, rect)
5562 self.AdjustMyScrollbars()
5563
5564
5565 def RefreshLine(self, item):
5566 """Refreshes a damaged item line."""
5567
5568 if self._dirty:
5569 return
5570 if self._freezeCount:
5571 return
5572
5573 rect = wx.Rect()
5574 x, rect.y = self.CalcScrolledPosition(0, item.GetY())
5575 rect.width = self.GetClientSize().x
5576 rect.height = self.GetLineHeight(item)
5577
5578 self.Refresh(True, rect)
5579
5580
5581 def RefreshSelected(self):
5582 """Refreshes a damaged selected item line."""
5583
5584 if self._freezeCount:
5585 return
5586
5587 # TODO: this is awfully inefficient, we should keep the list of all
5588 # selected items internally, should be much faster
5589 if self._anchor:
5590 self.RefreshSelectedUnder(self._anchor)
5591
5592
5593 def RefreshSelectedUnder(self, item):
5594 """Refreshes the selected items under the given item."""
5595
5596 if self._freezeCount:
5597 return
5598
5599 if item.IsSelected():
5600 self.RefreshLine(item)
5601
5602 children = item.GetChildren()
5603 for child in children:
5604 self.RefreshSelectedUnder(child)
5605
5606
5607 def Freeze(self):
5608 """Freeze CustomTreeCtrl."""
5609
5610 self._freezeCount = self._freezeCount + 1
5611
5612
5613 def Thaw(self):
5614 """Thaw CustomTreeCtrl."""
5615
5616 if self._freezeCount == 0:
5617 raise "\nERROR: Thawing Unfrozen Tree Control?"
5618
5619 self._freezeCount = self._freezeCount - 1
5620
5621 if not self._freezeCount:
5622 self.Refresh()
5623
5624
5625 # ----------------------------------------------------------------------------
5626 # changing colours: we need to refresh the tree control
5627 # ----------------------------------------------------------------------------
5628
5629 def SetBackgroundColour(self, colour):
5630 """Changes the background colour of CustomTreeCtrl."""
5631
5632 if not wx.Window.SetBackgroundColour(self, colour):
5633 return False
5634
5635 if self._freezeCount:
5636 return True
5637
5638 self.Refresh()
5639
5640 return True
5641
5642
5643 def SetForegroundColour(self, colour):
5644 """Changes the foreground colour of CustomTreeCtrl."""
5645
5646 if not wx.Window.SetForegroundColour(self, colour):
5647 return False
5648
5649 if self._freezeCount:
5650 return True
5651
5652 self.Refresh()
5653
5654 return True
5655
5656
5657 def OnGetToolTip(self, event):
5658 """
5659 Process the tooltip event, to speed up event processing. Does not actually
5660 get a tooltip.
5661 """
5662
5663 event.Veto()
5664
5665
5666 def DoGetBestSize(self):
5667 """Something is better than nothing..."""
5668
5669 # something is better than nothing...
5670 # 100x80 is what the MSW version will get from the default
5671 # wxControl::DoGetBestSize
5672
5673 return wx.Size(100, 80)
5674
5675
5676 def GetClassDefaultAttributes(self):
5677 """Gets the class default attributes."""
5678
5679 attr = wx.VisualAttributes()
5680 attr.colFg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
5681 attr.colBg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_LISTBOX)
5682 attr.font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
5683 return attr
5684
5685