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