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