PageRenderTime 113ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/pwiki/customtreectrl.py

https://bitbucket.org/xkjq/wikidpad_svn
Python | 6065 lines | 5857 code | 74 blank | 134 comment | 14 complexity | 6184bada91d2c01aab5636d1e5082b99 MD5 | raw file
Possible License(s): LGPL-2.1
  1. # --------------------------------------------------------------------------------- #
  2. # CUSTOMTREECTRL wxPython IMPLEMENTATION
  3. # Inspired By And Heavily Based On wxGenericTreeCtrl.
  4. #
  5. # Andrea Gavana, @ 17 May 2006
  6. # Latest Revision: 16 Apr 2007, 11.00 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. # Modifications by Michael Butscher (mbutscher@gmx.de) based on
  42. # rev. 1.14 in wxWidgets repository
  43. #
  44. # Modifications by Michael Butscher Jan. 2007:
  45. #
  46. # - Expand buttons at the same place where they are on Windows tree
  47. # - No button for root element
  48. # - Expansion near the bottom scrolls tree appropriately
  49. # - Flicker-free expansion/collapse (not tested with background image)
  50. # - Unselect also works on single-select tree
  51. # - Option to set image list without generation of grayed icons (faster)
  52. #
  53. # Modifications by Michael Butscher May 2007:
  54. # - Tooltip if label is broader than window
  55. #
  56. # Modifications by Michael Butscher May 2010:
  57. # - Parameter in SelectItem() to suppress event generation
  58. #
  59. #
  60. # End Of Comments
  61. # --------------------------------------------------------------------------------- #
  62. """
  63. Description
  64. ===========
  65. CustomTreeCtrl is a class that mimics the behaviour of wx.TreeCtrl, with almost the
  66. same base functionalities plus some more enhancements. This class does not rely on
  67. the native control, as it is a full owner-drawn tree control.
  68. Apart of the base functionalities of CustomTreeCtrl (described below), in addition
  69. to the standard wx.TreeCtrl behaviour this class supports:
  70. * CheckBox-type items: checkboxes are easy to handle, just selected or unselected
  71. state with no particular issues in handling the item's children;
  72. * RadioButton-type items: since I elected to put radiobuttons in CustomTreeCtrl, I
  73. needed some way to handle them, that made sense. So, I used the following approach:
  74. - All peer-nodes that are radiobuttons will be mutually exclusive. In other words,
  75. only one of a set of radiobuttons that share a common parent can be checked at
  76. once. If a radiobutton node becomes checked, then all of its peer radiobuttons
  77. must be unchecked.
  78. - If a radiobutton node becomes unchecked, then all of its child nodes will become
  79. inactive.
  80. * Hyperlink-type items: they look like an hyperlink, with the proper mouse cursor on
  81. hovering.
  82. * Multiline text items.
  83. * Enabling/disabling items (together with their plain or grayed out icons).
  84. * Whatever non-toplevel widget can be attached next to an item.
  85. * Default selection style, gradient (horizontal/vertical) selection style and Windows
  86. Vista selection style.
  87. * Customized drag and drop images built on the fly.
  88. * Setting the CustomTreeCtrl item buttons to a personalized imagelist.
  89. * Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist.
  90. * Changing the style of the lines that connect the items (in terms of wx.Pen styles).
  91. * Using an image as a CustomTreeCtrl background (currently only in "tile" mode).
  92. And a lot more. Check the demo for an almost complete review of the functionalities.
  93. Base Functionalities
  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. Plus it has 3 more styles to handle checkbox-type items:
  98. - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children;
  99. - TR_AUTO_CHECK_PARENT : automatically checks/unchecks the item parent;
  100. - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children.
  101. All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl.
  102. Events
  103. ======
  104. All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with
  105. a few exceptions:
  106. - EVT_TREE_GET_INFO (don't know what this means);
  107. - EVT_TREE_SET_INFO (don't know what this means);
  108. - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add);
  109. - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below.
  110. Plus, CustomTreeCtrl supports the events related to the checkbutton-type items:
  111. - EVT_TREE_ITEM_CHECKING: an item is being checked;
  112. - EVT_TREE_ITEM_CHECKED: an item has been checked.
  113. And to hyperlink-type items:
  114. - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent
  115. after the EVT_TREE_SEL_CHANGED event).
  116. Supported Platforms
  117. ===================
  118. CustomTreeCtrl has been tested on the following platforms:
  119. * Windows (Windows XP);
  120. * GTK (Thanks to Michele Petrazzo);
  121. * Mac OS (Thanks to John Jackson).
  122. Latest Revision: Andrea Gavana @ 16 Apr 2007, 11.00 CET
  123. Version 1.0
  124. """
  125. import wx
  126. import zlib
  127. import cStringIO
  128. import types
  129. import traceback
  130. # ----------------------------------------------------------------------------
  131. # Constants
  132. # ----------------------------------------------------------------------------
  133. _NO_IMAGE = -1
  134. _PIXELS_PER_UNIT = 10
  135. # Bug workaround: In wxPython 2.6 these constants weren't defined
  136. # in 2.8 they are defined under a different name and with different values
  137. try:
  138. wxWINDOWS_NT = wx.OS_WINDOWS_NT
  139. except AttributeError:
  140. wxWINDOWS_NT = 18 # For wxGetOsVersion(), this includes NT 4.0, 2000, XP
  141. try:
  142. wxWIN95 = wx.OS_WINDOWS_9X
  143. except AttributeError:
  144. wxWIN95 = 20 # For wx.GetOsVersion(), this includes also Win 98 and ME
  145. # Start editing the current item after half a second (if the mouse hasn't
  146. # been clicked/moved)
  147. _DELAY = 500
  148. # ----------------------------------------------------------------------------
  149. # Constants
  150. # ----------------------------------------------------------------------------
  151. # Enum for different images associated with a treectrl item
  152. TreeItemIcon_Normal = 0 # not selected, not expanded
  153. TreeItemIcon_Selected = 1 # selected, not expanded
  154. TreeItemIcon_Expanded = 2 # not selected, expanded
  155. TreeItemIcon_SelectedExpanded = 3 # selected, expanded
  156. TreeItemIcon_Checked = 0 # check button, checked
  157. TreeItemIcon_NotChecked = 1 # check button, not checked
  158. TreeItemIcon_Flagged = 2 # radio button, selected
  159. TreeItemIcon_NotFlagged = 3 # radio button, not selected
  160. # ----------------------------------------------------------------------------
  161. # CustomTreeCtrl flags
  162. # ----------------------------------------------------------------------------
  163. TR_NO_BUTTONS = wx.TR_NO_BUTTONS # for convenience
  164. TR_HAS_BUTTONS = wx.TR_HAS_BUTTONS # draw collapsed/expanded btns
  165. TR_NO_LINES = wx.TR_NO_LINES # don't draw lines at all
  166. TR_LINES_AT_ROOT = wx.TR_LINES_AT_ROOT # connect top-level nodes
  167. TR_TWIST_BUTTONS = wx.TR_TWIST_BUTTONS # still used by wxTreeListCtrl
  168. TR_SINGLE = wx.TR_SINGLE # for convenience
  169. TR_MULTIPLE = wx.TR_MULTIPLE # can select multiple items
  170. TR_EXTENDED = wx.TR_EXTENDED # TODO: allow extended selection
  171. TR_HAS_VARIABLE_ROW_HEIGHT = wx.TR_HAS_VARIABLE_ROW_HEIGHT # what it says
  172. TR_EDIT_LABELS = wx.TR_EDIT_LABELS # can edit item labels
  173. TR_ROW_LINES = wx.TR_ROW_LINES # put border around items
  174. TR_HIDE_ROOT = wx.TR_HIDE_ROOT # don't display root node
  175. TR_FULL_ROW_HIGHLIGHT = wx.TR_FULL_ROW_HIGHLIGHT # highlight full horz space
  176. TR_AUTO_CHECK_CHILD = 0x04000 # only meaningful for checkboxes
  177. TR_AUTO_TOGGLE_CHILD = 0x08000 # only meaningful for checkboxes
  178. TR_AUTO_CHECK_PARENT = 0x10000 # only meaningful for checkboxes
  179. TR_DEFAULT_STYLE = wx.TR_DEFAULT_STYLE # default style for the tree control
  180. # Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine
  181. # where exactly the specified point is situated:
  182. TREE_HITTEST_ABOVE = wx.TREE_HITTEST_ABOVE
  183. TREE_HITTEST_BELOW = wx.TREE_HITTEST_BELOW
  184. TREE_HITTEST_NOWHERE = wx.TREE_HITTEST_NOWHERE
  185. # on the button associated with an item.
  186. TREE_HITTEST_ONITEMBUTTON = wx.TREE_HITTEST_ONITEMBUTTON
  187. # on the bitmap associated with an item.
  188. TREE_HITTEST_ONITEMICON = wx.TREE_HITTEST_ONITEMICON
  189. # on the indent associated with an item.
  190. TREE_HITTEST_ONITEMINDENT = wx.TREE_HITTEST_ONITEMINDENT
  191. # on the label (string) associated with an item.
  192. TREE_HITTEST_ONITEMLABEL = wx.TREE_HITTEST_ONITEMLABEL
  193. # on the right of the label associated with an item.
  194. TREE_HITTEST_ONITEMRIGHT = wx.TREE_HITTEST_ONITEMRIGHT
  195. # on the label (string) associated with an item.
  196. TREE_HITTEST_ONITEMSTATEICON = wx.TREE_HITTEST_ONITEMSTATEICON
  197. # on the left of the CustomTreeCtrl.
  198. TREE_HITTEST_TOLEFT = wx.TREE_HITTEST_TOLEFT
  199. # on the right of the CustomTreeCtrl.
  200. TREE_HITTEST_TORIGHT = wx.TREE_HITTEST_TORIGHT
  201. # on the upper part (first half) of the item.
  202. TREE_HITTEST_ONITEMUPPERPART = wx.TREE_HITTEST_ONITEMUPPERPART
  203. # on the lower part (second half) of the item.
  204. TREE_HITTEST_ONITEMLOWERPART = wx.TREE_HITTEST_ONITEMLOWERPART
  205. # on the check icon, if present
  206. TREE_HITTEST_ONITEMCHECKICON = 0x4000
  207. # anywhere on the item
  208. TREE_HITTEST_ONITEM = TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON
  209. # Background Image Style
  210. _StyleTile = 0
  211. _StyleStretch = 1
  212. # Windows Vista Colours
  213. _rgbSelectOuter = wx.Colour(170, 200, 245)
  214. _rgbSelectInner = wx.Colour(230, 250, 250)
  215. _rgbSelectTop = wx.Colour(210, 240, 250)
  216. _rgbSelectBottom = wx.Colour(185, 215, 250)
  217. _rgbNoFocusTop = wx.Colour(250, 250, 250)
  218. _rgbNoFocusBottom = wx.Colour(235, 235, 235)
  219. _rgbNoFocusOuter = wx.Colour(220, 220, 220)
  220. _rgbNoFocusInner = wx.Colour(245, 245, 245)
  221. # Flags for wx.RendererNative
  222. _CONTROL_EXPANDED = 8
  223. _CONTROL_CURRENT = 16
  224. # Version Info
  225. __version__ = "0.8"
  226. # ----------------------------------------------------------------------------
  227. # CustomTreeCtrl events and binding for handling them
  228. # ----------------------------------------------------------------------------
  229. wxEVT_TREE_BEGIN_DRAG = wx.wxEVT_COMMAND_TREE_BEGIN_DRAG
  230. wxEVT_TREE_BEGIN_RDRAG = wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG
  231. wxEVT_TREE_BEGIN_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
  232. wxEVT_TREE_END_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT
  233. wxEVT_TREE_DELETE_ITEM = wx.wxEVT_COMMAND_TREE_DELETE_ITEM
  234. wxEVT_TREE_GET_INFO = wx.wxEVT_COMMAND_TREE_GET_INFO
  235. wxEVT_TREE_SET_INFO = wx.wxEVT_COMMAND_TREE_SET_INFO
  236. wxEVT_TREE_ITEM_EXPANDED = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDED
  237. wxEVT_TREE_ITEM_EXPANDING = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING
  238. wxEVT_TREE_ITEM_COLLAPSED = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED
  239. wxEVT_TREE_ITEM_COLLAPSING = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSING
  240. wxEVT_TREE_SEL_CHANGED = wx.wxEVT_COMMAND_TREE_SEL_CHANGED
  241. wxEVT_TREE_SEL_CHANGING = wx.wxEVT_COMMAND_TREE_SEL_CHANGING
  242. wxEVT_TREE_KEY_DOWN = wx.wxEVT_COMMAND_TREE_KEY_DOWN
  243. wxEVT_TREE_ITEM_ACTIVATED = wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED
  244. wxEVT_TREE_ITEM_RIGHT_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
  245. wxEVT_TREE_ITEM_MIDDLE_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK
  246. wxEVT_TREE_END_DRAG = wx.wxEVT_COMMAND_TREE_END_DRAG
  247. wxEVT_TREE_STATE_IMAGE_CLICK = wx.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
  248. wxEVT_TREE_ITEM_GETTOOLTIP = wx.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
  249. wxEVT_TREE_ITEM_MENU = wx.wxEVT_COMMAND_TREE_ITEM_MENU
  250. wxEVT_TREE_ITEM_CHECKING = wx.NewEventType()
  251. wxEVT_TREE_ITEM_CHECKED = wx.NewEventType()
  252. wxEVT_TREE_ITEM_HYPERLINK = wx.NewEventType()
  253. EVT_TREE_BEGIN_DRAG = wx.EVT_TREE_BEGIN_DRAG
  254. EVT_TREE_BEGIN_RDRAG = wx.EVT_TREE_BEGIN_RDRAG
  255. EVT_TREE_BEGIN_LABEL_EDIT = wx.EVT_TREE_BEGIN_LABEL_EDIT
  256. EVT_TREE_END_LABEL_EDIT = wx.EVT_TREE_END_LABEL_EDIT
  257. EVT_TREE_DELETE_ITEM = wx.EVT_TREE_DELETE_ITEM
  258. EVT_TREE_GET_INFO = wx.EVT_TREE_GET_INFO
  259. EVT_TREE_SET_INFO = wx.EVT_TREE_SET_INFO
  260. EVT_TREE_ITEM_EXPANDED = wx.EVT_TREE_ITEM_EXPANDED
  261. EVT_TREE_ITEM_EXPANDING = wx.EVT_TREE_ITEM_EXPANDING
  262. EVT_TREE_ITEM_COLLAPSED = wx.EVT_TREE_ITEM_COLLAPSED
  263. EVT_TREE_ITEM_COLLAPSING = wx.EVT_TREE_ITEM_COLLAPSING
  264. EVT_TREE_SEL_CHANGED = wx.EVT_TREE_SEL_CHANGED
  265. EVT_TREE_SEL_CHANGING = wx.EVT_TREE_SEL_CHANGING
  266. EVT_TREE_KEY_DOWN = wx.EVT_TREE_KEY_DOWN
  267. EVT_TREE_ITEM_ACTIVATED = wx.EVT_TREE_ITEM_ACTIVATED
  268. EVT_TREE_ITEM_RIGHT_CLICK = wx.EVT_TREE_ITEM_RIGHT_CLICK
  269. EVT_TREE_ITEM_MIDDLE_CLICK = wx.EVT_TREE_ITEM_MIDDLE_CLICK
  270. EVT_TREE_END_DRAG = wx.EVT_TREE_END_DRAG
  271. EVT_TREE_STATE_IMAGE_CLICK = wx.EVT_TREE_STATE_IMAGE_CLICK
  272. EVT_TREE_ITEM_GETTOOLTIP = wx.EVT_TREE_ITEM_GETTOOLTIP
  273. EVT_TREE_ITEM_MENU = wx.EVT_TREE_ITEM_MENU
  274. EVT_TREE_ITEM_CHECKING = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKING, 1)
  275. EVT_TREE_ITEM_CHECKED = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKED, 1)
  276. EVT_TREE_ITEM_HYPERLINK = wx.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK, 1)
  277. def GetFlaggedData():
  278. return zlib.decompress(
  279. 'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
  280. \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
  281. |\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\
  282. \x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\
  283. \xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\
  284. \xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\
  285. \xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\
  286. \xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\
  287. \r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\
  288. \xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\
  289. \xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\
  290. \xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\
  291. \x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\
  292. \xb3\xb7H,a\xe1\xa9)\xdf<e$2Ble\x85\x94e\xb1\x96\xcep\xfb\xdd-D\x04\xa5\x14\
  293. \xdeZ\'\xb1\x84\x85\xd8\x8bm\x84\xe6\x977\x7f8kog)\xba\xc4\xb7\xe5\xef$\xe2?\
  294. \xe9\xa9\xbf\x86R\n\x11a&\x1c\xc1^lC|\r.\x02\xb3\x8b\x9b\xa6&G\x13W\xaa\xbb\
  295. \x91_\x05\x0c\x1d\xbfI\xc7\xa1\x8e\xbf&a|:\x8c\xaf\xc1\x05J4\x8e\xd6>36\x192\
  296. \xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\
  297. \xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\
  298. nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\
  299. \xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\
  300. \xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' )
  301. def GetFlaggedBitmap():
  302. return wx.BitmapFromImage(GetFlaggedImage())
  303. def GetFlaggedImage():
  304. stream = cStringIO.StringIO(GetFlaggedData())
  305. return wx.ImageFromStream(stream)
  306. #----------------------------------------------------------------------
  307. def GetNotFlaggedData():
  308. return zlib.decompress(
  309. 'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\
  310. \x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\
  311. |\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\
  312. \x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\
  313. \x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\
  314. \x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\
  315. \x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\
  316. \xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\
  317. \x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\
  318. \xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\
  319. \x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\
  320. \xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\
  321. \x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\
  322. \x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\
  323. \xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\
  324. \xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\
  325. \xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' )
  326. def GetNotFlaggedBitmap():
  327. return wx.BitmapFromImage(GetNotFlaggedImage())
  328. def GetNotFlaggedImage():
  329. stream = cStringIO.StringIO(GetNotFlaggedData())
  330. return wx.ImageFromStream(stream)
  331. #----------------------------------------------------------------------
  332. def GetCheckedData():
  333. return zlib.decompress(
  334. "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
  335. \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\
  336. \x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\
  337. \xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\
  338. \x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\
  339. \xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\
  340. \xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\
  341. \xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\
  342. \x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\
  343. \xeb\x9c\x12\x9a\x00\x92\xccS\x9f" )
  344. def GetCheckedBitmap():
  345. return wx.BitmapFromImage(GetCheckedImage())
  346. def GetCheckedImage():
  347. stream = cStringIO.StringIO(GetCheckedData())
  348. return wx.ImageFromStream(stream)
  349. #----------------------------------------------------------------------
  350. def GetNotCheckedData():
  351. return zlib.decompress(
  352. "x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\
  353. \x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\
  354. \xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\
  355. \xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\
  356. \x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\
  357. \xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\
  358. \xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\
  359. \x00\x87S=\xbe" )
  360. def GetNotCheckedBitmap():
  361. return wx.BitmapFromImage(GetNotCheckedImage())
  362. def GetNotCheckedImage():
  363. stream = cStringIO.StringIO(GetNotCheckedData())
  364. return wx.ImageFromStream(stream)
  365. def GrayOut(anImage):
  366. """
  367. Convert the given image (in place) to a grayed-out version,
  368. appropriate for a 'disabled' appearance.
  369. """
  370. factor = 0.7 # 0 < f < 1. Higher Is Grayer
  371. if anImage.HasMask():
  372. maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue())
  373. else:
  374. maskColor = None
  375. data = map(ord, list(anImage.GetData()))
  376. for i in range(0, len(data), 3):
  377. pixel = (data[i], data[i+1], data[i+2])
  378. pixel = MakeGray(pixel, factor, maskColor)
  379. for x in range(3):
  380. data[i+x] = pixel[x]
  381. anImage.SetData(''.join(map(chr, data)))
  382. return anImage
  383. def MakeGray((r,g,b), factor, maskColor):
  384. """
  385. Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be
  386. changed.
  387. """
  388. if (r,g,b) != maskColor:
  389. return map(lambda x: int((230 - x) * factor) + x, (r,g,b))
  390. else:
  391. return (r,g,b)
  392. def DrawTreeItemButton(win, dc, rect, flags):
  393. """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """
  394. # white background
  395. dc.SetPen(wx.GREY_PEN)
  396. dc.SetBrush(wx.WHITE_BRUSH)
  397. dc.DrawRectangleRect(rect)
  398. # black lines
  399. xMiddle = rect.x + rect.width/2
  400. yMiddle = rect.y + rect.height/2
  401. # half of the length of the horz lines in "-" and "+"
  402. halfWidth = rect.width/2 - 2
  403. dc.SetPen(wx.BLACK_PEN)
  404. dc.DrawLine(xMiddle - halfWidth, yMiddle,
  405. xMiddle + halfWidth + 1, yMiddle)
  406. if not flags & _CONTROL_EXPANDED:
  407. # turn "-" into "+"
  408. halfHeight = rect.height/2 - 2
  409. dc.DrawLine(xMiddle, yMiddle - halfHeight,
  410. xMiddle, yMiddle + halfHeight + 1)
  411. #---------------------------------------------------------------------------
  412. # DragImage Implementation
  413. # This Class Handles The Creation Of A Custom Image In Case Of Item Drag
  414. # And Drop.
  415. #---------------------------------------------------------------------------
  416. class DragImage(wx.DragImage):
  417. """
  418. This class handles the creation of a custom image in case of item drag
  419. and drop.
  420. """
  421. def __init__(self, treeCtrl, item):
  422. """
  423. Default class constructor.
  424. For internal use: do not call it in your code!
  425. """
  426. text = item.GetText()
  427. font = item.Attr().GetFont()
  428. colour = item.Attr().GetTextColour()
  429. if not colour:
  430. colour = wx.BLACK
  431. if not font:
  432. font = treeCtrl._normalFont
  433. backcolour = treeCtrl.GetBackgroundColour()
  434. r, g, b = int(backcolour.Red()), int(backcolour.Green()), int(backcolour.Blue())
  435. backcolour = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
  436. backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2])
  437. self._backgroundColour = backcolour
  438. tempdc = wx.ClientDC(treeCtrl)
  439. tempdc.SetFont(font)
  440. width, height, dummy = tempdc.GetMultiLineTextExtent(text + "M")
  441. image = item.GetCurrentImage()
  442. image_w, image_h = 0, 0
  443. wcheck, hcheck = 0, 0
  444. itemcheck = None
  445. itemimage = None
  446. ximagepos = 0
  447. yimagepos = 0
  448. xcheckpos = 0
  449. ycheckpos = 0
  450. if image != _NO_IMAGE:
  451. if treeCtrl._imageListNormal:
  452. image_w, image_h = treeCtrl._imageListNormal.GetSize(image)
  453. image_w += 4
  454. itemimage = treeCtrl._imageListNormal.GetBitmap(image)
  455. checkimage = item.GetCurrentCheckedImage()
  456. if checkimage is not None:
  457. if treeCtrl._imageListCheck:
  458. wcheck, hcheck = treeCtrl._imageListCheck.GetSize(checkimage)
  459. wcheck += 4
  460. itemcheck = treeCtrl._imageListCheck.GetBitmap(checkimage)
  461. total_h = max(hcheck, height)
  462. total_h = max(image_h, total_h)
  463. if image_w:
  464. ximagepos = wcheck
  465. yimagepos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0]
  466. if checkimage is not None:
  467. xcheckpos = 2
  468. ycheckpos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] + 2
  469. extraH = ((total_h > height) and [(total_h - height)/2] or [0])[0]
  470. xtextpos = wcheck + image_w
  471. ytextpos = extraH
  472. total_h = max(image_h, hcheck)
  473. total_h = max(total_h, height)
  474. if total_h < 30:
  475. total_h += 2 # at least 2 pixels
  476. else:
  477. total_h += total_h/10 # otherwise 10% extra spacing
  478. total_w = image_w + wcheck + width
  479. self._total_w = total_w
  480. self._total_h = total_h
  481. self._itemimage = itemimage
  482. self._itemcheck = itemcheck
  483. self._text = text
  484. self._colour = colour
  485. self._font = font
  486. self._xtextpos = xtextpos
  487. self._ytextpos = ytextpos
  488. self._ximagepos = ximagepos
  489. self._yimagepos = yimagepos
  490. self._xcheckpos = xcheckpos
  491. self._ycheckpos = ycheckpos
  492. self._textwidth = width
  493. self._textheight = height
  494. self._extraH = extraH
  495. self._bitmap = self.CreateBitmap()
  496. wx.DragImage.__init__(self, self._bitmap)
  497. def CreateBitmap(self):
  498. """Actually creates the dnd bitmap."""
  499. memory = wx.MemoryDC()
  500. bitmap = wx.EmptyBitmap(self._total_w, self._total_h)
  501. memory.SelectObject(bitmap)
  502. memory.SetTextBackground(self._backgroundColour)
  503. memory.SetBackground(wx.Brush(self._backgroundColour))
  504. memory.SetFont(self._font)
  505. memory.SetTextForeground(self._colour)
  506. memory.Clear()
  507. if self._itemimage:
  508. memory.DrawBitmap(self._itemimage, self._ximagepos, self._yimagepos, True)
  509. if self._itemcheck:
  510. memory.DrawBitmap(self._itemcheck, self._xcheckpos, self._ycheckpos, True)
  511. textrect = wx.Rect(self._xtextpos, self._ytextpos+self._extraH, self._textwidth, self._textheight)
  512. memory.DrawLabel(self._text, textrect)
  513. memory.SelectObject(wx.NullBitmap)
  514. return bitmap
  515. # ----------------------------------------------------------------------------
  516. # TreeItemAttr: a structure containing the visual attributes of an item
  517. # ----------------------------------------------------------------------------
  518. class TreeItemAttr:
  519. """Creates the item attributes (text colour, background colour and font)."""
  520. def __init__(self, colText=wx.NullColour, colBack=wx.NullColour, font=wx.NullFont):
  521. """
  522. Default class constructor.
  523. For internal use: do not call it in your code!
  524. """
  525. self._colText = colText
  526. self._colBack = colBack
  527. self._font = font
  528. # setters
  529. def SetTextColour(self, colText):
  530. """Sets the attribute text colour."""
  531. self._colText = colText
  532. def SetBackgroundColour(self, colBack):
  533. """Sets the attribute background colour."""
  534. self._colBack = colBack
  535. def SetFont(self, font):
  536. """Sets the attribute font."""
  537. self._font = font
  538. # accessors
  539. def HasTextColour(self):
  540. """Returns whether the attribute has text colour."""
  541. return self._colText != wx.NullColour
  542. def HasBackgroundColour(self):
  543. """Returns whether the attribute has background colour."""
  544. return self._colBack != wx.NullColour
  545. def HasFont(self):
  546. """Returns whether the attribute has font."""
  547. return self._font != wx.NullFont
  548. # getters
  549. def GetTextColour(self):
  550. """Returns the attribute text colour."""
  551. return self._colText
  552. def GetBackgroundColour(self):
  553. """Returns the attribute background colour."""
  554. return self._colBack
  555. def GetFont(self):
  556. """Returns the attribute font."""
  557. return self._font
  558. # ----------------------------------------------------------------------------
  559. # CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent
  560. #
  561. # NB: Note That Not All The Accessors Make Sense For All The Events, See The
  562. # Event Description Below.
  563. # ----------------------------------------------------------------------------
  564. class CommandTreeEvent(wx.PyCommandEvent):
  565. """
  566. CommandTreeEvent is a special subclassing of wx.PyCommandEvent.
  567. NB: note that not all the accessors make sense for all the events, see the
  568. event description for every method in this class.
  569. """
  570. def __init__(self, type, id, item=None, evtKey=None, point=None,
  571. label=None, **kwargs):
  572. """
  573. Default class constructor.
  574. For internal use: do not call it in your code!
  575. """
  576. wx.PyCommandEvent.__init__(self, type, id, **kwargs)
  577. self._item = item
  578. self._evtKey = evtKey
  579. self._pointDrag = point
  580. self._label = label
  581. def GetItem(self):
  582. """
  583. Gets the item on which the operation was performed or the newly selected
  584. item for EVT_TREE_SEL_CHANGED/ING events.
  585. """
  586. return self._item
  587. def SetItem(self, item):
  588. """
  589. Sets the item on which the operation was performed or the newly selected
  590. item for EVT_TREE_SEL_CHANGED/ING events.
  591. """
  592. self._item = item
  593. def GetOldItem(self):
  594. """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item."""
  595. return self._itemOld
  596. def SetOldItem(self, item):
  597. """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item."""
  598. self._itemOld = item
  599. def GetPoint(self):
  600. """
  601. Returns the point where the mouse was when the drag operation started
  602. (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
  603. """
  604. return self._pointDrag
  605. def SetPoint(self, pt):
  606. """
  607. Sets the point where the mouse was when the drag operation started
  608. (for EVT_TREE_BEGIN(R)DRAG events only) or the click position.
  609. """
  610. self._pointDrag = pt
  611. def GetKeyEvent(self):
  612. """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
  613. return self._evtKey
  614. def GetKeyCode(self):
  615. """Returns the integer key code (for EVT_TREE_KEY_DOWN only)."""
  616. return self._evtKey.GetKeyCode()
  617. def SetKeyEvent(self, evt):
  618. """Keyboard data (for EVT_TREE_KEY_DOWN only)."""
  619. self._evtKey = evt
  620. def GetLabel(self):
  621. """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
  622. return self._label
  623. def SetLabel(self, label):
  624. """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
  625. self._label = label
  626. def IsEditCancelled(self):
  627. """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
  628. return self._editCancelled
  629. def SetEditCanceled(self, editCancelled):
  630. """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only)."""
  631. self._editCancelled = editCancelled
  632. def SetToolTip(self, toolTip):
  633. """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
  634. self._label = toolTip
  635. def GetToolTip(self):
  636. """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events)."""
  637. return self._label
  638. # ----------------------------------------------------------------------------
  639. # TreeEvent is a special class for all events associated with tree controls
  640. #
  641. # NB: note that not all accessors make sense for all events, see the event
  642. # descriptions below
  643. # ----------------------------------------------------------------------------
  644. class TreeEvent(CommandTreeEvent):
  645. def __init__(self, type, id, item=None, evtKey=None, point=None,
  646. label=None, **kwargs):
  647. """
  648. Default class constructor.
  649. For internal use: do not call it in your code!
  650. """
  651. CommandTreeEvent.__init__(self, type, id, item, evtKey, point, label, **kwargs)
  652. self.notify = wx.NotifyEvent(type, id)
  653. def GetNotifyEvent(self):
  654. """Returns the actual wx.NotifyEvent."""
  655. return self.notify
  656. def IsAllowed(self):
  657. """Returns whether the event is allowed or not."""
  658. return self.notify.IsAllowed()
  659. def Veto(self):
  660. """Vetos the event."""
  661. self.notify.Veto()
  662. def Allow(self):
  663. """The event is allowed."""
  664. self.notify.Allow()
  665. # -----------------------------------------------------------------------------
  666. # Auxiliary Classes: TreeRenameTimer
  667. # -----------------------------------------------------------------------------
  668. class TreeRenameTimer(wx.Timer):
  669. """Timer used for enabling in-place edit."""
  670. def __init__(self, owner):
  671. """
  672. Default class constructor.
  673. For internal use: do not call it in your code!
  674. """
  675. wx.Timer.__init__(self)
  676. self._owner = owner
  677. def Notify(self):
  678. """The timer has expired."""
  679. self._owner.OnRenameTimer()
  680. # -----------------------------------------------------------------------------
  681. # Auxiliary Classes: TreeTextCtrl
  682. # This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item
  683. # -----------------------------------------------------------------------------
  684. class TreeTextCtrl(wx.TextCtrl):
  685. """Control used for in-place edit."""
  686. def __init__(self, owner, item=None):
  687. """
  688. Default class constructor.
  689. For internal use: do not call it in your code!
  690. """
  691. self._owner = owner
  692. self._itemEdited = item
  693. self._startValue = item.GetText()
  694. self._finished = False
  695. self._aboutToFinish = False
  696. w = self._itemEdited.GetWidth()
  697. h = self._itemEdited.GetHeight()
  698. wnd = self._itemEdited.GetWindow()
  699. if wnd:
  700. w = w - self._itemEdited.GetWindowSize()[0]
  701. h = 0
  702. x, y = self._owner.CalcScrolledPosition(item.GetX(), item.GetY())
  703. image_h = 0
  704. image_w = 0
  705. image = item.GetCurrentImage()
  706. if image != _NO_IMAGE:
  707. if self._owner._imageListNormal:
  708. image_w, image_h = self._owner._imageListNormal.GetSize(image)
  709. image_w += 4
  710. else:
  711. raise Exception("\n ERROR: You Must Create An Image List To Use Images!")
  712. checkimage = item.GetCurrentCheckedImage()
  713. if checkimage is not None:
  714. wcheck, hcheck = self._owner._imageListCheck.GetSize(checkimage)
  715. wcheck += 4
  716. else:
  717. wcheck = 0
  718. if wnd:
  719. h = max(hcheck, image_h)
  720. dc = wx.ClientDC(self._owner)
  721. h = max(h, dc.GetTextExtent("Aq")[1])
  722. h = h + 2
  723. # FIXME: what are all these hardcoded 4, 8 and 11s really?
  724. x += image_w + wcheck
  725. w -= image_w + 4 + wcheck
  726. wx.TextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue,
  727. wx.Point(x - 4, y), wx.Size(w + 15, h))
  728. if wx.Platform == "__WXMAC__":
  729. self.SetFont(owner.GetFont())
  730. bs = self.GetBestSize()
  731. self.SetSize((-1, bs.height))
  732. self.Bind(wx.EVT_CHAR, self.OnChar)
  733. self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  734. self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
  735. def AcceptChanges(self):
  736. """Accepts/refuses the changes made by the user."""
  737. value = self.GetValue()
  738. if value == self._startValue:
  739. # nothing changed, always accept
  740. # when an item remains unchanged, the owner
  741. # needs to be notified that the user decided
  742. # not to change the tree item label, and that
  743. # the edit has been cancelled
  744. self._owner.OnRenameCancelled(self._itemEdited)
  745. return True
  746. if not self._owner.OnRenameAccept(self._itemEdited, value):
  747. # vetoed by the user
  748. return False
  749. # accepted, do rename the item
  750. self._owner.SetItemText(self._itemEdited, value)
  751. return True
  752. def Finish(self):
  753. """Finish editing."""
  754. if not self._finished:
  755. ## wxPendingDelete.Append(this)
  756. self._finished = True
  757. self._owner.SetFocusIgnoringChildren()
  758. self._owner.ResetTextControl()
  759. def OnChar(self, event):
  760. """Handles the wx.EVT_CHAR event for TreeTextCtrl."""
  761. keycode = event.GetKeyCode()
  762. if keycode == wx.WXK_RETURN:
  763. self._aboutToFinish = True
  764. # Notify the owner about the changes
  765. self.AcceptChanges()
  766. # Even if vetoed, close the control (consistent with MSW)
  767. wx.CallAfter(self.Finish)
  768. elif keycode == wx.WXK_ESCAPE:
  769. self.StopEditing()
  770. else:
  771. event.Skip()
  772. def OnKeyUp(self, event):
  773. """Handles the wx.EVT_KEY_UP event for TreeTextCtrl."""
  774. if not self._finished:
  775. # auto-grow the textctrl:
  776. parentSize = self._owner.GetSize()
  777. myPos = self.GetPosition()
  778. mySize = self.GetSize()
  779. sx, sy = self.GetTextExtent(self.GetValue() + "M")
  780. if myPos.x + sx > parentSize.x:
  781. sx = parentSize.x - myPos.x
  782. if mySize.x > sx:
  783. sx = mySize.x
  784. self.SetSize((sx, -1))
  785. event.Skip()
  786. def OnKillFocus(self, event):
  787. """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl."""
  788. # I commented out those lines, and everything seems to work fine.
  789. # But why in the world are these lines of code here? Maybe GTK
  790. # or MAC give troubles?
  791. ## if not self._finished and not self._aboutToFinish:
  792. ##
  793. ## # We must finish regardless of success, otherwise we'll get
  794. ## # focus problems:
  795. ##
  796. ## if not self.AcceptChanges():
  797. ## self._owner.OnRenameCancelled(self._itemEdited)
  798. # We must let the native text control handle focus, too, otherwise
  799. # it could have problems with the cursor (e.g., in wxGTK).
  800. event.Skip()
  801. def StopEditing(self):
  802. """Suddenly stops the editing."""
  803. self._owner.OnRenameCancelled(self._itemEdited)
  804. self.Finish()
  805. def item(self):
  806. """Returns the item currently edited."""
  807. return self._itemEdited
  808. # -----------------------------------------------------------------------------
  809. # Auxiliary Classes: TreeFindTimer
  810. # Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A
  811. # Sufficiently Long Time.
  812. # -----------------------------------------------------------------------------
  813. class TreeFindTimer(wx.Timer):
  814. """
  815. Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed
  816. for a sufficiently long time.
  817. """
  818. def __init__(self, owner):
  819. """
  820. Default class constructor.
  821. For internal use: do not call it in your code!
  822. """
  823. wx.Timer.__init__(self)
  824. self._owner = owner
  825. def Notify(self):
  826. """The timer has expired."""
  827. self._owner._findPrefix = ""
  828. # -----------------------------------------------------------------------------
  829. # GenericTreeItem Implementation.
  830. # This Class Holds All The Information And Methods For Every Single Item In
  831. # CustomTreeCtrl.
  832. # -----------------------------------------------------------------------------
  833. class GenericTreeItem:
  834. """
  835. This class holds all the information and methods for every single item in
  836. CustomTreeCtrl. No wx based.
  837. """
  838. def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  839. """
  840. Default class constructor.
  841. For internal use: do not call it in your code!
  842. """
  843. # since there can be very many of these, we save size by chosing
  844. # the smallest representation for the elements and by ordering
  845. # the members to avoid padding.
  846. assert isinstance(text, types.StringTypes)
  847. self._text = text # label to be rendered for item
  848. self._data = data # user-provided data
  849. self._children = [] # list of children
  850. self._parent = parent # parent of this item
  851. self._attr = None # attributes???
  852. # tree ctrl images for the normal, selected, expanded and
  853. # expanded+selected states
  854. self._images = [-1, -1, -1, -1]
  855. self._images[TreeItemIcon_Normal] = image
  856. self._images[TreeItemIcon_Selected] = selImage
  857. self._images[TreeItemIcon_Expanded] = _NO_IMAGE
  858. self._images[TreeItemIcon_SelectedExpanded] = _NO_IMAGE
  859. self._checkedimages = [None, None, None, None]
  860. self._x = 0 # (virtual) offset from top
  861. self._y = 0 # (virtual) offset from left
  862. self._width = 0 # width of this item
  863. self._height = 0 # height of this item
  864. self._isCollapsed = True
  865. self._hasHilight = False # same as focused
  866. self._hasPlus = False # used for item which doesn't have
  867. # children but has a [+] button
  868. self._isBold = False # render the label in bold font
  869. self._isItalic = False # render the label in italic font
  870. self._ownsAttr = False # delete attribute when done
  871. self._type = ct_type # item type: 0=normal, 1=check, 2=radio
  872. self._checked = False # only meaningful for check and radio
  873. self._enabled = True # flag to enable/disable an item
  874. self._hypertext = False # indicates if the item is hypertext
  875. self._visited = False # visited state for an hypertext item
  876. if self._type > 0:
  877. # do not construct the array for normal items
  878. self._checkedimages[TreeItemIcon_Checked] = 0
  879. self._checkedimages[TreeItemIcon_NotChecked] = 1
  880. self._checkedimages[TreeItemIcon_Flagged] = 2
  881. self._checkedimages[TreeItemIcon_NotFlagged] = 3
  882. if parent:
  883. if parent.GetType() == 2 and not parent.IsChecked():
  884. # if the node parent is a radio not enabled, we are disabled
  885. self._enabled = False
  886. self._wnd = wnd # are we holding a window?
  887. if wnd:
  888. self.SetWindow(wnd)
  889. def IsOk(self):
  890. """
  891. Returns whether the item is ok or not. Useless on Python, but added for
  892. backward compatibility with the C++ implementation.
  893. """
  894. return True
  895. def GetChildren(self):
  896. """Returns the item's children."""
  897. return self._children
  898. def GetText(self):
  899. """Returns the item text."""
  900. return self._text
  901. def GetImage(self, which=TreeItemIcon_Normal):
  902. """Returns the item image for a particular state."""
  903. return self._images[which]
  904. def GetCheckedImage(self, which=TreeItemIcon_Checked):
  905. """Returns the item check image. Meaningful only for radio & check items."""
  906. return self._checkedimages[which]
  907. def GetData(self):
  908. """Returns the data associated to this item."""
  909. return self._data
  910. def SetImage(self, image, which):
  911. """Sets the item image."""
  912. self._images[which] = image
  913. def SetData(self, data):
  914. """Sets the data associated to this item."""
  915. self._data = data
  916. def SetHasPlus(self, has=True):
  917. """Sets whether an item has the 'plus' button."""
  918. self._hasPlus = has
  919. def SetBold(self, bold):
  920. """Sets the item font bold."""
  921. self._isBold = bold
  922. def SetItalic(self, italic):
  923. """Sets the item font italic."""
  924. self._isItalic = italic
  925. def GetX(self):
  926. """Returns the x position on an item in the ScrolledWindow."""
  927. return self._x
  928. def GetY(self):
  929. """Returns the y position on an item in the ScrolledWindow."""
  930. return self._y
  931. def SetX(self, x):
  932. """Sets the x position on an item in the ScrolledWindow."""
  933. self._x = x
  934. def SetY(self, y):
  935. """Sets the y position on an item in the ScrolledWindow."""
  936. self._y = y
  937. def GetHeight(self):
  938. """Returns the height of the item."""
  939. return self._height
  940. def GetWidth(self):
  941. """Returns the width of the item."""
  942. return self._width
  943. def SetHeight(self, h):
  944. """Sets the height of the item."""
  945. self._height = h
  946. def SetWidth(self, w):
  947. """Sets the width of the item."""
  948. self._width = w
  949. def SetWindow(self, wnd):
  950. """Sets the window associated to the item."""
  951. self._wnd = wnd
  952. if wnd.GetSizer(): # the window is a complex one hold by a sizer
  953. size = wnd.GetBestSize()
  954. else: # simple window, without sizers
  955. size = wnd.GetSize()
  956. # We have to bind the wx.EVT_SET_FOCUS for the associated window
  957. # No other solution to handle the focus changing from an item in
  958. # CustomTreeCtrl and the window associated to an item
  959. # Do better strategies exist?
  960. self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
  961. self._height = size.GetHeight() + 2
  962. self._width = size.GetWidth()
  963. self._windowsize = size
  964. # We don't show the window if the item is collapsed
  965. if self._isCollapsed:
  966. self._wnd.Show(False)
  967. # The window is enabled only if the item is enabled
  968. self._wnd.Enable(self._enabled)
  969. self._windowenabled = self._enabled
  970. def GetWindow(self):
  971. """Returns the window associated to the item."""
  972. return self._wnd
  973. def DeleteWindow(self):
  974. """Deletes the window associated to the item (if any)."""
  975. if self._wnd:
  976. self._wnd.Destroy()
  977. self._wnd = None
  978. def GetWindowEnabled(self):
  979. """Returns whether the associated window is enabled or not."""
  980. if not self._wnd:
  981. raise Exception("\nERROR: This Item Has No Window Associated")
  982. return self._windowenabled
  983. def SetWindowEnabled(self, enable=True):
  984. """Sets whether the associated window is enabled or not."""
  985. if not self._wnd:
  986. raise Exception("\nERROR: This Item Has No Window Associated")
  987. self._windowenabled = enable
  988. self._wnd.Enable(enable)
  989. def GetWindowSize(self):
  990. """Returns the associated window size."""
  991. return self._windowsize
  992. def OnSetFocus(self, event):
  993. """Handles the wx.EVT_SET_FOCUS event for the associated window."""
  994. treectrl = self._wnd.GetParent()
  995. select = treectrl.GetSelection()
  996. # If the window is associated to an item that currently is selected
  997. # (has focus) we don't kill the focus. Otherwise we do it.
  998. if select != self:
  999. treectrl._hasFocus = False
  1000. else:
  1001. treectrl._hasFocus = True
  1002. event.Skip()
  1003. def GetType(self):
  1004. """
  1005. Returns the item type. It should be one of:
  1006. 0: normal items
  1007. 1: checkbox item
  1008. 2: radiobutton item
  1009. """
  1010. return self._type
  1011. def SetHyperText(self, hyper=True):
  1012. """Sets whether the item is hypertext or not."""
  1013. self._hypertext = hyper
  1014. def SetVisited(self, visited=True):
  1015. """Sets whether an hypertext item was visited or not."""
  1016. self._visited = visited
  1017. def GetVisited(self):
  1018. """Returns whether an hypertext item was visited or not."""
  1019. return self._visited
  1020. def IsHyperText(self):
  1021. """Returns whether the item is hypetext or not."""
  1022. return self._hypertext
  1023. def GetParent(self):
  1024. """Gets the item parent."""
  1025. return self._parent
  1026. def Insert(self, child, index):
  1027. """Inserts an item in the item children."""
  1028. self._children.insert(index, child)
  1029. def Expand(self):
  1030. """Expand the item."""
  1031. self._isCollapsed = False
  1032. def Collapse(self):
  1033. """Collapse the item."""
  1034. self._isCollapsed = True
  1035. def SetHilight(self, set=True):
  1036. """Sets the item focus/unfocus."""
  1037. self._hasHilight = set
  1038. def HasChildren(self):
  1039. """Returns whether the item has children or not."""
  1040. return len(self._children) > 0
  1041. def IsSelected(self):
  1042. """Returns whether the item is selected or not."""
  1043. return self._hasHilight != 0
  1044. def IsExpanded(self):
  1045. """Returns whether the item is expanded or not."""
  1046. return not self._isCollapsed
  1047. def IsChecked(self):
  1048. """Returns whether the item is checked or not."""
  1049. return self._checked
  1050. def Check(self, checked=True):
  1051. """Check an item. Meaningful only for check and radio items."""
  1052. self._checked = checked
  1053. def HasPlus(self):
  1054. """Returns whether the item has the plus button or not."""
  1055. return self._hasPlus or self.HasChildren()
  1056. def IsBold(self):
  1057. """Returns whether the item font is bold or not."""
  1058. return self._isBold != 0
  1059. def IsItalic(self):
  1060. """Returns whether the item font is italic or not."""
  1061. return self._isItalic != 0
  1062. def Enable(self, enable=True):
  1063. """Enables/disables the item."""
  1064. self._enabled = enable
  1065. def IsEnabled(self):
  1066. """Returns whether the item is enabled or not."""
  1067. return self._enabled
  1068. def GetAttributes(self):
  1069. """Returns the item attributes (font, colours)."""
  1070. return self._attr
  1071. def Attr(self):
  1072. """Creates a new attribute (font, colours)."""
  1073. if not self._attr:
  1074. self._attr = TreeItemAttr()
  1075. self._ownsAttr = True
  1076. return self._attr
  1077. def SetAttributes(self, attr):
  1078. """Sets the item attributes (font, colours)."""
  1079. if self._ownsAttr:
  1080. del self._attr
  1081. self._attr = attr
  1082. self._ownsAttr = False
  1083. def AssignAttributes(self, attr):
  1084. """Assigns the item attributes (font, colours)."""
  1085. self.SetAttributes(attr)
  1086. self._ownsAttr = True
  1087. def DeleteChildren(self, tree):
  1088. """Deletes the item children."""
  1089. for child in self._children:
  1090. if tree:
  1091. tree.SendDeleteEvent(child)
  1092. child.DeleteChildren(tree)
  1093. if child == tree._select_me:
  1094. tree._select_me = None
  1095. # We have to destroy the associated window
  1096. wnd = child.GetWindow()
  1097. if wnd:
  1098. wnd.Destroy()
  1099. child._wnd = None
  1100. if child in tree._itemWithWindow:
  1101. tree._itemWithWindow.remove(child)
  1102. del child
  1103. self._children = []
  1104. def SetText(self, text):
  1105. """Sets the item text."""
  1106. assert isinstance(text, types.StringTypes)
  1107. self._text = text
  1108. def GetChildrenCount(self, recursively=True):
  1109. """Gets the number of children."""
  1110. count = len(self._children)
  1111. if not recursively:
  1112. return count
  1113. total = count
  1114. for n in xrange(count):
  1115. total += self._children[n].GetChildrenCount()
  1116. return total
  1117. def GetSize(self, x, y, theButton):
  1118. """Returns the item size."""
  1119. bottomY = self._y + theButton.GetLineHeight(self)
  1120. if y < bottomY:
  1121. y = bottomY
  1122. width = self._x + self._width
  1123. if x < width:
  1124. x = width
  1125. if self.IsExpanded():
  1126. for child in self._children:
  1127. x, y = child.GetSize(x, y, theButton)
  1128. return x, y
  1129. def HitTest(self, point, theCtrl, flags=0, level=0):
  1130. """
  1131. HitTest method for an item. Called from the main window HitTest.
  1132. see the CustomTreeCtrl HitTest method for the flags explanation.
  1133. """
  1134. # for a hidden root node, don't evaluate it, but do evaluate children
  1135. if not (level == 0 and theCtrl.HasFlag(TR_HIDE_ROOT)):
  1136. # evaluate the item
  1137. h = theCtrl.GetLineHeight(self)
  1138. if point.y > self._y and point.y < self._y + h:
  1139. y_mid = self._y + h/2
  1140. if point.y < y_mid:
  1141. flags |= TREE_HITTEST_ONITEMUPPERPART
  1142. else:
  1143. flags |= TREE_HITTEST_ONITEMLOWERPART
  1144. xCross = self._x - theCtrl.GetSpacing()
  1145. if xCross > theCtrl.GetIndent():
  1146. xCross -= theCtrl.GetIndent()
  1147. if wx.Platform == "__WXMAC__":
  1148. # according to the drawing code the triangels are drawn
  1149. # at -4 , -4 from the position up to +10/+10 max
  1150. if point.x > xCross-4 and point.x < xCross+10 and point.y > y_mid-4 and \
  1151. point.y < y_mid+10 and self.HasPlus() and theCtrl.HasButtons():
  1152. flags |= TREE_HITTEST_ONITEMBUTTON
  1153. return self, flags
  1154. else:
  1155. # 5 is the size of the plus sign
  1156. if point.x > xCross-6 and point.x < xCross+6 and point.y > y_mid-6 and \
  1157. point.y < y_mid+6 and self.HasPlus() and theCtrl.HasButtons():
  1158. flags |= TREE_HITTEST_ONITEMBUTTON
  1159. return self, flags
  1160. if point.x >= self._x and point.x <= self._x + self._width:
  1161. image_w = -1
  1162. wcheck = 0
  1163. # assuming every image (normal and selected) has the same size!
  1164. if self.GetImage() != _NO_IMAGE and theCtrl._imageListNormal:
  1165. image_w, image_h = theCtrl._imageListNormal.GetSize(self.GetImage())
  1166. if self.GetCheckedImage() is not None:
  1167. wcheck, hcheck = theCtrl._imageListCheck.GetSize(self.GetCheckedImage())
  1168. if wcheck and point.x <= self._x + wcheck + 1:
  1169. flags |= TREE_HITTEST_ONITEMCHECKICON
  1170. return self, flags
  1171. if image_w != -1 and point.x <= self._x + wcheck + image_w + 1:
  1172. flags |= TREE_HITTEST_ONITEMICON
  1173. else:
  1174. flags |= TREE_HITTEST_ONITEMLABEL
  1175. return self, flags
  1176. if point.x < self._x:
  1177. flags |= TREE_HITTEST_ONITEMINDENT
  1178. if point.x > self._x + self._width:
  1179. flags |= TREE_HITTEST_ONITEMRIGHT
  1180. return self, flags
  1181. # if children are expanded, fall through to evaluate them
  1182. if self._isCollapsed:
  1183. return None, 0
  1184. # evaluate children
  1185. for child in self._children:
  1186. res, flags = child.HitTest(point, theCtrl, flags, level + 1)
  1187. if res != None:
  1188. return res, flags
  1189. return None, 0
  1190. def GetCurrentImage(self):
  1191. """Returns the current item image."""
  1192. image = _NO_IMAGE
  1193. if self.IsExpanded():
  1194. if self.IsSelected():
  1195. image = self.GetImage(TreeItemIcon_SelectedExpanded)
  1196. if image == _NO_IMAGE:
  1197. # we usually fall back to the normal item, but try just the
  1198. # expanded one (and not selected) first in this case
  1199. image = self.GetImage(TreeItemIcon_Expanded)
  1200. else: # not expanded
  1201. if self.IsSelected():
  1202. image = self.GetImage(TreeItemIcon_Selected)
  1203. # maybe it doesn't have the specific image we want,
  1204. # try the default one instead
  1205. if image == _NO_IMAGE:
  1206. image = self.GetImage()
  1207. return image
  1208. def GetCurrentCheckedImage(self):
  1209. """Returns the current item check image."""
  1210. if self._type == 0:
  1211. return None
  1212. if self.IsChecked():
  1213. if self._type == 1: # Checkbox
  1214. return self._checkedimages[TreeItemIcon_Checked]
  1215. else: # Radiobutton
  1216. return self._checkedimages[TreeItemIcon_Flagged]
  1217. else:
  1218. if self._type == 1: # Checkbox
  1219. return self._checkedimages[TreeItemIcon_NotChecked]
  1220. else: # Radiobutton
  1221. return self._checkedimages[TreeItemIcon_NotFlagged]
  1222. def EventFlagsToSelType(style, shiftDown=False, ctrlDown=False):
  1223. """
  1224. Translate the key or mouse event flag to the type of selection we
  1225. are dealing with.
  1226. """
  1227. is_multiple = (style & TR_MULTIPLE) != 0
  1228. extended_select = shiftDown and is_multiple
  1229. unselect_others = not (extended_select or (ctrlDown and is_multiple))
  1230. return is_multiple, extended_select, unselect_others
  1231. # -----------------------------------------------------------------------------
  1232. # CustomTreeCtrl Main Implementation.
  1233. # This Is The Main Class.
  1234. # -----------------------------------------------------------------------------
  1235. class CustomTreeCtrl(wx.PyScrolledWindow):
  1236. def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
  1237. style=TR_DEFAULT_STYLE, ctstyle=0, validator=wx.DefaultValidator,
  1238. name="CustomTreeCtrl"):
  1239. """
  1240. Default class constructor.
  1241. parent: parent window. Must not be none.
  1242. id: window identifier. A value of -1 indicates a default value.
  1243. pos: window position.
  1244. size: window size. If the default size (-1, -1) is specified then the window is sized appropriately.
  1245. style: the underlying wx.ScrolledWindow style + CustomTreeCtrl window style. This can be one of:
  1246. TR_NO_BUTTONS
  1247. TR_HAS_BUTTONS # draw collapsed/expanded btns
  1248. TR_NO_LINES # don't draw lines at all
  1249. TR_LINES_AT_ROOT # connect top-level nodes
  1250. TR_TWIST_BUTTONS # draw mac-like twist buttons
  1251. TR_SINGLE # single selection mode
  1252. TR_MULTIPLE # can select multiple items
  1253. TR_EXTENDED # todo: allow extended selection
  1254. TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
  1255. TR_EDIT_LABELS # can edit item labels
  1256. TR_ROW_LINES # put border around items
  1257. TR_HIDE_ROOT # don't display root node
  1258. TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space
  1259. TR_AUTO_CHECK_CHILD # only meaningful for checkboxes
  1260. TR_AUTO_CHECK_PARENT # only meaningful for checkboxes
  1261. TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes
  1262. ctstyle: kept for backward compatibility.
  1263. validator: window validator.
  1264. name: window name.
  1265. """
  1266. style = style | ctstyle
  1267. self._current = self._key_current = self._anchor = self._select_me = None
  1268. self._hasFocus = False
  1269. self._dirty = False
  1270. # Default line height: it will soon be changed
  1271. self._lineHeight = 10
  1272. # Item indent wrt parent
  1273. self._indent = 15
  1274. # item horizontal spacing between the start and the text
  1275. self._spacing = 18
  1276. # Brushes for focused/unfocused items (also gradient type)
  1277. self._hilightBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
  1278. btnshadow = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
  1279. self._hilightUnfocusedBrush = wx.Brush(btnshadow)
  1280. r, g, b = btnshadow.Red(), btnshadow.Green(), btnshadow.Blue()
  1281. backcolour = (max((r >> 1) - 20, 0),
  1282. max((g >> 1) - 20, 0),
  1283. max((b >> 1) - 20, 0))
  1284. backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2])
  1285. self._hilightUnfocusedBrush2 = wx.Brush(backcolour)
  1286. # image list for icons
  1287. self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = None
  1288. self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = False
  1289. # Drag and drop initial settings
  1290. self._dragCount = 0
  1291. self._countDrag = 0
  1292. self._isDragging = False
  1293. self._dropTarget = self._oldSelection = None
  1294. self._dragImage = None
  1295. self._underMouse = None
  1296. self._selectedNodeWhileMousePressed = None
  1297. # TextCtrl initial settings for editable items
  1298. self._textCtrl = None
  1299. self._renameTimer = None
  1300. # This one allows us to handle Freeze() and Thaw() calls
  1301. self._freezeCount = 0
  1302. self._findPrefix = ""
  1303. self._findTimer = None
  1304. self._dropEffectAboveItem = False
  1305. self._lastOnSame = False
  1306. # Default normal and bold fonts for an item
  1307. self._hasFont = True
  1308. self._normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
  1309. self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
  1310. self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(),
  1311. self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
  1312. # Hyperlinks things
  1313. self._hypertextfont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
  1314. self._normalFont.GetStyle(), wx.NORMAL, True,
  1315. self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
  1316. self._hypertextnewcolour = wx.BLUE
  1317. self._hypertextvisitedcolour = wx.Colour(200, 47, 200)
  1318. self._isonhyperlink = False
  1319. # Default CustomTreeCtrl background colour.
  1320. # self._backgroundColour = wx.WHITE
  1321. # self._backgroundColour = wx.SystemSettings.GetColour(
  1322. # wx.SYS_COLOUR_WINDOW)
  1323. self._backgroundColour = wx.NullColour
  1324. # Background image settings
  1325. self._backgroundImage = None
  1326. self._imageStretchStyle = _StyleTile
  1327. # Disabled items colour
  1328. self._disabledColour = wx.Colour(180, 180, 180)
  1329. # Gradient selection colours
  1330. self._firstcolour = color= wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
  1331. self._secondcolour = wx.WHITE
  1332. self._usegradients = False
  1333. self._gradientstyle = 0 # Horizontal Gradient
  1334. # Vista Selection Styles
  1335. self._vistaselection = False
  1336. self._defaultScrollVisiblePos = "auto" # Other possibility: "middle"
  1337. # Connection lines style
  1338. # if wx.Platform != "__WXMAC__":
  1339. if wx.GetOsVersion()[0] == wxWINDOWS_NT:
  1340. self._dottedPen = wx.Pen("grey", 1, wx.USER_DASH)
  1341. self._dottedPen.SetDashes([1,1])
  1342. self._dottedPen.SetCap(wx.CAP_BUTT)
  1343. else:
  1344. self._dottedPen = wx.Pen("light grey", 1)
  1345. # Pen Used To Draw The Border Around Selected Items
  1346. self._borderPen = wx.BLACK_PEN
  1347. self._cursor = wx.StockCursor(wx.CURSOR_ARROW)
  1348. # For Appended Windows
  1349. self._hasWindows = False
  1350. self._itemWithWindow = []
  1351. if wx.Platform == "__WXMAC__":
  1352. style &= ~TR_LINES_AT_ROOT
  1353. style |= TR_NO_LINES
  1354. platform, major, minor = wx.GetOsVersion()
  1355. if major < 10:
  1356. style |= TR_ROW_LINES
  1357. self._windowStyle = style
  1358. # Create the default check image list
  1359. self.SetImageListCheck(13, 13)
  1360. # A constant to use my translation of RendererNative.DrawTreeItemButton
  1361. # if the wxPython version is less or equal 2.6.3.2.
  1362. ## if wx.VERSION_STRING < "2.6.2.1":
  1363. if wx.VERSION_STRING <= "2.6.3.2":
  1364. self._drawingfunction = DrawTreeItemButton
  1365. else:
  1366. self._drawingfunction = wx.RendererNative.Get().DrawTreeItemButton
  1367. # Create our container... at last!
  1368. wx.PyScrolledWindow.__init__(self, parent, id, pos, size, style|wx.HSCROLL|wx.VSCROLL, name)
  1369. # If the tree display has no buttons, but does have
  1370. # connecting lines, we can use a narrower layout.
  1371. # It may not be a good idea to force this...
  1372. if not self.HasButtons() and not self.HasFlag(TR_NO_LINES):
  1373. self._indent= 10
  1374. self._spacing = 10
  1375. self.SetValidator(validator)
  1376. attr = self.GetDefaultAttributes()
  1377. self.SetOwnForegroundColour(attr.colFg)
  1378. self.SetOwnBackgroundColour(wx.WHITE)
  1379. if not self._hasFont:
  1380. self.SetOwnFont(attr.font)
  1381. self.SetSize(size)
  1382. # Bind the events
  1383. self.Bind(wx.EVT_PAINT, self.OnPaint)
  1384. self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
  1385. self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged)
  1386. self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
  1387. self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
  1388. self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
  1389. self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
  1390. self.Bind(EVT_TREE_ITEM_GETTOOLTIP, self.OnGetToolTip)
  1391. self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
  1392. # Sets the focus to ourselves: this is useful if you have items
  1393. # with associated widgets.
  1394. self.SetFocus()
  1395. def AcceptsFocus(self):
  1396. # overridden base class method, allows this ctrl to
  1397. # participate in the tab-order, etc. It's overridable because
  1398. # of deriving this class from wx.PyScrolledWindow...
  1399. return True
  1400. def OnDestroy(self, event):
  1401. """Handles the wx.EVT_WINDOW_DESTROY event."""
  1402. # Here there may be something I miss... do I have to destroy
  1403. # something else?
  1404. if self._renameTimer and self._renameTimer.IsRunning():
  1405. self._renameTimer.Stop()
  1406. del self._renameTimer
  1407. if self._findTimer and self._findTimer.IsRunning():
  1408. self._findTimer.Stop()
  1409. del self._findTimer
  1410. event.Skip()
  1411. def GetCount(self):
  1412. """Returns the global number of items in the tree."""
  1413. if not self._anchor:
  1414. # the tree is empty
  1415. return 0
  1416. count = self._anchor.GetChildrenCount()
  1417. if not self.HasFlag(TR_HIDE_ROOT):
  1418. # take the root itself into account
  1419. count = count + 1
  1420. return count
  1421. def GetIndent(self):
  1422. """Returns the item indentation."""
  1423. return self._indent
  1424. def GetSpacing(self):
  1425. """Returns the spacing between the start and the text."""
  1426. return self._spacing
  1427. def GetRootItem(self):
  1428. """Returns the root item."""
  1429. return self._anchor
  1430. def GetSelection(self):
  1431. """Returns the current selection: TR_SINGLE only."""
  1432. return self._current
  1433. def ToggleItemSelection(self, item):
  1434. """Toggles the item selection."""
  1435. if not item:
  1436. raise Exception("\nERROR: Invalid Tree Item. ")
  1437. self.SelectItem(item, not self.IsSelected(item))
  1438. def EnableChildren(self, item, enable=True):
  1439. """Enables/disables item children. Used internally."""
  1440. torefresh = False
  1441. if item.IsExpanded():
  1442. torefresh = True
  1443. if item.GetType() == 2 and enable and not item.IsChecked():
  1444. # We hit a radiobutton item not checked, we don't want to
  1445. # enable the children
  1446. return
  1447. child, cookie = self.GetFirstChild(item)
  1448. while child:
  1449. self.EnableItem(child, enable, torefresh=torefresh)
  1450. # Recurse on tree
  1451. if child.GetType != 2 or (child.GetType() == 2 and item.IsChecked()):
  1452. self.EnableChildren(child, enable)
  1453. (child, cookie) = self.GetNextChild(item, cookie)
  1454. def EnableItem(self, item, enable=True, torefresh=True):
  1455. """Enables/disables an item."""
  1456. if not item:
  1457. raise Exception("\nERROR: Invalid Tree Item. ")
  1458. if item.IsEnabled() == enable:
  1459. return
  1460. if not enable and item.IsSelected():
  1461. self.SelectItem(item, False)
  1462. item.Enable(enable)
  1463. wnd = item.GetWindow()
  1464. # Handles the eventual window associated to the item
  1465. if wnd:
  1466. wndenable = item.GetWindowEnabled()
  1467. if enable:
  1468. if wndenable:
  1469. wnd.Enable(enable)
  1470. else:
  1471. wnd.Enable(enable)
  1472. if torefresh:
  1473. # We have to refresh the item line
  1474. dc = wx.ClientDC(self)
  1475. self.CalculateSize(item, dc)
  1476. self.RefreshLine(item)
  1477. def IsItemEnabled(self, item):
  1478. """Returns whether an item is enabled or disabled."""
  1479. if not item:
  1480. raise Exception("\nERROR: Invalid Tree Item. ")
  1481. return item.IsEnabled()
  1482. def SetDisabledColour(self, colour):
  1483. """Sets the items disabled colour."""
  1484. self._disabledColour = colour
  1485. self._dirty = True
  1486. def GetDisabledColour(self):
  1487. """Returns the items disabled colour."""
  1488. return self._disabledColour
  1489. def IsItemChecked(self, item):
  1490. """Returns whether an item is checked or not."""
  1491. if not item:
  1492. raise Exception("\nERROR: Invalid Tree Item. ")
  1493. return item.IsChecked()
  1494. def CheckItem2(self, item, checked=True, torefresh=False):
  1495. """Used internally to avoid EVT_TREE_ITEM_CHECKED events."""
  1496. if item.GetType() == 0:
  1497. return
  1498. item.Check(checked)
  1499. if torefresh:
  1500. dc = wx.ClientDC(self)
  1501. self.CalculateSize(item, dc)
  1502. self.RefreshLine(item)
  1503. def UnCheckRadioParent(self, item, checked=False):
  1504. """Used internally to handle radio node parent correctly."""
  1505. e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
  1506. e.SetItem(item)
  1507. e.SetEventObject(self)
  1508. if self.GetEventHandler().ProcessEvent(e):
  1509. return False
  1510. item.Check(checked)
  1511. self.RefreshLine(item)
  1512. self.EnableChildren(item, checked)
  1513. e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
  1514. e.SetItem(item)
  1515. e.SetEventObject(self)
  1516. self.GetEventHandler().ProcessEvent(e)
  1517. return True
  1518. def CheckItem(self, item, checked=True):
  1519. """
  1520. Actually checks/uncheks an item, sending (eventually) the two
  1521. events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED.
  1522. """
  1523. if not item:
  1524. raise Exception("\nERROR: Invalid Tree Item. ")
  1525. # Should we raise an error here?!?
  1526. if item.GetType() == 0:
  1527. return
  1528. if item.GetType() == 2: # it's a radio button
  1529. if not checked and item.IsChecked(): # Try To Unckeck?
  1530. if item.HasChildren():
  1531. self.UnCheckRadioParent(item, checked)
  1532. return
  1533. else:
  1534. if not self.UnCheckRadioParent(item, checked):
  1535. return
  1536. self.CheckSameLevel(item, False)
  1537. return
  1538. # Radiobuttons are done, let's handle checkbuttons...
  1539. e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId())
  1540. e.SetItem(item)
  1541. e.SetEventObject(self)
  1542. if self.GetEventHandler().ProcessEvent(e):
  1543. # Blocked by user
  1544. return
  1545. item.Check(checked)
  1546. dc = wx.ClientDC(self)
  1547. self.RefreshLine(item)
  1548. if self._windowStyle & TR_AUTO_CHECK_CHILD:
  1549. ischeck = self.IsItemChecked(item)
  1550. self.AutoCheckChild(item, ischeck)
  1551. if self._windowStyle & TR_AUTO_CHECK_PARENT:
  1552. ischeck = self.IsItemChecked(item)
  1553. self.AutoCheckParent(item, ischeck)
  1554. elif self._windowStyle & TR_AUTO_TOGGLE_CHILD:
  1555. self.AutoToggleChild(item)
  1556. e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
  1557. e.SetItem(item)
  1558. e.SetEventObject(self)
  1559. self.GetEventHandler().ProcessEvent(e)
  1560. def AutoToggleChild(self, item):
  1561. """Transverses the tree and toggles the items. Meaningful only for check items."""
  1562. if not item:
  1563. raise Exception("\nERROR: Invalid Tree Item. ")
  1564. child, cookie = self.GetFirstChild(item)
  1565. torefresh = False
  1566. if item.IsExpanded():
  1567. torefresh = True
  1568. # Recurse on tree
  1569. while child:
  1570. if child.GetType() == 1 and child.IsEnabled():
  1571. self.CheckItem2(child, not child.IsChecked(), torefresh=torefresh)
  1572. self.AutoToggleChild(child)
  1573. (child, cookie) = self.GetNextChild(item, cookie)
  1574. def AutoCheckChild(self, item, checked):
  1575. """Transverses the tree and checks/unchecks the items. Meaningful only for check items."""
  1576. if not item:
  1577. raise Exception("\nERROR: Invalid Tree Item. ")
  1578. (child, cookie) = self.GetFirstChild(item)
  1579. torefresh = False
  1580. if item.IsExpanded():
  1581. torefresh = True
  1582. while child:
  1583. if child.GetType() == 1 and child.IsEnabled():
  1584. self.CheckItem2(child, checked, torefresh=torefresh)
  1585. self.AutoCheckChild(child, checked)
  1586. (child, cookie) = self.GetNextChild(item, cookie)
  1587. def AutoCheckParent(self, item, checked):
  1588. """Traverses up the tree and checks/unchecks parent items.
  1589. Meaningful only for check items."""
  1590. if not item:
  1591. raise Exception("\nERROR: Invalid Tree Item. ")
  1592. parent = item.GetParent()
  1593. if not parent or parent.GetType() != 1:
  1594. return
  1595. (child, cookie) = self.GetFirstChild(parent)
  1596. while child:
  1597. if child.GetType() == 1 and child.IsEnabled():
  1598. if checked != child.IsChecked():
  1599. return
  1600. (child, cookie) = self.GetNextChild(parent, cookie)
  1601. self.CheckItem2(parent, checked, torefresh=True)
  1602. self.AutoCheckParent(parent, checked)
  1603. def CheckChilds(self, item, checked=True):
  1604. """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events."""
  1605. if not item:
  1606. raise Exception("\nERROR: Invalid Tree Item. ")
  1607. if checked == None:
  1608. self.AutoToggleChild(item)
  1609. else:
  1610. self.AutoCheckChild(item, checked)
  1611. def CheckSameLevel(self, item, checked=False):
  1612. """
  1613. Uncheck radio items which are on the same level of the checked one.
  1614. Used internally.
  1615. """
  1616. parent = item.GetParent()
  1617. if not parent:
  1618. return
  1619. torefresh = False
  1620. if parent.IsExpanded():
  1621. torefresh = True
  1622. (child, cookie) = self.GetFirstChild(parent)
  1623. while child:
  1624. if child.GetType() == 2 and child != item:
  1625. self.CheckItem2(child, checked, torefresh=torefresh)
  1626. if child.GetType != 2 or (child.GetType() == 2 and child.IsChecked()):
  1627. self.EnableChildren(child, checked)
  1628. (child, cookie) = self.GetNextChild(parent, cookie)
  1629. def EditLabel(self, item):
  1630. """Starts editing an item label."""
  1631. if not item:
  1632. raise Exception("\nERROR: Invalid Tree Item. ")
  1633. self.Edit(item)
  1634. def ShouldInheritColours(self):
  1635. """We don't inherit colours from anyone."""
  1636. return False
  1637. def SetIndent(self, indent):
  1638. """Sets item indentation."""
  1639. self._indent = indent
  1640. self._dirty = True
  1641. def SetSpacing(self, spacing):
  1642. """Sets item spacing."""
  1643. self._spacing = spacing
  1644. self._dirty = True
  1645. def HasFlag(self, flag):
  1646. """Returns whether CustomTreeCtrl has a flag."""
  1647. return self._windowStyle & flag
  1648. def HasChildren(self, item):
  1649. """Returns whether an item has children or not."""
  1650. if not item:
  1651. raise Exception("\nERROR: Invalid Tree Item. ")
  1652. return len(item.GetChildren()) > 0
  1653. def GetChildrenCount(self, item, recursively=True):
  1654. """Gets the item children count."""
  1655. if not item:
  1656. raise Exception("\nERROR: Invalid Tree Item. ")
  1657. return item.GetChildrenCount(recursively)
  1658. def SetTreeStyle(self, styles):
  1659. """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation."""
  1660. # Do not try to expand the root node if it hasn't been created yet
  1661. if self._anchor and not self.HasFlag(TR_HIDE_ROOT) and styles & TR_HIDE_ROOT:
  1662. # if we will hide the root, make sure children are visible
  1663. self._anchor.SetHasPlus()
  1664. self._anchor.Expand()
  1665. self.CalculatePositions()
  1666. # right now, just sets the styles. Eventually, we may
  1667. # want to update the inherited styles, but right now
  1668. # none of the parents has updatable styles
  1669. if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE):
  1670. selections = self.GetSelections()
  1671. for select in selections[0:-1]:
  1672. self.SelectItem(select, False)
  1673. self._windowStyle = styles
  1674. self._dirty = True
  1675. def GetTreeStyle(self):
  1676. """Returns the CustomTreeCtrl style."""
  1677. return self._windowStyle
  1678. def HasButtons(self):
  1679. """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag."""
  1680. return self.HasFlag(TR_HAS_BUTTONS)
  1681. # -----------------------------------------------------------------------------
  1682. # functions to work with tree items
  1683. # -----------------------------------------------------------------------------
  1684. def GetItemText(self, item):
  1685. """Returns the item text."""
  1686. if not item:
  1687. raise Exception("\nERROR: Invalid Tree Item. ")
  1688. return item.GetText()
  1689. def GetItemImage(self, item, which=TreeItemIcon_Normal):
  1690. """Returns the item image."""
  1691. if not item:
  1692. raise Exception("\nERROR: Invalid Tree Item. ")
  1693. return item.GetImage(which)
  1694. def GetPyData(self, item):
  1695. """Returns the data associated to an item."""
  1696. if not item:
  1697. raise Exception("\nERROR: Invalid Tree Item. ")
  1698. return item.GetData()
  1699. GetItemPyData = GetPyData
  1700. def GetItemTextColour(self, item):
  1701. """Returns the item text colour."""
  1702. if not item:
  1703. raise Exception("\nERROR: Invalid Tree Item. ")
  1704. return item.Attr().GetTextColour()
  1705. def GetItemBackgroundColour(self, item):
  1706. """Returns the item background colour."""
  1707. if not item:
  1708. raise Exception("\nERROR: Invalid Tree Item. ")
  1709. return item.Attr().GetBackgroundColour()
  1710. def GetItemFont(self, item):
  1711. """Returns the item font."""
  1712. if not item:
  1713. raise Exception("\nERROR: Invalid Tree Item. ")
  1714. return item.Attr().GetFont()
  1715. def IsItemHyperText(self, item):
  1716. """Returns whether an item is hypertext or not."""
  1717. if not item:
  1718. raise Exception("\nERROR: Invalid Tree Item. ")
  1719. return item.IsHyperText()
  1720. def SetItemText(self, item, text, recalcSize=True):
  1721. """Sets the item text."""
  1722. if not item:
  1723. raise Exception("\nERROR: Invalid Tree Item. ")
  1724. # dc = wx.ClientDC(self)
  1725. # item.SetText(text)
  1726. # self.CalculateSize(item, dc)
  1727. # self.RefreshLine(item)
  1728. item.SetText(text)
  1729. if recalcSize:
  1730. dc = wx.ClientDC(self)
  1731. self.CalculateSize(item, dc)
  1732. self.RefreshLine(item)
  1733. def SetItemImage(self, item, image, which=TreeItemIcon_Normal):
  1734. """Sets the item image, depending on the item state."""
  1735. if not item:
  1736. raise Exception("\nERROR: Invalid Tree Item. ")
  1737. item.SetImage(image, which)
  1738. dc = wx.ClientDC(self)
  1739. self.CalculateSize(item, dc)
  1740. self.RefreshLine(item)
  1741. def SetPyData(self, item, data):
  1742. """Sets the data associated to an item."""
  1743. if not item:
  1744. raise Exception("\nERROR: Invalid Tree Item. ")
  1745. item.SetData(data)
  1746. SetItemPyData = SetPyData
  1747. def SetItemHasChildren(self, item, has=True):
  1748. """Forces the appearance of the button next to the item."""
  1749. if not item:
  1750. raise Exception("\nERROR: Invalid Tree Item. ")
  1751. item.SetHasPlus(has)
  1752. self.RefreshLine(item)
  1753. def SetItemBold(self, item, bold=True):
  1754. """Sets the item font bold/unbold."""
  1755. if not item:
  1756. raise Exception("\nERROR: Invalid Tree Item. ")
  1757. # avoid redrawing the tree if no real change
  1758. if item.IsBold() != bold:
  1759. item.SetBold(bold)
  1760. self._dirty = True
  1761. def SetItemItalic(self, item, italic=True):
  1762. """Sets the item font italic/non-italic."""
  1763. if not item:
  1764. raise Exception("\nERROR: Invalid Tree Item. ")
  1765. if item.IsItalic() != italic:
  1766. itemFont = self.GetItemFont(item)
  1767. if itemFont != wx.NullFont:
  1768. style = wx.ITALIC
  1769. if not italic:
  1770. style = ~style
  1771. item.SetItalic(italic)
  1772. itemFont.SetStyle(style)
  1773. self.SetItemFont(item, itemFont)
  1774. self._dirty = True
  1775. def SetItemDropHighlight(self, item, highlight=True):
  1776. """
  1777. Gives the item the visual feedback for drag and drop operations.
  1778. This is useful when something is dragged from outside the CustomTreeCtrl.
  1779. """
  1780. if not item:
  1781. raise Exception("\nERROR: Invalid Tree Item. ")
  1782. if highlight:
  1783. bg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
  1784. fg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
  1785. item.Attr().SetTextColour(fg)
  1786. item.Attr.SetBackgroundColour(bg)
  1787. self.RefreshLine(item)
  1788. def SetItemTextColour(self, item, col):
  1789. """Sets the item text colour."""
  1790. if not item:
  1791. raise Exception("\nERROR: Invalid Tree Item. ")
  1792. if self.GetItemTextColour(item) == col:
  1793. return
  1794. item.Attr().SetTextColour(col)
  1795. self.RefreshLine(item)
  1796. def SetItemBackgroundColour(self, item, col):
  1797. """Sets the item background colour."""
  1798. if not item:
  1799. raise Exception("\nERROR: Invalid Tree Item. ")
  1800. item.Attr().SetBackgroundColour(col)
  1801. self.RefreshLine(item)
  1802. def SetItemHyperText(self, item, hyper=True):
  1803. """Sets whether the item is hypertext or not."""
  1804. if not item:
  1805. raise Exception("\nERROR: Invalid Tree Item. ")
  1806. item.SetHyperText(hyper)
  1807. self.RefreshLine(item)
  1808. def SetItemFont(self, item, font):
  1809. """Sets the item font."""
  1810. if not item:
  1811. raise Exception("\nERROR: Invalid Tree Item. ")
  1812. if self.GetItemFont(item) == font:
  1813. return
  1814. item.Attr().SetFont(font)
  1815. self._dirty = True
  1816. def SetFont(self, font):
  1817. """Sets the CustomTreeCtrl font."""
  1818. if font is None or not font.IsOk():
  1819. font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
  1820. wx.ScrolledWindow.SetFont(self, font)
  1821. self._normalFont = font
  1822. self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(),
  1823. self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(),
  1824. self._normalFont.GetFaceName(), self._normalFont.GetEncoding())
  1825. return True
  1826. def GetHyperTextFont(self):
  1827. """Returns the font used to render an hypertext item."""
  1828. return self._hypertextfont
  1829. def SetHyperTextFont(self, font):
  1830. """Sets the font used to render an hypertext item."""
  1831. self._hypertextfont = font
  1832. self._dirty = True
  1833. def SetHyperTextNewColour(self, colour):
  1834. """Sets the colour used to render a non-visited hypertext item."""
  1835. self._hypertextnewcolour = colour
  1836. self._dirty = True
  1837. def GetHyperTextNewColour(self):
  1838. """Returns the colour used to render a non-visited hypertext item."""
  1839. return self._hypertextnewcolour
  1840. def SetHyperTextVisitedColour(self, colour):
  1841. """Sets the colour used to render a visited hypertext item."""
  1842. self._hypertextvisitedcolour = colour
  1843. self._dirty = True
  1844. def GetHyperTextVisitedColour(self):
  1845. """Returns the colour used to render a visited hypertext item."""
  1846. return self._hypertextvisitedcolour
  1847. def SetItemVisited(self, item, visited=True):
  1848. """Sets whether an hypertext item was visited."""
  1849. if not item:
  1850. raise Exception("\nERROR: Invalid Tree Item. ")
  1851. item.SetVisited(visited)
  1852. self.RefreshLine(item)
  1853. def GetItemVisited(self, item):
  1854. """Returns whether an hypertext item was visited."""
  1855. if not item:
  1856. raise Exception("\nERROR: Invalid Tree Item. ")
  1857. return item.GetVisited()
  1858. def SetHilightFocusColour(self, colour):
  1859. """
  1860. Sets the colour used to highlight focused selected items.
  1861. This is applied only if gradient and Windows Vista styles are disabled.
  1862. """
  1863. self._hilightBrush = wx.Brush(colour)
  1864. self.RefreshSelected()
  1865. def SetHilightNonFocusColour(self, colour):
  1866. """
  1867. Sets the colour used to highlight unfocused selected items.
  1868. This is applied only if gradient and Windows Vista styles are disabled.
  1869. """
  1870. self._hilightUnfocusedBrush = wx.Brush(colour)
  1871. self.RefreshSelected()
  1872. def GetHilightFocusColour(self):
  1873. """
  1874. Returns the colour used to highlight focused selected items.
  1875. This is applied only if gradient and Windows Vista styles are disabled.
  1876. """
  1877. return self._hilightBrush.GetColour()
  1878. def GetHilightNonFocusColour(self):
  1879. """
  1880. Returns the colour used to highlight unfocused selected items.
  1881. This is applied only if gradient and Windows Vista styles are disabled.
  1882. """
  1883. return self._hilightUnfocusedBrush.GetColour()
  1884. def SetFirstGradientColour(self, colour=None):
  1885. """Sets the first gradient colour."""
  1886. if colour is None:
  1887. colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
  1888. self._firstcolour = colour
  1889. if self._usegradients:
  1890. self.RefreshSelected()
  1891. def SetSecondGradientColour(self, colour=None):
  1892. """Sets the second gradient colour."""
  1893. if colour is None:
  1894. # No colour given, generate a slightly darker from the
  1895. # CustomTreeCtrl background colour
  1896. color = self.GetBackgroundColour()
  1897. r, g, b = int(color.Red()), int(color.Green()), int(color.Blue())
  1898. color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20)
  1899. colour = wx.Colour(color[0], color[1], color[2])
  1900. self._secondcolour = colour
  1901. if self._usegradients:
  1902. self.RefreshSelected()
  1903. def GetFirstGradientColour(self):
  1904. """Returns the first gradient colour."""
  1905. return self._firstcolour
  1906. def GetSecondGradientColour(self):
  1907. """Returns the second gradient colour."""
  1908. return self._secondcolour
  1909. def EnableSelectionGradient(self, enable=True):
  1910. """Globally enables/disables drawing of gradient selection."""
  1911. self._usegradients = enable
  1912. self._vistaselection = False
  1913. self.RefreshSelected()
  1914. def SetGradientStyle(self, vertical=0):
  1915. """
  1916. Sets the gradient style:
  1917. 0: horizontal gradient
  1918. 1: vertical gradient
  1919. """
  1920. # 0 = Horizontal, 1 = Vertical
  1921. self._gradientstyle = vertical
  1922. if self._usegradients:
  1923. self.RefreshSelected()
  1924. def GetGradientStyle(self):
  1925. """
  1926. Returns the gradient style:
  1927. 0: horizontal gradient
  1928. 1: vertical gradient
  1929. """
  1930. return self._gradientstyle
  1931. def EnableSelectionVista(self, enable=True):
  1932. """Globally enables/disables drawing of Windows Vista selection."""
  1933. self._usegradients = False
  1934. self._vistaselection = enable
  1935. self.RefreshSelected()
  1936. def SetBorderPen(self, pen):
  1937. """
  1938. Sets the pen used to draw the selected item border.
  1939. The border pen is not used if the Windows Vista style is applied.
  1940. """
  1941. self._borderPen = pen
  1942. self.RefreshSelected()
  1943. def GetBorderPen(self):
  1944. """
  1945. Returns the pen used to draw the selected item border.
  1946. The border pen is not used if the Windows Vista style is applied.
  1947. """
  1948. return self._borderPen
  1949. def SetConnectionPen(self, pen):
  1950. """Sets the pen used to draw the connecting lines between items."""
  1951. self._dottedPen = pen
  1952. self._dirty = True
  1953. def GetConnectionPen(self):
  1954. """Returns the pen used to draw the connecting lines between items."""
  1955. return self._dottedPen
  1956. def SetBackgroundImage(self, image):
  1957. """Sets the CustomTreeCtrl background image (can be none)."""
  1958. self._backgroundImage = image
  1959. self.Refresh()
  1960. def GetBackgroundImage(self):
  1961. """Returns the CustomTreeCtrl background image (can be none)."""
  1962. return self._backgroundImage
  1963. def GetItemWindow(self, item):
  1964. """Returns the window associated to the item (if any)."""
  1965. if not item:
  1966. raise Exception("\nERROR: Invalid Item")
  1967. return item.GetWindow()
  1968. def GetItemWindowEnabled(self, item):
  1969. """Returns whether the window associated to the item is enabled."""
  1970. if not item:
  1971. raise Exception("\nERROR: Invalid Item")
  1972. return item.GetWindowEnabled()
  1973. def SetItemWindowEnabled(self, item, enable=True):
  1974. """Enables/disables the window associated to the item."""
  1975. if not item:
  1976. raise Exception("\nERROR: Invalid Item")
  1977. item.SetWindowEnabled(enable)
  1978. def GetItemType(self, item):
  1979. """
  1980. Returns the item type:
  1981. 0: normal
  1982. 1: checkbox item
  1983. 2: radiobutton item
  1984. """
  1985. if not item:
  1986. raise Exception("\nERROR: Invalid Item")
  1987. return item.GetType()
  1988. # -----------------------------------------------------------------------------
  1989. # item status inquiries
  1990. # -----------------------------------------------------------------------------
  1991. def IsVisible(self, item):
  1992. """Returns whether the item is visible or not."""
  1993. if not item:
  1994. raise Exception("\nERROR: Invalid Tree Item. ")
  1995. # An item is only visible if it's not a descendant of a collapsed item
  1996. parent = item.GetParent()
  1997. while parent:
  1998. if not parent.IsExpanded():
  1999. return False
  2000. parent = parent.GetParent()
  2001. startX, startY = self.GetViewStart()
  2002. clientSize = self.GetClientSize()
  2003. rect = self.GetBoundingRect(item)
  2004. if not rect:
  2005. return False
  2006. if rect.GetWidth() == 0 or rect.GetHeight() == 0:
  2007. return False
  2008. if rect.GetBottom() < 0 or rect.GetTop() > clientSize.y:
  2009. return False
  2010. if rect.GetRight() < 0 or rect.GetLeft() > clientSize.x:
  2011. return False
  2012. return True
  2013. def ItemHasChildren(self, item):
  2014. """Returns whether the item has children or not."""
  2015. if not item:
  2016. raise Exception("\nERROR: Invalid Tree Item. ")
  2017. # consider that the item does have children if it has the "+" button: it
  2018. # might not have them (if it had never been expanded yet) but then it
  2019. # could have them as well and it's better to err on this side rather than
  2020. # disabling some operations which are restricted to the items with
  2021. # children for an item which does have them
  2022. return item.HasPlus()
  2023. def IsExpanded(self, item):
  2024. """Returns whether the item is expanded or not."""
  2025. if not item:
  2026. raise Exception("\nERROR: Invalid Tree Item. ")
  2027. return item.IsExpanded()
  2028. def IsSelected(self, item):
  2029. """Returns whether the item is selected or not."""
  2030. if not item:
  2031. raise Exception("\nERROR: Invalid Tree Item. ")
  2032. return item.IsSelected()
  2033. def IsBold(self, item):
  2034. """Returns whether the item font is bold or not."""
  2035. if not item:
  2036. raise Exception("\nERROR: Invalid Tree Item. ")
  2037. return item.IsBold()
  2038. def IsItalic(self, item):
  2039. """Returns whether the item font is italic or not."""
  2040. if not item:
  2041. raise Exception("\nERROR: Invalid Tree Item. ")
  2042. return item.IsItalic()
  2043. # -----------------------------------------------------------------------------
  2044. # navigation
  2045. # -----------------------------------------------------------------------------
  2046. def GetItemParent(self, item):
  2047. """Gets the item parent."""
  2048. if not item:
  2049. raise Exception("\nERROR: Invalid Tree Item. ")
  2050. return item.GetParent()
  2051. def GetFirstChild(self, item):
  2052. """Gets the item first child."""
  2053. if not item:
  2054. raise Exception("\nERROR: Invalid Tree Item. ")
  2055. cookie = 0
  2056. return self.GetNextChild(item, cookie)
  2057. def GetNextChild(self, item, cookie):
  2058. """
  2059. Gets the item next child based on the 'cookie' parameter.
  2060. This method has no sense if you do not call GetFirstChild() before.
  2061. """
  2062. if not item:
  2063. raise Exception("\nERROR: Invalid Tree Item. ")
  2064. children = item.GetChildren()
  2065. # it's ok to cast cookie to size_t, we never have indices big enough to
  2066. # overflow "void *"
  2067. if cookie < len(children):
  2068. return children[cookie], cookie+1
  2069. else:
  2070. # there are no more of them
  2071. return None, cookie
  2072. def GetLastChild(self, item):
  2073. """Gets the item last child."""
  2074. if not item:
  2075. raise Exception("\nERROR: Invalid Tree Item. ")
  2076. children = item.GetChildren()
  2077. return (len(children) == 0 and [None] or [children[-1]])[0]
  2078. def GetNextSibling(self, item):
  2079. """Gets the next sibling of an item."""
  2080. if not item:
  2081. raise Exception("\nERROR: Invalid Tree Item. ")
  2082. i = item
  2083. parent = i.GetParent()
  2084. if parent == None:
  2085. # root item doesn't have any siblings
  2086. return None
  2087. siblings = parent.GetChildren()
  2088. index = siblings.index(i)
  2089. n = index + 1
  2090. return (n == len(siblings) and [None] or [siblings[n]])[0]
  2091. def GetPrevSibling(self, item):
  2092. """Gets the previous sibling of an item."""
  2093. if not item:
  2094. raise Exception("\nERROR: Invalid Tree Item. ")
  2095. i = item
  2096. parent = i.GetParent()
  2097. if parent == None:
  2098. # root item doesn't have any siblings
  2099. return None
  2100. siblings = parent.GetChildren()
  2101. index = siblings.index(i)
  2102. return (index == 0 and [None] or [siblings[index-1]])[0]
  2103. def GetNext(self, item):
  2104. """Gets the next item. Only for internal use right now."""
  2105. if not item:
  2106. raise Exception("\nERROR: Invalid Tree Item. ")
  2107. i = item
  2108. # First see if there are any children.
  2109. children = i.GetChildren()
  2110. if len(children) > 0:
  2111. return children[0]
  2112. else:
  2113. # Try a sibling of this or ancestor instead
  2114. p = item
  2115. toFind = None
  2116. while p and not toFind:
  2117. toFind = self.GetNextSibling(p)
  2118. p = self.GetItemParent(p)
  2119. return toFind
  2120. def GetFirstVisibleItem(self):
  2121. """Returns the first visible item."""
  2122. id = self.GetRootItem()
  2123. if not id:
  2124. return id
  2125. while id:
  2126. if self.IsVisible(id):
  2127. return id
  2128. id = self.GetNext(id)
  2129. return None
  2130. def GetNextVisible(self, item):
  2131. """Returns the next visible item."""
  2132. if not item:
  2133. raise Exception("\nERROR: Invalid Tree Item. ")
  2134. id = item
  2135. while id:
  2136. id = self.GetNext(id)
  2137. if id and self.IsVisible(id):
  2138. return id
  2139. return None
  2140. def GetPrevVisible(self, item):
  2141. if not item:
  2142. raise Exception("\nERROR: Invalid Tree Item. ")
  2143. raise Exception("\nERROR: Not Implemented")
  2144. return None
  2145. def ResetTextControl(self):
  2146. """Called by TreeTextCtrl when it marks itself for deletion."""
  2147. self._textCtrl.Destroy()
  2148. self._textCtrl = None
  2149. def FindItem(self, idParent, prefixOrig):
  2150. """Finds the first item starting with the given prefix after the given item."""
  2151. # match is case insensitive as this is more convenient to the user: having
  2152. # to press Shift-letter to go to the item starting with a capital letter
  2153. # would be too bothersome
  2154. prefix = prefixOrig.lower()
  2155. # determine the starting point: we shouldn't take the current item (this
  2156. # allows to switch between two items starting with the same letter just by
  2157. # pressing it) but we shouldn't jump to the next one if the user is
  2158. # continuing to type as otherwise he might easily skip the item he wanted
  2159. id = idParent
  2160. if len(prefix) == 1:
  2161. id = self.GetNext(id)
  2162. # look for the item starting with the given prefix after it
  2163. while id and not self.GetItemText(id).lower().startswith(prefix):
  2164. id = self.GetNext(id)
  2165. # if we haven't found anything...
  2166. if not id:
  2167. # ... wrap to the beginning
  2168. id = self.GetRootItem()
  2169. if self.HasFlag(TR_HIDE_ROOT):
  2170. # can't select virtual root
  2171. id = self.GetNext(id)
  2172. # and try all the items (stop when we get to the one we started from)
  2173. while id != idParent and not self.GetItemText(id).lower().startswith(prefix):
  2174. id = self.GetNext(id)
  2175. return id
  2176. # -----------------------------------------------------------------------------
  2177. # operations
  2178. # -----------------------------------------------------------------------------
  2179. def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2180. """Actually inserts an item in the tree."""
  2181. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2182. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2183. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2184. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2185. if ct_type < 0 or ct_type > 2:
  2186. raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
  2187. parent = parentId
  2188. if not parent:
  2189. # should we give a warning here?
  2190. return self.AddRoot(text, ct_type, wnd, image, selImage, data)
  2191. self._dirty = True # do this first so stuff below doesn't cause flicker
  2192. item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data)
  2193. if wnd is not None:
  2194. self._hasWindows = True
  2195. self._itemWithWindow.append(item)
  2196. parent.Insert(item, previous)
  2197. return item
  2198. def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2199. """Adds a root to the CustomTreeCtrl. Only one root must exist."""
  2200. if self._anchor:
  2201. raise Exception("\nERROR: Tree Can Have Only One Root")
  2202. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2203. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2204. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2205. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2206. if ct_type < 0 or ct_type > 2:
  2207. raise Exception("\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). ")
  2208. self._dirty = True # do this first so stuff below doesn't cause flicker
  2209. self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data)
  2210. if wnd is not None:
  2211. self._hasWindows = True
  2212. self._itemWithWindow.append(self._anchor)
  2213. if self.HasFlag(TR_HIDE_ROOT):
  2214. # if root is hidden, make sure we can navigate
  2215. # into children
  2216. self._anchor.SetHasPlus()
  2217. self._anchor.Expand()
  2218. self.CalculatePositions()
  2219. if not self.HasFlag(TR_MULTIPLE):
  2220. self._current = self._key_current = self._anchor
  2221. self._current.SetHilight(True)
  2222. return self._anchor
  2223. def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2224. """Appends an item as a first child of parent."""
  2225. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2226. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2227. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2228. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2229. return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data)
  2230. def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2231. """Auxiliary function to cope with the C++ hideous multifunction."""
  2232. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2233. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2234. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2235. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2236. parent = parentId
  2237. if not parent:
  2238. # should we give a warning here?
  2239. return self.AddRoot(text, ct_type, wnd, image, selImage, data)
  2240. index = -1
  2241. if idPrevious:
  2242. try:
  2243. index = parent.GetChildren().index(idPrevious)
  2244. except:
  2245. raise Exception("ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling")
  2246. return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data)
  2247. def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2248. """Auxiliary function to cope with the C++ hideous multifunction."""
  2249. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2250. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2251. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2252. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2253. parent = parentId
  2254. if not parent:
  2255. # should we give a warning here?
  2256. return self.AddRoot(text, ct_type, wnd, image, selImage, data)
  2257. return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data)
  2258. def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2259. """Inserts an item after the given previous."""
  2260. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2261. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2262. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2263. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2264. if type(input) == type(1):
  2265. return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data)
  2266. else:
  2267. return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data)
  2268. def InsertItemBefore(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2269. return self.InsertItemByIndex(parentId, before, text, ct_type, wnd, image, selImage, data)
  2270. def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
  2271. """Appends an item as a last child of its parent."""
  2272. if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2273. raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2274. if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
  2275. raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
  2276. parent = parentId
  2277. if not parent:
  2278. # should we give a warning here?
  2279. return self.AddRoot(text, ct_type, wnd, image, selImage, data)
  2280. return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data)
  2281. def SendDeleteEvent(self, item):
  2282. """Actully sends the EVT_TREE_DELETE_ITEM event."""
  2283. event = TreeEvent(wxEVT_TREE_DELETE_ITEM, self.GetId())
  2284. event._item = item
  2285. event.SetEventObject(self)
  2286. self.GetEventHandler().ProcessEvent(event)
  2287. def IsDescendantOf(self, parent, item):
  2288. """Checks if the given item is under another one."""
  2289. while item:
  2290. if item == parent:
  2291. # item is a descendant of parent
  2292. return True
  2293. item = item.GetParent()
  2294. return False
  2295. # Don't leave edit or selection on a child which is about to disappear
  2296. def ChildrenClosing(self, item):
  2297. """We are about to destroy the item children."""
  2298. if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()):
  2299. self._textCtrl.StopEditing()
  2300. if item != self._key_current and self.IsDescendantOf(item, self._key_current):
  2301. self._key_current = None
  2302. if self.IsDescendantOf(item, self._select_me):
  2303. self._select_me = item
  2304. if item != self._current and self.IsDescendantOf(item, self._current):
  2305. self._current.SetHilight(False)
  2306. self._current = None
  2307. self._select_me = item
  2308. def DeleteChildren(self, item):
  2309. """Delete item children."""
  2310. if not item:
  2311. raise Exception("\nERROR: Invalid Tree Item. ")
  2312. self._dirty = True # do this first so stuff below doesn't cause flicker
  2313. self.ChildrenClosing(item)
  2314. item.DeleteChildren(self)
  2315. def Delete(self, item):
  2316. """Delete an item."""
  2317. if not item:
  2318. raise Exception("\nERROR: Invalid Tree Item. ")
  2319. self._dirty = True # do this first so stuff below doesn't cause flicker
  2320. if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()):
  2321. # can't delete the item being edited, cancel editing it first
  2322. self._textCtrl.StopEditing()
  2323. parent = item.GetParent()
  2324. # don't keep stale pointers around!
  2325. if self.IsDescendantOf(item, self._key_current):
  2326. # Don't silently change the selection:
  2327. # do it properly in idle time, so event
  2328. # handlers get called.
  2329. # self._key_current = parent
  2330. self._key_current = None
  2331. # self._select_me records whether we need to select
  2332. # a different item, in idle time.
  2333. if self._select_me and self.IsDescendantOf(item, self._select_me):
  2334. self._select_me = parent
  2335. if self.IsDescendantOf(item, self._current):
  2336. # Don't silently change the selection:
  2337. # do it properly in idle time, so event
  2338. # handlers get called.
  2339. # self._current = parent
  2340. self._current = None
  2341. self._select_me = parent
  2342. # remove the item from the tree
  2343. if parent:
  2344. parent.GetChildren().remove(item) # remove by value # Can throw ValueError, catch?
  2345. else: # deleting the root
  2346. # nothing will be left in the tree
  2347. self._anchor = None
  2348. # and delete all of its children and the item itself now
  2349. item.DeleteChildren(self)
  2350. self.SendDeleteEvent(item)
  2351. if item == self._select_me:
  2352. self._select_me = None
  2353. # Remove the item with window
  2354. if item in self._itemWithWindow:
  2355. wnd = item.GetWindow()
  2356. wnd.Hide()
  2357. wnd.Destroy()
  2358. item._wnd = None
  2359. self._itemWithWindow.remove(item)
  2360. del item
  2361. def DeleteAllItems(self):
  2362. """Delete all items in the CustomTreeCtrl."""
  2363. if self._anchor:
  2364. self.Delete(self._anchor)
  2365. def Expand(self, item):
  2366. """
  2367. Expands an item, sending a EVT_TREE_ITEM_EXPANDING and
  2368. EVT_TREE_ITEM_EXPANDED events.
  2369. """
  2370. if not item:
  2371. raise Exception("\nERROR: Invalid Tree Item. ")
  2372. if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
  2373. raise Exception("\nERROR: Can't Expand An Hidden Root. ")
  2374. if not item.HasPlus():
  2375. return
  2376. if item.IsExpanded():
  2377. return
  2378. event = TreeEvent(wxEVT_TREE_ITEM_EXPANDING, self.GetId())
  2379. event._item = item
  2380. event.SetEventObject(self)
  2381. if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed():
  2382. # cancelled by program
  2383. return
  2384. item.Expand()
  2385. self.CalculatePositions()
  2386. self.RefreshSubtree(item)
  2387. self.AdjustMyScrollbars()
  2388. clientHeight = self.GetClientSize()[1] - 15
  2389. children = item.GetChildren()
  2390. # Get total height of all children of the item in pixels
  2391. if len(children) == 0:
  2392. totalHeight = 0
  2393. childrenHeight = 0
  2394. else:
  2395. childrenHeight = children[-1].GetY() + self.GetLineHeight(children[-1])
  2396. childrenHeight -= children[0].GetY()
  2397. rect = self.GetBoundingRect(item)
  2398. if childrenHeight > clientHeight:
  2399. # Childrens have in sum a larger height than the tree ctrl
  2400. # -> scroll parent item to top
  2401. scrollYBy = rect.GetTop()
  2402. if scrollYBy > 0:
  2403. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2404. # Round down so parent is definitely visible
  2405. y_pos = self.GetScrollPos(wx.VERTICAL) + \
  2406. scrollYBy // _PIXELS_PER_UNIT
  2407. self.Scroll(x_pos, y_pos)
  2408. else:
  2409. # Childrens are not as high as the tree ctrl
  2410. # -> scroll so that all children are visible
  2411. scrollYBy = rect.GetTop() + rect.GetHeight() + childrenHeight - \
  2412. clientHeight
  2413. if scrollYBy > 0:
  2414. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2415. # Round up so last child is definitely visible
  2416. y_pos = self.GetScrollPos(wx.VERTICAL) + \
  2417. (scrollYBy + _PIXELS_PER_UNIT) // _PIXELS_PER_UNIT
  2418. self.Scroll(x_pos, y_pos)
  2419. if self._hasWindows:
  2420. # We hide the associated window here, we may show it after
  2421. self.HideWindows()
  2422. event.SetEventType(wxEVT_TREE_ITEM_EXPANDED)
  2423. self.GetEventHandler().ProcessEvent(event)
  2424. def ExpandAllChildren(self, item):
  2425. """Expands all the items children of the input item."""
  2426. if not item:
  2427. raise Exception("\nERROR: Invalid Tree Item. ")
  2428. if not self.HasFlag(TR_HIDE_ROOT) or item != self.GetRootItem():
  2429. self.Expand(item)
  2430. if not self.IsExpanded(item):
  2431. return
  2432. child, cookie = self.GetFirstChild(item)
  2433. while child:
  2434. self.ExpandAllChildren(child)
  2435. child, cookie = self.GetNextChild(item, cookie)
  2436. def ExpandAll(self):
  2437. """Expands all CustomTreeCtrl items."""
  2438. if self._anchor:
  2439. self.ExpandAllChildren(self._anchor)
  2440. def Collapse(self, item):
  2441. """
  2442. Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and
  2443. EVT_TREE_ITEM_COLLAPSED events.
  2444. """
  2445. if not item:
  2446. raise Exception("\nERROR: Invalid Tree Item. ")
  2447. if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem():
  2448. raise Exception("\nERROR: Can't Collapse An Hidden Root. ")
  2449. if not item.IsExpanded():
  2450. return
  2451. event = TreeEvent(wxEVT_TREE_ITEM_COLLAPSING, self.GetId())
  2452. event._item = item
  2453. event.SetEventObject(self)
  2454. if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed():
  2455. # cancelled by program
  2456. return
  2457. self.ChildrenClosing(item)
  2458. item.Collapse()
  2459. self.CalculatePositions()
  2460. self.RefreshSubtree(item)
  2461. if self._hasWindows:
  2462. self.HideWindows()
  2463. event.SetEventType(wxEVT_TREE_ITEM_COLLAPSED)
  2464. self.GetEventHandler().ProcessEvent(event)
  2465. def CollapseAndReset(self, item):
  2466. """Collapse the given item and deletes its children."""
  2467. self.Collapse(item)
  2468. self.DeleteChildren(item)
  2469. def Toggle(self, item):
  2470. """Toggles the item state (collapsed/expanded)."""
  2471. if item.IsExpanded():
  2472. self.Collapse(item)
  2473. else:
  2474. self.Expand(item)
  2475. def HideWindows(self):
  2476. """Hides the windows associated to the items. Used internally."""
  2477. for child in self._itemWithWindow:
  2478. if not self.IsVisible(child):
  2479. wnd = child.GetWindow()
  2480. wnd.Hide()
  2481. def Unselect(self):
  2482. """Unselects the current selection."""
  2483. if self._current:
  2484. self._current.SetHilight(False)
  2485. self.RefreshLine(self._current)
  2486. self._current = None
  2487. self._select_me = None
  2488. def UnselectAllChildren(self, item):
  2489. """Unselects all the children of the given item."""
  2490. if item.IsSelected():
  2491. item.SetHilight(False)
  2492. self.RefreshLine(item)
  2493. if item.HasChildren():
  2494. for child in item.GetChildren():
  2495. self.UnselectAllChildren(child)
  2496. def UnselectAll(self):
  2497. """Unselect all the items."""
  2498. rootItem = self.GetRootItem()
  2499. # the tree might not have the root item at all
  2500. if rootItem:
  2501. self.UnselectAllChildren(rootItem)
  2502. self.Unselect()
  2503. # Recursive function !
  2504. # To stop we must have crt_item<last_item
  2505. # Algorithm :
  2506. # Tag all next children, when no more children,
  2507. # Move to parent (not to tag)
  2508. # Keep going... if we found last_item, we stop.
  2509. def TagNextChildren(self, crt_item, last_item, select):
  2510. """Used internally."""
  2511. parent = crt_item.GetParent()
  2512. if parent == None: # This is root item
  2513. return self.TagAllChildrenUntilLast(crt_item, last_item, select)
  2514. children = parent.GetChildren()
  2515. index = children.index(crt_item)
  2516. count = len(children)
  2517. for n in xrange(index+1, count):
  2518. if self.TagAllChildrenUntilLast(children[n], last_item, select):
  2519. return True
  2520. return self.TagNextChildren(parent, last_item, select)
  2521. def TagAllChildrenUntilLast(self, crt_item, last_item, select):
  2522. """Used internally."""
  2523. crt_item.SetHilight(select)
  2524. self.RefreshLine(crt_item)
  2525. if crt_item == last_item:
  2526. return True
  2527. if crt_item.HasChildren():
  2528. for child in crt_item.GetChildren():
  2529. if self.TagAllChildrenUntilLast(child, last_item, select):
  2530. return True
  2531. return False
  2532. def SelectItemRange(self, item1, item2):
  2533. """Selects all the items between item1 and item2."""
  2534. self._select_me = None
  2535. # item2 is not necessary after item1
  2536. # choice first' and 'last' between item1 and item2
  2537. first = (item1.GetY() < item2.GetY() and [item1] or [item2])[0]
  2538. last = (item1.GetY() < item2.GetY() and [item2] or [item1])[0]
  2539. select = self._current.IsSelected()
  2540. if self.TagAllChildrenUntilLast(first, last, select):
  2541. return
  2542. self.TagNextChildren(first, last, select)
  2543. def DoSelectItem(self, item, unselect_others=True, extended_select=False,
  2544. expand_if_necessary=True, send_events=True):
  2545. """Actually selects/unselects an item, sending a EVT_TREE_SEL_CHANGED event."""
  2546. if not item:
  2547. raise Exception("\nERROR: Invalid Tree Item. ")
  2548. self._select_me = None
  2549. is_single = not (self.GetTreeStyle() & TR_MULTIPLE)
  2550. # to keep going anyhow !!!
  2551. if is_single:
  2552. if item.IsSelected():
  2553. return # nothing to do
  2554. unselect_others = True
  2555. extended_select = False
  2556. elif unselect_others and item.IsSelected():
  2557. # selection change if there is more than one item currently selected
  2558. if len(self.GetSelections()) == 1:
  2559. return
  2560. if send_events:
  2561. event = TreeEvent(wxEVT_TREE_SEL_CHANGING, self.GetId())
  2562. event._item = item
  2563. event._itemOld = self._current
  2564. event.SetEventObject(self)
  2565. # TODO : Here we don't send any selection mode yet !
  2566. if self.GetEventHandler().ProcessEvent(event) and not event.IsAllowed():
  2567. return
  2568. parent = self.GetItemParent(item)
  2569. while parent:
  2570. if not self.IsExpanded(parent):
  2571. if expand_if_necessary:
  2572. self.Expand(parent)
  2573. else:
  2574. return # TODO Better reaction?
  2575. parent = self.GetItemParent(parent)
  2576. # ctrl press
  2577. if unselect_others:
  2578. if is_single:
  2579. self.Unselect() # to speed up thing
  2580. else:
  2581. self.UnselectAll()
  2582. # shift press
  2583. if extended_select:
  2584. if not self._current:
  2585. self._current = self._key_current = self.GetRootItem()
  2586. # don't change the mark (self._current)
  2587. self.SelectItemRange(self._current, item)
  2588. else:
  2589. select = True # the default
  2590. # Check if we need to toggle hilight (ctrl mode)
  2591. if not unselect_others:
  2592. select = not item.IsSelected()
  2593. self._current = self._key_current = item
  2594. self._current.SetHilight(select)
  2595. self.RefreshLine(self._current)
  2596. # This can cause idle processing to select the root
  2597. # if no item is selected, so it must be after the
  2598. # selection is set
  2599. self.EnsureVisible(item)
  2600. if send_events:
  2601. event.SetEventType(wxEVT_TREE_SEL_CHANGED)
  2602. self.GetEventHandler().ProcessEvent(event)
  2603. # Handles hypertext items
  2604. if self.IsItemHyperText(item):
  2605. event = TreeEvent(wxEVT_TREE_ITEM_HYPERLINK, self.GetId())
  2606. event._item = item
  2607. self.GetEventHandler().ProcessEvent(event)
  2608. def SelectItem(self, item, select=True, expand_if_necessary=True,
  2609. send_events=True):
  2610. """Selects/deselects an item."""
  2611. if not item:
  2612. raise Exception("\nERROR: Invalid Tree Item. ")
  2613. if select:
  2614. self.DoSelectItem(item, not self.HasFlag(TR_MULTIPLE),
  2615. expand_if_necessary=expand_if_necessary,
  2616. send_events=send_events)
  2617. else: # deselect
  2618. item.SetHilight(False)
  2619. self.RefreshLine(item)
  2620. def FillArray(self, item, array=[]):
  2621. """
  2622. Internal function. Used to populate an array of selected items when
  2623. the style TR_MULTIPLE is used.
  2624. """
  2625. if not array:
  2626. array = []
  2627. if item.IsSelected():
  2628. array.append(item)
  2629. if item.HasChildren() and item.IsExpanded():
  2630. for child in item.GetChildren():
  2631. array = self.FillArray(child, array)
  2632. return array
  2633. def GetSelections(self):
  2634. """
  2635. Returns a list of selected items. This can be used only if CustomTreeCtrl has
  2636. the TR_MULTIPLE style set.
  2637. """
  2638. array = []
  2639. idRoot = self.GetRootItem()
  2640. if idRoot:
  2641. array = self.FillArray(idRoot, array)
  2642. #else: the tree is empty, so no selections
  2643. return array
  2644. def SetDefaultScrollVisiblePos(self, dpos):
  2645. self._defaultScrollVisiblePos = dpos
  2646. def GetDefaultScrollVisiblePos(self):
  2647. return self._defaultScrollVisiblePos
  2648. def EnsureVisible(self, item, toMiddle=None):
  2649. """Ensure that an item is visible in CustomTreeCtrl."""
  2650. if not item:
  2651. raise Exception("\nERROR: Invalid Tree Item. ")
  2652. # first expand all parent branches
  2653. parent = item.GetParent()
  2654. if self.HasFlag(TR_HIDE_ROOT):
  2655. while parent and parent != self._anchor:
  2656. self.Expand(parent)
  2657. parent = parent.GetParent()
  2658. else:
  2659. while parent:
  2660. self.Expand(parent)
  2661. parent = parent.GetParent()
  2662. if toMiddle is None:
  2663. toMiddle = self._defaultScrollVisiblePos == "middle"
  2664. if toMiddle:
  2665. self.ScrollToMiddle(item)
  2666. else:
  2667. self.ScrollTo(item)
  2668. def ScrollTo(self, item):
  2669. """Scrolls the specified item into view."""
  2670. if not item:
  2671. return
  2672. # We have to call this here because the label in
  2673. # question might just have been added and no screen
  2674. # update taken place.
  2675. if self._dirty:
  2676. if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
  2677. self.Update()
  2678. else:
  2679. wx.YieldIfNeeded()
  2680. # now scroll to the item
  2681. item_y = item.GetY()
  2682. start_x, start_y = self.GetViewStart()
  2683. start_y *= _PIXELS_PER_UNIT
  2684. client_w, client_h = self.GetClientSize()
  2685. x, y = 0, 0
  2686. if item_y < start_y+3:
  2687. # going down
  2688. x, y = self._anchor.GetSize(x, y, self)
  2689. y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2690. x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2691. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2692. # Item should appear at top
  2693. self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, item_y/_PIXELS_PER_UNIT)
  2694. elif item_y+self.GetLineHeight(item) > start_y+client_h:
  2695. # going up
  2696. x, y = self._anchor.GetSize(x, y, self)
  2697. y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2698. x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2699. item_y += _PIXELS_PER_UNIT+2
  2700. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2701. # Item should appear at bottom
  2702. 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 )
  2703. def ScrollToMiddle(self, item):
  2704. """Scrolls the specified item into the vertical middle of the view."""
  2705. if not item:
  2706. return
  2707. # We have to call this here because the label in
  2708. # question might just have been added and no screen
  2709. # update taken place.
  2710. if self._dirty:
  2711. if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
  2712. self.Update()
  2713. else:
  2714. wx.YieldIfNeeded()
  2715. # now scroll to the item
  2716. item_y = item.GetY()
  2717. start_x, start_y = self.GetViewStart()
  2718. start_y *= _PIXELS_PER_UNIT
  2719. client_w, client_h = self.GetClientSize()
  2720. target_y = item_y - (client_h - self.GetLineHeight(item))// 2
  2721. target_y = max(0, target_y)
  2722. x, y = 0, 0
  2723. x, y = self._anchor.GetSize(x, y, self)
  2724. y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2725. x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2726. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2727. self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, target_y//_PIXELS_PER_UNIT)
  2728. def OnCompareItems(self, item1, item2):
  2729. """
  2730. Returns whether 2 items have the same text.
  2731. Override this function in the derived class to change the sort order of the items
  2732. in the CustomTreeCtrl. The function should return a negative, zero or positive
  2733. value if the first item is less than, equal to or greater than the second one.
  2734. The base class version compares items alphabetically.
  2735. """
  2736. return self.GetItemText(item1) == self.GetItemText(item2)
  2737. def SortChildren(self, item):
  2738. """
  2739. Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl.
  2740. You should override that method to change the sort order (the default is ascending
  2741. case-sensitive alphabetical order).
  2742. """
  2743. if not item:
  2744. raise Exception("\nERROR: Invalid Tree Item. ")
  2745. children = item.GetChildren()
  2746. if len(children) > 1:
  2747. self._dirty = True
  2748. children.sort(self.OnCompareItems)
  2749. def GetImageList(self):
  2750. """Returns the normal image list."""
  2751. return self._imageListNormal
  2752. def GetButtonsImageList(self):
  2753. """Returns the buttons image list (from which application-defined button images are taken)."""
  2754. return self._imageListButtons
  2755. def GetStateImageList(self):
  2756. """Returns the state image list (from which application-defined state images are taken)."""
  2757. return self._imageListState
  2758. def GetImageListCheck(self):
  2759. """Returns the image list used to build the check/radio buttons."""
  2760. return self._imageListCheck
  2761. def CalculateLineHeight(self):
  2762. """Calculates the height of a line."""
  2763. dc = wx.ClientDC(self)
  2764. self._lineHeight = dc.GetCharHeight()
  2765. if self._imageListNormal:
  2766. # Calculate a self._lineHeight value from the normal Image sizes.
  2767. # May be toggle off. Then CustomTreeCtrl will spread when
  2768. # necessary (which might look ugly).
  2769. n = self._imageListNormal.GetImageCount()
  2770. for i in xrange(n):
  2771. width, height = self._imageListNormal.GetSize(i)
  2772. if height > self._lineHeight:
  2773. self._lineHeight = height
  2774. if self._imageListButtons:
  2775. # Calculate a self._lineHeight value from the Button image sizes.
  2776. # May be toggle off. Then CustomTreeCtrl will spread when
  2777. # necessary (which might look ugly).
  2778. n = self._imageListButtons.GetImageCount()
  2779. for i in xrange(n):
  2780. width, height = self._imageListButtons.GetSize(i)
  2781. if height > self._lineHeight:
  2782. self._lineHeight = height
  2783. if self._imageListCheck:
  2784. # Calculate a self._lineHeight value from the check/radio image sizes.
  2785. # May be toggle off. Then CustomTreeCtrl will spread when
  2786. # necessary (which might look ugly).
  2787. n = self._imageListCheck.GetImageCount()
  2788. for i in xrange(n):
  2789. width, height = self._imageListCheck.GetSize(i)
  2790. if height > self._lineHeight:
  2791. self._lineHeight = height
  2792. # if self._lineHeight < 30:
  2793. # self._lineHeight += 2 # at least 2 pixels
  2794. # else:
  2795. # self._lineHeight += self._lineHeight/10 # otherwise 10% extra spacing
  2796. def SetImageList(self, imageList):
  2797. """Sets the normal image list."""
  2798. if self._ownsImageListNormal:
  2799. del self._imageListNormal
  2800. self._imageListNormal = imageList
  2801. self._ownsImageListNormal = False
  2802. self._dirty = True
  2803. # Don't do any drawing if we're setting the list to NULL,
  2804. # since we may be in the process of deleting the tree control.
  2805. if imageList:
  2806. self.CalculateLineHeight()
  2807. # We gray out the image list to use the grayed icons with disabled items
  2808. sz = imageList.GetSize(0)
  2809. self._grayedImageList = wx.ImageList(sz[0], sz[1], True, 0)
  2810. for ii in xrange(imageList.GetImageCount()):
  2811. bmp = imageList.GetBitmap(ii)
  2812. image = wx.ImageFromBitmap(bmp)
  2813. image = GrayOut(image)
  2814. newbmp = wx.BitmapFromImage(image)
  2815. self._grayedImageList.Add(newbmp)
  2816. def SetImageListNoGrayedItems(self, imageList):
  2817. """
  2818. Sets the normal image list, but not the grayed image list
  2819. """
  2820. if self._ownsImageListNormal:
  2821. del self._imageListNormal
  2822. self._imageListNormal = imageList
  2823. self._ownsImageListNormal = False
  2824. self._dirty = True
  2825. # Don't do any drawing if we're setting the list to NULL,
  2826. # since we may be in the process of deleting the tree control.
  2827. if imageList:
  2828. self.CalculateLineHeight()
  2829. def SetStateImageList(self, imageList):
  2830. """Sets the state image list (from which application-defined state images are taken)."""
  2831. if self._ownsImageListState:
  2832. del self._imageListState
  2833. self._imageListState = imageList
  2834. self._ownsImageListState = False
  2835. def SetButtonsImageList(self, imageList):
  2836. """Sets the buttons image list (from which application-defined button images are taken)."""
  2837. if self._ownsImageListButtons:
  2838. del self._imageListButtons
  2839. self._imageListButtons = imageList
  2840. self._ownsImageListButtons = False
  2841. self._dirty = True
  2842. self.CalculateLineHeight()
  2843. def SetImageListCheck(self, sizex, sizey, imglist=None):
  2844. """Sets the check image list."""
  2845. if imglist is None:
  2846. self._imageListCheck = wx.ImageList(sizex, sizey)
  2847. self._imageListCheck.Add(GetCheckedBitmap())
  2848. self._imageListCheck.Add(GetNotCheckedBitmap())
  2849. self._imageListCheck.Add(GetFlaggedBitmap())
  2850. self._imageListCheck.Add(GetNotFlaggedBitmap())
  2851. else:
  2852. sizex, sizey = imglist.GetSize(0)
  2853. self._imageListCheck = imglist
  2854. # We gray out the image list to use the grayed icons with disabled items
  2855. self._grayedCheckList = wx.ImageList(sizex, sizey, True, 0)
  2856. for ii in xrange(self._imageListCheck.GetImageCount()):
  2857. bmp = self._imageListCheck.GetBitmap(ii)
  2858. image = wx.ImageFromBitmap(bmp)
  2859. image = GrayOut(image)
  2860. newbmp = wx.BitmapFromImage(image)
  2861. self._grayedCheckList.Add(newbmp)
  2862. self._dirty = True
  2863. if imglist:
  2864. self.CalculateLineHeight()
  2865. def AssignImageList(self, imageList):
  2866. """Assigns the normal image list."""
  2867. self.SetImageList(imageList)
  2868. self._ownsImageListNormal = True
  2869. def AssignStateImageList(self, imageList):
  2870. """Assigns the state image list."""
  2871. self.SetStateImageList(imageList)
  2872. self._ownsImageListState = True
  2873. def AssignButtonsImageList(self, imageList):
  2874. """Assigns the button image list."""
  2875. self.SetButtonsImageList(imageList)
  2876. self._ownsImageListButtons = True
  2877. # -----------------------------------------------------------------------------
  2878. # helpers
  2879. # -----------------------------------------------------------------------------
  2880. def AdjustMyScrollbars(self):
  2881. """Adjust the wx.ScrolledWindow scrollbars."""
  2882. if self._anchor:
  2883. x, y = self._anchor.GetSize(0, 0, self)
  2884. y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2885. x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels
  2886. x_pos = self.GetScrollPos(wx.HORIZONTAL)
  2887. y_pos = self.GetScrollPos(wx.VERTICAL)
  2888. self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, y_pos)
  2889. else:
  2890. self.SetScrollbars(0, 0, 0, 0)
  2891. def GetLineHeight(self, item):
  2892. """Returns the line height for the given item."""
  2893. if self.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT:
  2894. return item.GetHeight()
  2895. else:
  2896. return self._lineHeight
  2897. def DrawVerticalGradient(self, dc, rect, hasfocus):
  2898. """Gradient fill from colour 1 to colour 2 from top to bottom."""
  2899. oldpen = dc.GetPen()
  2900. oldbrush = dc.GetBrush()
  2901. dc.SetPen(wx.TRANSPARENT_PEN)
  2902. # calculate gradient coefficients
  2903. if hasfocus:
  2904. col2 = self._secondcolour
  2905. col1 = self._firstcolour
  2906. else:
  2907. col2 = self._hilightUnfocusedBrush.GetColour()
  2908. col1 = self._hilightUnfocusedBrush2.GetColour()
  2909. r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
  2910. r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
  2911. flrect = float(rect.height)
  2912. rstep = float((r2 - r1)) / flrect
  2913. gstep = float((g2 - g1)) / flrect
  2914. bstep = float((b2 - b1)) / flrect
  2915. rf, gf, bf = 0, 0, 0
  2916. for y in xrange(rect.y, rect.y + rect.height):
  2917. currCol = (r1 + rf, g1 + gf, b1 + bf)
  2918. dc.SetBrush(wx.Brush(currCol, wx.SOLID))
  2919. dc.DrawRectangle(rect.x, y, rect.width, 1)
  2920. rf = rf + rstep
  2921. gf = gf + gstep
  2922. bf = bf + bstep
  2923. dc.SetPen(oldpen)
  2924. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  2925. dc.DrawRectangleRect(rect)
  2926. dc.SetBrush(oldbrush)
  2927. def DrawHorizontalGradient(self, dc, rect, hasfocus):
  2928. """Gradient fill from colour 1 to colour 2 from left to right."""
  2929. oldpen = dc.GetPen()
  2930. oldbrush = dc.GetBrush()
  2931. dc.SetPen(wx.TRANSPARENT_PEN)
  2932. # calculate gradient coefficients
  2933. if hasfocus:
  2934. col2 = self._secondcolour
  2935. col1 = self._firstcolour
  2936. else:
  2937. col2 = self._hilightUnfocusedBrush.GetColour()
  2938. col1 = self._hilightUnfocusedBrush2.GetColour()
  2939. r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue())
  2940. r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue())
  2941. flrect = float(rect.width)
  2942. rstep = float((r2 - r1)) / flrect
  2943. gstep = float((g2 - g1)) / flrect
  2944. bstep = float((b2 - b1)) / flrect
  2945. rf, gf, bf = 0, 0, 0
  2946. for x in xrange(rect.x, rect.x + rect.width):
  2947. currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf))
  2948. dc.SetBrush(wx.Brush(currCol, wx.SOLID))
  2949. dc.DrawRectangle(x, rect.y, 1, rect.height)
  2950. rf = rf + rstep
  2951. gf = gf + gstep
  2952. bf = bf + bstep
  2953. dc.SetPen(oldpen)
  2954. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  2955. dc.DrawRectangleRect(rect)
  2956. dc.SetBrush(oldbrush)
  2957. def DrawVistaRectangle(self, dc, rect, hasfocus):
  2958. """Draw the selected item(s) with the Windows Vista style."""
  2959. if hasfocus:
  2960. outer = _rgbSelectOuter
  2961. inner = _rgbSelectInner
  2962. top = _rgbSelectTop
  2963. bottom = _rgbSelectBottom
  2964. else:
  2965. outer = _rgbNoFocusOuter
  2966. inner = _rgbNoFocusInner
  2967. top = _rgbNoFocusTop
  2968. bottom = _rgbNoFocusBottom
  2969. oldpen = dc.GetPen()
  2970. oldbrush = dc.GetBrush()
  2971. bdrRect = wx.Rect(*rect.Get())
  2972. filRect = wx.Rect(*rect.Get())
  2973. filRect.Deflate(1,1)
  2974. r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue())
  2975. r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue())
  2976. flrect = float(filRect.height)
  2977. if flrect < 1:
  2978. flrect = self._lineHeight
  2979. rstep = float((r2 - r1)) / flrect
  2980. gstep = float((g2 - g1)) / flrect
  2981. bstep = float((b2 - b1)) / flrect
  2982. rf, gf, bf = 0, 0, 0
  2983. dc.SetPen(wx.TRANSPARENT_PEN)
  2984. for y in xrange(filRect.y, filRect.y + filRect.height):
  2985. currCol = (r1 + rf, g1 + gf, b1 + bf)
  2986. dc.SetBrush(wx.Brush(currCol, wx.SOLID))
  2987. dc.DrawRectangle(filRect.x, y, filRect.width, 1)
  2988. rf = rf + rstep
  2989. gf = gf + gstep
  2990. bf = bf + bstep
  2991. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  2992. dc.SetPen(wx.Pen(outer))
  2993. dc.DrawRoundedRectangleRect(bdrRect, 3)
  2994. bdrRect.Deflate(1, 1)
  2995. dc.SetPen(wx.Pen(inner))
  2996. dc.DrawRoundedRectangleRect(bdrRect, 2)
  2997. dc.SetPen(oldpen)
  2998. dc.SetBrush(oldbrush)
  2999. def PaintItem(self, item, dc):
  3000. """Actually paint an item."""
  3001. attr = item.GetAttributes()
  3002. if attr and attr.HasFont():
  3003. dc.SetFont(attr.GetFont())
  3004. elif item.IsBold():
  3005. dc.SetFont(self._boldFont)
  3006. if item.IsHyperText():
  3007. dc.SetFont(self.GetHyperTextFont())
  3008. if item.GetVisited():
  3009. dc.SetTextForeground(self.GetHyperTextVisitedColour())
  3010. else:
  3011. dc.SetTextForeground(self.GetHyperTextNewColour())
  3012. text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
  3013. image = item.GetCurrentImage()
  3014. checkimage = item.GetCurrentCheckedImage()
  3015. image_w, image_h = 0, 0
  3016. if image != _NO_IMAGE:
  3017. if self._imageListNormal:
  3018. image_w, image_h = self._imageListNormal.GetSize(image)
  3019. image_w += 4
  3020. else:
  3021. image = _NO_IMAGE
  3022. if item.GetType() != 0:
  3023. wcheck, hcheck = self._imageListCheck.GetSize(item.GetType())
  3024. wcheck += 4
  3025. else:
  3026. wcheck, hcheck = 0, 0
  3027. total_h = self.GetLineHeight(item)
  3028. drawItemBackground = False
  3029. if item.IsSelected():
  3030. # under mac selections are only a rectangle in case they don't have the focus
  3031. if wx.Platform == "__WXMAC__":
  3032. if not self._hasFocus:
  3033. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  3034. dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), 1, wx.SOLID))
  3035. else:
  3036. dc.SetBrush(self._hilightBrush)
  3037. else:
  3038. dc.SetBrush((self._hasFocus and [self._hilightBrush] or [self._hilightUnfocusedBrush])[0])
  3039. drawItemBackground = True
  3040. else:
  3041. if attr and attr.HasBackgroundColour():
  3042. drawItemBackground = True
  3043. colBg = attr.GetBackgroundColour()
  3044. else:
  3045. colBg = self._backgroundColour
  3046. dc.SetBrush(wx.Brush(colBg, wx.SOLID))
  3047. dc.SetPen(wx.TRANSPARENT_PEN)
  3048. offset = (self.HasFlag(TR_ROW_LINES) and [1] or [0])[0]
  3049. if self.HasFlag(TR_FULL_ROW_HIGHLIGHT):
  3050. x = 0
  3051. w, h = self.GetClientSize()
  3052. itemrect = wx.Rect(x, item.GetY()+offset, w, total_h-offset)
  3053. if item.IsSelected():
  3054. if self._usegradients:
  3055. if self._gradientstyle == 0: # Horizontal
  3056. self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
  3057. else: # Vertical
  3058. self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
  3059. elif self._vistaselection:
  3060. self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
  3061. else:
  3062. if wx.Platform in ["__WXGTK2__", "__WXMAC__"]:
  3063. flags = wx.CONTROL_SELECTED
  3064. if self._hasFocus: flags = flags | wx.CONTROL_FOCUSED
  3065. wx.RendererNative.Get().DrawItemSelectionRect(self, dc, itemrect, flags)
  3066. else:
  3067. dc.DrawRectangleRect(itemrect)
  3068. else:
  3069. if item.IsSelected():
  3070. # If it's selected, and there's an image, then we should
  3071. # take care to leave the area under the image painted in the
  3072. # background colour.
  3073. wnd = item.GetWindow()
  3074. wndx = 0
  3075. if wnd:
  3076. wndx, wndy = item.GetWindowSize()
  3077. itemrect = wx.Rect(item.GetX() + wcheck + image_w - 2,
  3078. item.GetY()+offset,
  3079. item.GetWidth() - image_w - wcheck + 2 - wndx,
  3080. total_h-offset)
  3081. if self._usegradients:
  3082. if self._gradientstyle == 0: # Horizontal
  3083. self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
  3084. else: # Vertical
  3085. self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
  3086. elif self._vistaselection:
  3087. self.DrawVistaRectangle(dc, itemrect, self._hasFocus)
  3088. else:
  3089. if wx.Platform in ["__WXGTK2__", "__WXMAC__"]:
  3090. flags = wx.CONTROL_SELECTED
  3091. if self._hasFocus: flags = flags | wx.CONTROL_FOCUSED
  3092. wx.RendererNative.Get().DrawItemSelectionRect(self, dc, itemrect, flags)
  3093. else:
  3094. dc.DrawRectangleRect(itemrect)
  3095. # On GTK+ 2, drawing a 'normal' background is wrong for themes that
  3096. # don't allow backgrounds to be customized. Not drawing the background,
  3097. # except for custom item backgrounds, works for both kinds of theme.
  3098. elif drawItemBackground:
  3099. minusicon = wcheck + image_w - 2
  3100. itemrect = wx.Rect(item.GetX()+minusicon,
  3101. item.GetY()+offset,
  3102. item.GetWidth()-minusicon,
  3103. total_h-offset)
  3104. if self._usegradients and self._hasFocus:
  3105. if self._gradientstyle == 0: # Horizontal
  3106. self.DrawHorizontalGradient(dc, itemrect, self._hasFocus)
  3107. else: # Vertical
  3108. self.DrawVerticalGradient(dc, itemrect, self._hasFocus)
  3109. else:
  3110. dc.DrawRectangleRect(itemrect)
  3111. if image != _NO_IMAGE:
  3112. dc.SetClippingRegion(item.GetX(), item.GetY(), wcheck+image_w-2, total_h)
  3113. if item.IsEnabled():
  3114. imglist = self._imageListNormal
  3115. else:
  3116. imglist = self._grayedImageList
  3117. imglist.Draw(image, dc,
  3118. item.GetX() + wcheck,
  3119. item.GetY() + ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0],
  3120. wx.IMAGELIST_DRAW_TRANSPARENT)
  3121. dc.DestroyClippingRegion()
  3122. if wcheck:
  3123. if item.IsEnabled():
  3124. imglist = self._imageListCheck
  3125. else:
  3126. imglist = self._grayedCheckList
  3127. imglist.Draw(checkimage, dc,
  3128. item.GetX(),
  3129. item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0],
  3130. wx.IMAGELIST_DRAW_TRANSPARENT)
  3131. dc.SetBackgroundMode(wx.TRANSPARENT)
  3132. extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0]
  3133. textrect = wx.Rect(wcheck + image_w + item.GetX(), item.GetY() + extraH, text_w, text_h)
  3134. if not item.IsEnabled():
  3135. foreground = dc.GetTextForeground()
  3136. dc.SetTextForeground(self._disabledColour)
  3137. dc.DrawLabel(item.GetText(), textrect)
  3138. dc.SetTextForeground(foreground)
  3139. else:
  3140. if wx.Platform == "__WXMAC__" and item.IsSelected() and self._hasFocus:
  3141. dc.SetTextForeground(wx.WHITE)
  3142. dc.DrawLabel(item.GetText(), textrect)
  3143. wnd = item.GetWindow()
  3144. if wnd:
  3145. wndx = wcheck + image_w + item.GetX() + text_w + 4
  3146. xa, ya = self.CalcScrolledPosition((0, item.GetY()))
  3147. wndx += xa
  3148. if item.GetHeight() > item.GetWindowSize()[1]:
  3149. ya += (item.GetHeight() - item.GetWindowSize()[1])/2
  3150. if not wnd.IsShown():
  3151. wnd.Show()
  3152. if wnd.GetPosition() != (wndx, ya):
  3153. wnd.SetPosition((wndx, ya))
  3154. # restore normal font
  3155. dc.SetFont(self._normalFont)
  3156. # Now y stands for the top of the item, whereas it used to stand for middle !
  3157. def PaintLevel(self, item, dc, level, y):
  3158. y = self._RecurPaintLevel(item, dc, level, y)
  3159. self.PaintButtons(item, dc, level)
  3160. return y
  3161. # Now y stands for the top of the item, whereas it used to stand for middle !
  3162. def _RecurPaintLevel(self, item, dc, level, y):
  3163. """Paint a level of CustomTreeCtrl."""
  3164. x = level * self._indent
  3165. # print "PaintLevel1", repr(level)
  3166. if not self.HasFlag(TR_HIDE_ROOT):
  3167. x += self._indent
  3168. elif level == 0:
  3169. # always expand hidden root
  3170. origY = y
  3171. children = item.GetChildren()
  3172. count = len(children)
  3173. if count > 0:
  3174. n = 0
  3175. while n < count:
  3176. oldY = y
  3177. y = self._RecurPaintLevel(children[n], dc, 1, y)
  3178. n = n + 1
  3179. if not self.HasFlag(TR_NO_LINES) and self.HasFlag(TR_LINES_AT_ROOT) and count > 0:
  3180. # draw line down to last child
  3181. origY += self.GetLineHeight(children[0])>>1
  3182. oldY += self.GetLineHeight(children[n-1])>>1
  3183. oldPen = dc.GetPen()
  3184. dc.SetPen(self._dottedPen)
  3185. dc.DrawLine(3, origY, 3, oldY)
  3186. dc.SetPen(oldPen)
  3187. return y
  3188. item.SetX(x+self._spacing)
  3189. item.SetY(y)
  3190. h = self.GetLineHeight(item)
  3191. y_top = y
  3192. y_mid = y_top + (h>>1)
  3193. y += h
  3194. exposed_x = dc.LogicalToDeviceX(0)
  3195. exposed_y = dc.LogicalToDeviceY(y_top)
  3196. if self.IsExposed(exposed_x, exposed_y, 10000, h): # 10000 = very much
  3197. if wx.Platform == "__WXMAC__":
  3198. # don't draw rect outline if we already have the
  3199. # background color under Mac
  3200. pen = ((item.IsSelected() and self._hasFocus) and [self._borderPen] or [wx.TRANSPARENT_PEN])[0]
  3201. else:
  3202. pen = self._borderPen
  3203. if item.IsSelected():
  3204. if (wx.Platform == "__WXMAC__" and self._hasFocus):
  3205. colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
  3206. else:
  3207. colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
  3208. else:
  3209. attr = item.GetAttributes()
  3210. if attr and attr.HasTextColour():
  3211. colText = attr.GetTextColour()
  3212. else:
  3213. colText = self.GetForegroundColour()
  3214. if self._vistaselection:
  3215. colText = wx.BLACK
  3216. # prepare to draw
  3217. dc.SetTextForeground(colText)
  3218. dc.SetPen(pen)
  3219. oldpen = pen
  3220. # draw
  3221. self.PaintItem(item, dc)
  3222. if self.HasFlag(TR_ROW_LINES):
  3223. # if the background colour is white, choose a
  3224. # contrasting color for the lines
  3225. medium_grey = wx.Pen(wx.Colour(200, 200, 200))
  3226. dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0])
  3227. dc.DrawLine(0, y_top, 10000, y_top)
  3228. dc.DrawLine(0, y, 10000, y)
  3229. # restore DC objects
  3230. dc.SetBrush(wx.WHITE_BRUSH)
  3231. dc.SetTextForeground(wx.BLACK)
  3232. if not self.HasFlag(TR_NO_LINES):
  3233. # draw the horizontal line here
  3234. dc.SetPen(self._dottedPen)
  3235. x_start = x
  3236. if x > self._indent:
  3237. x_start -= self._indent
  3238. elif self.HasFlag(TR_LINES_AT_ROOT):
  3239. x_start = 3
  3240. dc.DrawLine(x_start, y_mid, x + self._spacing, y_mid)
  3241. dc.SetPen(oldpen)
  3242. origlevel = level
  3243. if item.IsExpanded():
  3244. children = item.GetChildren()
  3245. count = len(children)
  3246. if count > 0:
  3247. n = 0
  3248. level = level + 1
  3249. while n < count:
  3250. oldY = y
  3251. y = self._RecurPaintLevel(children[n], dc, level, y)
  3252. n = n + 1
  3253. if not self.HasFlag(TR_NO_LINES) and count > 0:
  3254. # draw line down to last child
  3255. oldY += self.GetLineHeight(children[n-1])>>1
  3256. if self.HasButtons():
  3257. y_mid += 5
  3258. # Only draw the portion of the line that is visible, in case it is huge
  3259. xOrigin, yOrigin = dc.GetDeviceOrigin()
  3260. yOrigin = abs(yOrigin)
  3261. width, height = self.GetClientSize()
  3262. # Move end points to the begining/end of the view?
  3263. if y_mid < yOrigin:
  3264. y_mid = yOrigin
  3265. if oldY > yOrigin + height:
  3266. oldY = yOrigin + height
  3267. # after the adjustments if y_mid is larger than oldY then the line
  3268. # isn't visible at all so don't draw anything
  3269. if y_mid < oldY:
  3270. dc.SetPen(self._dottedPen)
  3271. dc.DrawLine(x, y_mid, x, oldY)
  3272. for c in children:
  3273. self.PaintButtons(c, dc, level)
  3274. return y
  3275. def PaintButtons(self, item, dc, level):
  3276. x = level * self._indent
  3277. if not self.HasFlag(TR_HIDE_ROOT):
  3278. x += self._indent
  3279. h = self.GetLineHeight(item)
  3280. y_mid = item.GetY() + (h>>1)
  3281. if item.HasPlus() and self.HasButtons() and x > self._indent:
  3282. x_start = x
  3283. # if x > self._indent:
  3284. x_start -= self._indent
  3285. if self._imageListButtons:
  3286. # draw the image button here
  3287. image_h = 0
  3288. image_w = 0
  3289. image = (item.IsExpanded() and [TreeItemIcon_Expanded] or [TreeItemIcon_Normal])[0]
  3290. if item.IsSelected():
  3291. image += TreeItemIcon_Selected - TreeItemIcon_Normal
  3292. image_w, image_h = self._imageListButtons.GetSize(image)
  3293. # xx = x - image_w/2
  3294. xx = x_start - image_w/2
  3295. yy = y_mid - image_h/2
  3296. dc.SetClippingRegion(xx, yy, image_w, image_h)
  3297. self._imageListButtons.Draw(image, dc, xx, yy,
  3298. wx.IMAGELIST_DRAW_TRANSPARENT)
  3299. dc.DestroyClippingRegion()
  3300. else: # no custom buttons
  3301. if self._windowStyle & TR_TWIST_BUTTONS:
  3302. # We draw something like the Mac twist buttons
  3303. dc.SetPen(wx.BLACK_PEN)
  3304. dc.SetBrush(self._hilightBrush)
  3305. button = [wx.Point(), wx.Point(), wx.Point()]
  3306. if item.IsExpanded():
  3307. button[0].x = x_start - 5
  3308. button[0].y = y_mid - 3
  3309. button[1].x = x_start + 5
  3310. button[1].y = button[0].y
  3311. button[2].x = x_start
  3312. button[2].y = button[0].y + 6
  3313. else:
  3314. button[0].x = x_start - 3
  3315. button[0].y = y_mid - 5
  3316. button[1].x = button[0].x
  3317. button[1].y = y_mid + 5
  3318. button[2].x = button[0].x + 5
  3319. button[2].y = y_mid
  3320. dc.DrawPolygon(button)
  3321. else:
  3322. # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows
  3323. wImage = 9
  3324. hImage = 9
  3325. flag = 0
  3326. if item.IsExpanded():
  3327. flag |= _CONTROL_EXPANDED
  3328. if item == self._underMouse:
  3329. flag |= _CONTROL_CURRENT
  3330. self._drawingfunction(self, dc, wx.Rect(x_start - wImage/2, y_mid - hImage/2,wImage, hImage), flag)
  3331. # -----------------------------------------------------------------------------
  3332. # wxWidgets callbacks
  3333. # -----------------------------------------------------------------------------
  3334. def OnPaint(self, event):
  3335. """Handles the wx.EVT_PAINT event."""
  3336. # dc = wx.PaintDC(self)
  3337. dc = wx.BufferedPaintDC(self)
  3338. if self._backgroundColour == wx.NullColour:
  3339. bgBrush = wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
  3340. else:
  3341. bgBrush = wx.Brush(self._backgroundColour)
  3342. dc.SetBackground(bgBrush)
  3343. dc.Clear()
  3344. dc.SetBackground(wx.NullBrush)
  3345. self.PrepareDC(dc)
  3346. if not self._anchor:
  3347. return
  3348. dc.SetFont(self._normalFont)
  3349. dc.SetPen(self._dottedPen)
  3350. y = 2
  3351. dc.BeginDrawing()
  3352. self.PaintLevel(self._anchor, dc, 0, y)
  3353. dc.EndDrawing()
  3354. def OnEraseBackground(self, event):
  3355. """Handles the wx.EVT_ERASE_BACKGROUND event."""
  3356. # Can we actually do something here (or in OnPaint()) To Handle
  3357. # background images that are stretchable or always centered?
  3358. # I tried but I get enormous flickering...
  3359. if not self._backgroundImage:
  3360. # event.Skip()
  3361. return
  3362. if self._imageStretchStyle == _StyleTile:
  3363. dc = event.GetDC()
  3364. if not dc:
  3365. dc = wx.ClientDC(self)
  3366. rect = self.GetUpdateRegion().GetBox()
  3367. dc.SetClippingRect(rect)
  3368. self.TileBackground(dc)
  3369. def OnSysColourChanged(self, evt):
  3370. # self._backgroundColour = wx.SystemSettings.GetColour(
  3371. # wx.SYS_COLOUR_WINDOW)
  3372. self.Refresh()
  3373. def TileBackground(self, dc):
  3374. """Tiles the background image to fill all the available area."""
  3375. sz = self.GetClientSize()
  3376. w = self._backgroundImage.GetWidth()
  3377. h = self._backgroundImage.GetHeight()
  3378. x = 0
  3379. while x < sz.width:
  3380. y = 0
  3381. while y < sz.height:
  3382. dc.DrawBitmap(self._backgroundImage, x, y, True)
  3383. y = y + h
  3384. x = x + w
  3385. def OnSetFocus(self, event):
  3386. """Handles the wx.EVT_SET_FOCUS event."""
  3387. self._hasFocus = True
  3388. self.RefreshSelected()
  3389. event.Skip()
  3390. def OnKillFocus(self, event):
  3391. """Handles the wx.EVT_KILL_FOCUS event."""
  3392. self._hasFocus = False
  3393. self.RefreshSelected()
  3394. event.Skip()
  3395. def OnKeyDown(self, event):
  3396. """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event."""
  3397. te = TreeEvent(wxEVT_TREE_KEY_DOWN, self.GetId())
  3398. te._evtKey = event
  3399. te.SetEventObject(self)
  3400. if self.GetEventHandler().ProcessEvent(te):
  3401. # intercepted by the user code
  3402. return
  3403. if self._current is None or self._key_current is None:
  3404. if self._key_current is None:
  3405. event.Skip()
  3406. return
  3407. else: # MB: Not really knowing what I'm doing here
  3408. self._current = self._key_current
  3409. # how should the selection work for this event?
  3410. is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), event.ShiftDown(), event.CmdDown())
  3411. # + : Expand
  3412. # - : Collaspe
  3413. # * : Expand all/Collapse all
  3414. # ' ' | return : activate
  3415. # up : go up (not last children!)
  3416. # down : go down
  3417. # left : go to parent
  3418. # right : open if parent and go next
  3419. # home : go to root
  3420. # end : go to last item without opening parents
  3421. # alnum : start or continue searching for the item with this prefix
  3422. keyCode = event.GetKeyCode()
  3423. if keyCode in [ord("+"), wx.WXK_ADD, wx.WXK_NUMPAD_ADD]: # "+"
  3424. if self._current.HasPlus() and not self.IsExpanded(self._current) and self.IsItemEnabled(self._current):
  3425. self.Expand(self._current)
  3426. elif keyCode in [ord("*"), wx.WXK_MULTIPLY, wx.WXK_NUMPAD_MULTIPLY]: # "*"
  3427. if not self.IsExpanded(self._current) and self.IsItemEnabled(self._current):
  3428. # expand all
  3429. self.ExpandAll(self._current)
  3430. elif keyCode in [ord("-"), wx.WXK_SUBTRACT, wx.WXK_NUMPAD_SUBTRACT]: # "-"
  3431. if self.IsExpanded(self._current):
  3432. self.Collapse(self._current)
  3433. elif keyCode == wx.WXK_MENU:
  3434. # Use the item's bounding rectangle to determine position for the event
  3435. itemRect = self.GetBoundingRect(self._current, True)
  3436. event = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
  3437. event._item = self._current
  3438. # Use the left edge, vertical middle
  3439. event._pointDrag = wx.Point(itemRect.GetX(), itemRect.GetY() + itemRect.GetHeight()/2)
  3440. event.SetEventObject(self)
  3441. self.GetEventHandler().ProcessEvent(event)
  3442. elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]:
  3443. if not self.IsItemEnabled(self._current):
  3444. event.Skip()
  3445. return
  3446. if not event.HasModifiers():
  3447. event = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
  3448. event._item = self._current
  3449. event.SetEventObject(self)
  3450. self.GetEventHandler().ProcessEvent(event)
  3451. if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0:
  3452. checked = not self.IsItemChecked(self._current)
  3453. self.CheckItem(self._current, checked)
  3454. # in any case, also generate the normal key event for this key,
  3455. # even if we generated the ACTIVATED event above: this is what
  3456. # wxMSW does and it makes sense because you might not want to
  3457. # process ACTIVATED event at all and handle Space and Return
  3458. # directly (and differently) which would be impossible otherwise
  3459. event.Skip()
  3460. # up goes to the previous sibling or to the last
  3461. # of its children if it's expanded
  3462. elif keyCode in (wx.WXK_UP, wx.WXK_NUMPAD_UP):
  3463. prev = self.GetPrevSibling(self._key_current)
  3464. if not prev:
  3465. prev = self.GetItemParent(self._key_current)
  3466. if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
  3467. return
  3468. if prev:
  3469. current = self._key_current
  3470. # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be?
  3471. if current == self.GetFirstChild(prev)[0] and self.IsItemEnabled(prev):
  3472. # otherwise we return to where we came from
  3473. self.DoSelectItem(prev, unselect_others, extended_select)
  3474. self._key_current = prev
  3475. else:
  3476. current = self._key_current
  3477. # We are going to another parent node
  3478. while self.IsExpanded(prev) and self.HasChildren(prev):
  3479. child = self.GetLastChild(prev)
  3480. if child:
  3481. prev = child
  3482. current = prev
  3483. # Try to get the previous siblings and see if they are active
  3484. while prev and not self.IsItemEnabled(prev):
  3485. prev = self.GetPrevSibling(prev)
  3486. if not prev:
  3487. # No previous siblings active: go to the parent and up
  3488. prev = self.GetItemParent(current)
  3489. while prev and not self.IsItemEnabled(prev):
  3490. prev = self.GetItemParent(prev)
  3491. if prev:
  3492. self.DoSelectItem(prev, unselect_others, extended_select)
  3493. self._key_current = prev
  3494. # left arrow goes to the parent
  3495. elif keyCode in (wx.WXK_LEFT, wx.WXK_NUMPAD_LEFT):
  3496. prev = self.GetItemParent(self._current)
  3497. if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT):
  3498. # don't go to root if it is hidden
  3499. prev = self.GetPrevSibling(self._current)
  3500. if self.IsExpanded(self._current):
  3501. self.Collapse(self._current)
  3502. else:
  3503. if prev and self.IsItemEnabled(prev):
  3504. self.DoSelectItem(prev, unselect_others, extended_select)
  3505. elif keyCode in (wx.WXK_RIGHT, wx.WXK_NUMPAD_RIGHT):
  3506. # this works the same as the down arrow except that we
  3507. # also expand the item if it wasn't expanded yet
  3508. if self.IsExpanded(self._current) and self.HasChildren(self._current):
  3509. child, cookie = self.GetFirstChild(self._key_current)
  3510. if self.IsItemEnabled(child):
  3511. self.DoSelectItem(child, unselect_others, extended_select)
  3512. self._key_current = child
  3513. else:
  3514. self.Expand(self._current)
  3515. # fall through
  3516. elif keyCode in (wx.WXK_DOWN, wx.WXK_NUMPAD_DOWN):
  3517. if self.IsExpanded(self._key_current) and self.HasChildren(self._key_current):
  3518. child = self.GetNextActiveItem(self._key_current)
  3519. if child:
  3520. self.DoSelectItem(child, unselect_others, extended_select)
  3521. self._key_current = child
  3522. else:
  3523. next = self.GetNextSibling(self._key_current)
  3524. if not next:
  3525. current = self._key_current
  3526. while current and not next:
  3527. current = self.GetItemParent(current)
  3528. if current:
  3529. next = self.GetNextSibling(current)
  3530. if not next or not self.IsItemEnabled(next):
  3531. next = None
  3532. else:
  3533. while next and not self.IsItemEnabled(next):
  3534. next = self.GetNext(next)
  3535. if next:
  3536. self.DoSelectItem(next, unselect_others, extended_select)
  3537. self._key_current = next
  3538. # <End> selects the last visible tree item
  3539. elif keyCode in (wx.WXK_END, wx.WXK_NUMPAD_END):
  3540. last = self.GetRootItem()
  3541. while last and self.IsExpanded(last):
  3542. lastChild = self.GetLastChild(last)
  3543. # it may happen if the item was expanded but then all of
  3544. # its children have been deleted - so IsExpanded() returned
  3545. # true, but GetLastChild() returned invalid item
  3546. if not lastChild:
  3547. break
  3548. last = lastChild
  3549. if last and self.IsItemEnabled(last):
  3550. self.DoSelectItem(last, unselect_others, extended_select)
  3551. # <Home> selects the root item
  3552. elif keyCode in (wx.WXK_HOME, wx.WXK_NUMPAD_HOME):
  3553. prev = self.GetRootItem()
  3554. if not prev:
  3555. return
  3556. if self.HasFlag(TR_HIDE_ROOT):
  3557. prev, cookie = self.GetFirstChild(prev)
  3558. if not prev:
  3559. return
  3560. if self.IsItemEnabled(prev):
  3561. self.DoSelectItem(prev, unselect_others, extended_select)
  3562. else:
  3563. if not event.HasModifiers() and ((keyCode >= ord('0') and keyCode <= ord('9')) or \
  3564. (keyCode >= ord('a') and keyCode <= ord('z')) or \
  3565. (keyCode >= ord('A') and keyCode <= ord('Z'))):
  3566. # find the next item starting with the given prefix
  3567. ch = chr(keyCode)
  3568. id = self.FindItem(self._current, self._findPrefix + ch)
  3569. if not id:
  3570. # no such item
  3571. return
  3572. if self.IsItemEnabled(id):
  3573. self.SelectItem(id)
  3574. self._findPrefix += ch
  3575. # also start the timer to reset the current prefix if the user
  3576. # doesn't press any more alnum keys soon -- we wouldn't want
  3577. # to use this prefix for a new item search
  3578. if not self._findTimer:
  3579. self._findTimer = TreeFindTimer(self)
  3580. self._findTimer.Start(_DELAY, wx.TIMER_ONE_SHOT)
  3581. else:
  3582. event.Skip()
  3583. def GetNextActiveItem(self, item, down=True):
  3584. """Returns the next active item. Used Internally at present. """
  3585. if down:
  3586. sibling = self.GetNextSibling
  3587. else:
  3588. sibling = self.GetPrevSibling
  3589. if self.GetItemType(item) == 2 and not self.IsItemChecked(item):
  3590. # Is an unchecked radiobutton... all its children are inactive
  3591. # try to get the next/previous sibling
  3592. found = 0
  3593. while 1:
  3594. child = sibling(item)
  3595. if (child and self.IsItemEnabled(child)) or not child:
  3596. break
  3597. item = child
  3598. else:
  3599. # Tha's not a radiobutton... but some of its children can be
  3600. # inactive
  3601. child, cookie = self.GetFirstChild(item)
  3602. while child and not self.IsItemEnabled(child):
  3603. child, cookie = self.GetNextChild(item, cookie)
  3604. if child and self.IsItemEnabled(child):
  3605. return child
  3606. return None
  3607. def HitTest(self, point, flags=0):
  3608. """
  3609. Calculates which (if any) item is under the given point, returning the tree item
  3610. at this point plus extra information flags. Flags is a bitlist of the following:
  3611. TREE_HITTEST_ABOVE above the client area
  3612. TREE_HITTEST_BELOW below the client area
  3613. TREE_HITTEST_NOWHERE no item has been hit
  3614. TREE_HITTEST_ONITEMBUTTON on the button associated to an item
  3615. TREE_HITTEST_ONITEMICON on the icon associated to an item
  3616. TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present
  3617. TREE_HITTEST_ONITEMINDENT on the indent associated to an item
  3618. TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item
  3619. TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item
  3620. TREE_HITTEST_TOLEFT on the left of the client area
  3621. TREE_HITTEST_TORIGHT on the right of the client area
  3622. TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item
  3623. TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item
  3624. TREE_HITTEST_ONITEM anywhere on the item
  3625. Note: both the item (if any, None otherwise) and the flag are always returned as a tuple.
  3626. """
  3627. w, h = self.GetSize()
  3628. flags = 0
  3629. if point.x < 0:
  3630. flags |= TREE_HITTEST_TOLEFT
  3631. if point.x > w:
  3632. flags |= TREE_HITTEST_TORIGHT
  3633. if point.y < 0:
  3634. flags |= TREE_HITTEST_ABOVE
  3635. if point.y > h:
  3636. flags |= TREE_HITTEST_BELOW
  3637. if flags:
  3638. return None, flags
  3639. if self._anchor == None:
  3640. flags = TREE_HITTEST_NOWHERE
  3641. return None, flags
  3642. hit, flags = self._anchor.HitTest(self.CalcUnscrolledPosition(point), self, flags, 0)
  3643. if hit == None:
  3644. flags = TREE_HITTEST_NOWHERE
  3645. return None, flags
  3646. if not self.IsItemEnabled(hit):
  3647. return None, flags
  3648. return hit, flags
  3649. def GetBoundingRect(self, item, textOnly=False):
  3650. """Gets the bounding rectangle of the item."""
  3651. if not item:
  3652. raise Exception("\nERROR: Invalid Tree Item. ")
  3653. i = item
  3654. startX, startY = self.GetViewStart()
  3655. rect = wx.Rect()
  3656. rect.x = i.GetX() - startX*_PIXELS_PER_UNIT
  3657. rect.y = i.GetY() - startY*_PIXELS_PER_UNIT
  3658. rect.width = i.GetWidth()
  3659. rect.height = self.GetLineHeight(i)
  3660. return rect
  3661. def Edit(self, item):
  3662. """
  3663. Internal function. Starts the editing of an item label, sending a
  3664. EVT_TREE_BEGIN_LABEL_EDIT event.
  3665. """
  3666. te = TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT, self.GetId())
  3667. te._item = item
  3668. te.SetEventObject(self)
  3669. if self.GetEventHandler().ProcessEvent(te) and not te.IsAllowed():
  3670. # vetoed by user
  3671. return
  3672. # We have to call this here because the label in
  3673. # question might just have been added and no screen
  3674. # update taken place.
  3675. if self._dirty:
  3676. if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
  3677. self.Update()
  3678. else:
  3679. wx.YieldIfNeeded()
  3680. if self._textCtrl != None and item != self._textCtrl.item():
  3681. self._textCtrl.StopEditing()
  3682. self._textCtrl = TreeTextCtrl(self, item=item)
  3683. self._textCtrl.SetFocus()
  3684. def GetEditControl(self):
  3685. """
  3686. Returns a pointer to the edit TextCtrl if the item is being edited or
  3687. None otherwise (it is assumed that no more than one item may be edited
  3688. simultaneously).
  3689. """
  3690. return self._textCtrl
  3691. def OnRenameAccept(self, item, value):
  3692. """
  3693. Called by TreeTextCtrl, to accept the changes and to send the
  3694. EVT_TREE_END_LABEL_EDIT event.
  3695. """
  3696. le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
  3697. le._item = item
  3698. le.SetEventObject(self)
  3699. le._label = value
  3700. le._editCancelled = False
  3701. return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed()
  3702. def OnRenameCancelled(self, item):
  3703. """
  3704. Called by TreeTextCtrl, to cancel the changes and to send the
  3705. EVT_TREE_END_LABEL_EDIT event.
  3706. """
  3707. # let owner know that the edit was cancelled
  3708. le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId())
  3709. le._item = item
  3710. le.SetEventObject(self)
  3711. le._label = ""
  3712. le._editCancelled = True
  3713. self.GetEventHandler().ProcessEvent(le)
  3714. def OnRenameTimer(self):
  3715. """The timer for renaming has expired. Start editing."""
  3716. self.Edit(self._current)
  3717. def OnMouse(self, event):
  3718. """Handles a bunch of wx.EVT_MOUSE_EVENTS events."""
  3719. if not self._anchor:
  3720. return
  3721. pt = self.CalcUnscrolledPosition(event.GetPosition())
  3722. # Is the mouse over a tree item button?
  3723. flags = 0
  3724. thisItem, flags = self._anchor.HitTest(pt, self, flags, 0)
  3725. underMouse = thisItem
  3726. underMouseChanged = underMouse != self._underMouse
  3727. if underMouse and (flags & TREE_HITTEST_ONITEM) and not event.LeftIsDown() and \
  3728. not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
  3729. underMouse = underMouse
  3730. else:
  3731. underMouse = None
  3732. if underMouse != self._underMouse:
  3733. if self._underMouse:
  3734. # unhighlight old item
  3735. self._underMouse = None
  3736. self._underMouse = underMouse
  3737. # Determines what item we are hovering over and need a tooltip for
  3738. hoverItem = thisItem
  3739. # We do not want a tooltip if we are dragging, or if the rename timer is running
  3740. if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()):
  3741. if hoverItem is not None:
  3742. tooltipString = u""
  3743. # Ask the tree control what tooltip (if any) should be shown
  3744. hevent = TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP, self.GetId())
  3745. hevent._item = hoverItem
  3746. hevent.SetEventObject(self)
  3747. # if self.GetEventHandler().ProcessEvent(hevent) and hevent.IsAllowed():
  3748. # self.SetToolTip(hevent._label)
  3749. if self.GetEventHandler().ProcessEvent(hevent):
  3750. if hevent.IsAllowed():
  3751. self.SetToolTipString(hevent._label)
  3752. else:
  3753. if flags & TREE_HITTEST_ONITEMLABEL:
  3754. hPt = event.GetPosition()
  3755. hPt.x = self.GetSizeTuple()[0] # To right border
  3756. hPt = self.CalcUnscrolledPosition(hPt)
  3757. # If point at right border is inside label the
  3758. # label is probably longer than window width
  3759. if hoverItem.HitTest(hPt, self)[1] & \
  3760. TREE_HITTEST_ONITEMLABEL:
  3761. tooltipString = hoverItem.GetText()
  3762. self.SetToolTipString(tooltipString)
  3763. else:
  3764. self.SetToolTipString(tooltipString)
  3765. if hoverItem.IsHyperText() and (flags & TREE_HITTEST_ONITEMLABEL) and hoverItem.IsEnabled():
  3766. self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
  3767. self._isonhyperlink = True
  3768. else:
  3769. if self._isonhyperlink:
  3770. self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
  3771. self._isonhyperlink = False
  3772. # we process left mouse up event (enables in-place edit), right down
  3773. # (pass to the user code), left dbl click (activate item) and
  3774. # dragging/moving events for items drag-and-drop
  3775. if not (event.LeftDown() or event.LeftUp() or event.RightDown() or event.LeftDClick() or \
  3776. event.Dragging() or ((event.Moving() or event.RightUp()) and self._isDragging)):
  3777. event.Skip()
  3778. return
  3779. flags = 0
  3780. item, flags = self._anchor.HitTest(pt, self, flags, 0)
  3781. if event.Dragging() and not self._isDragging and \
  3782. (self._dragCount != 0 or (flags & TREE_HITTEST_ONITEMICON) or
  3783. (flags & TREE_HITTEST_ONITEMLABEL)):
  3784. if self._dragCount == 0:
  3785. self._dragStart = pt
  3786. self._countDrag = 0
  3787. self._dragCount = self._dragCount + 1
  3788. if self._dragCount != 6: # Orig. value: 3
  3789. # wait until user drags a bit further...
  3790. return
  3791. command = (event.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG] or [wxEVT_TREE_BEGIN_DRAG])[0]
  3792. nevent = TreeEvent(command, self.GetId())
  3793. nevent._item = self._selectedNodeWhileMousePressed # self._current
  3794. nevent.SetEventObject(self)
  3795. newpt = self.CalcScrolledPosition(pt)
  3796. nevent.SetPoint(newpt)
  3797. # by default the dragging is not supported, the user code must
  3798. # explicitly allow the event for it to take place
  3799. nevent.Veto()
  3800. if self.GetEventHandler().ProcessEvent(nevent) and nevent.IsAllowed():
  3801. # we're going to drag this item
  3802. self._isDragging = True
  3803. # remember the old cursor because we will change it while
  3804. # dragging
  3805. self._oldCursor = self._cursor
  3806. # in a single selection control, hide the selection temporarily
  3807. if not (self.GetTreeStyle() & TR_MULTIPLE):
  3808. self._oldSelection = self.GetSelection()
  3809. if self._oldSelection:
  3810. self._oldSelection.SetHilight(False)
  3811. self.RefreshLine(self._oldSelection)
  3812. else:
  3813. selections = self.GetSelections()
  3814. if len(selections) == 1:
  3815. self._oldSelection = selections[0]
  3816. self._oldSelection.SetHilight(False)
  3817. self.RefreshLine(self._oldSelection)
  3818. if self._dragImage:
  3819. del self._dragImage
  3820. # Create the custom draw image from the icons and the text of the item
  3821. self._dragImage = DragImage(self, self._selectedNodeWhileMousePressed) # self._current)
  3822. # print "self._dragImage =", repr(self._selectedNodeWhileMousePressed.GetText())
  3823. self._dragImage.BeginDrag(wx.Point(0,0), self)
  3824. self._dragImage.Show()
  3825. self._dragImage.Move(self.CalcScrolledPosition(pt))
  3826. elif event.Dragging() and self._isDragging:
  3827. self._dragImage.Move(self.CalcScrolledPosition(pt))
  3828. if self._countDrag == 0 and item:
  3829. self._oldItem = item
  3830. if item != self._dropTarget:
  3831. # unhighlight the previous drop target
  3832. if self._dropTarget:
  3833. self._dropTarget.SetHilight(False)
  3834. self.RefreshLine(self._dropTarget)
  3835. if item:
  3836. item.SetHilight(True)
  3837. self.RefreshLine(item)
  3838. self._countDrag = self._countDrag + 1
  3839. self._dropTarget = item
  3840. self.Update()
  3841. if self._countDrag >= 3:
  3842. # Here I am trying to avoid ugly repainting problems... hope it works
  3843. self.RefreshLine(self._oldItem)
  3844. self._countDrag = 0
  3845. elif (event.LeftUp() or event.RightUp()) and self._isDragging:
  3846. if self._dragImage:
  3847. self._dragImage.EndDrag()
  3848. if self._dropTarget:
  3849. self._dropTarget.SetHilight(False)
  3850. if not self.HasFlag(TR_MULTIPLE) and \
  3851. self._selectedNodeWhileMousePressed:
  3852. self.DoSelectItem(self._selectedNodeWhileMousePressed,
  3853. unselect_others, extended_select)
  3854. elif self._oldSelection:
  3855. self._oldSelection.SetHilight(True)
  3856. self.RefreshLine(self._oldSelection)
  3857. self._oldSelection = None
  3858. # generate the drag end event
  3859. event = TreeEvent(wxEVT_TREE_END_DRAG, self.GetId())
  3860. event._item = self._selectedNodeWhileMousePressed # item
  3861. # print "event._item =", repr(self._selectedNodeWhileMousePressed.GetText())
  3862. event._pointDrag = self.CalcScrolledPosition(pt)
  3863. event.SetEventObject(self)
  3864. self.GetEventHandler().ProcessEvent(event)
  3865. self._isDragging = False
  3866. self._dropTarget = None
  3867. self._dragCount = 0 # Added ???
  3868. self.SetCursor(self._oldCursor)
  3869. if wx.Platform in ["__WXMSW__", "__WXMAC__"]:
  3870. self.Refresh()
  3871. else:
  3872. # Probably this is not enough on GTK. Try a Refresh() if it does not work.
  3873. wx.YieldIfNeeded()
  3874. else:
  3875. # If we got to this point, we are not dragging or moving the mouse.
  3876. # Because the code in carbon/toplevel.cpp will only set focus to the tree
  3877. # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work.
  3878. # We skip even if we didn't hit an item because we still should
  3879. # restore focus to the tree control even if we didn't exactly hit an item.
  3880. if event.LeftDown():
  3881. self._hasFocus = True
  3882. self.SetFocusIgnoringChildren()
  3883. event.Skip()
  3884. # here we process only the messages which happen on tree items
  3885. self._dragCount = 0
  3886. if item == None:
  3887. if self._textCtrl != None and item != self._textCtrl.item():
  3888. self._textCtrl.StopEditing()
  3889. return # we hit the blank area
  3890. if event.RightDown():
  3891. if self._textCtrl != None and item != self._textCtrl.item():
  3892. self._textCtrl.StopEditing()
  3893. self._hasFocus = True
  3894. self.SetFocusIgnoringChildren()
  3895. # If the item is already selected, do not update the selection.
  3896. # Multi-selections should not be cleared if a selected item is clicked.
  3897. if not self.IsSelected(item) and not event.LeftDown():
  3898. # print "selectitem"
  3899. self.DoSelectItem(item, True, False)
  3900. nevent = TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, self.GetId())
  3901. nevent._item = item
  3902. nevent._pointDrag = self.CalcScrolledPosition(pt)
  3903. nevent.SetEventObject(self)
  3904. event.Skip(not self.GetEventHandler().ProcessEvent(nevent))
  3905. # Consistent with MSW (for now), send the ITEM_MENU *after*
  3906. # the RIGHT_CLICK event. TODO: This behaviour may change.
  3907. nevent2 = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId())
  3908. nevent2._item = item
  3909. nevent2._pointDrag = self.CalcScrolledPosition(pt)
  3910. nevent2.SetEventObject(self)
  3911. self.GetEventHandler().ProcessEvent(nevent2)
  3912. elif event.LeftUp():
  3913. if self._selectedNodeWhileMousePressed is item:
  3914. # this facilitates multiple-item drag-and-drop
  3915. if self.HasFlag(TR_MULTIPLE):
  3916. selections = self.GetSelections()
  3917. if len(selections) > 1 and not event.CmdDown() and not event.ShiftDown():
  3918. self.DoSelectItem(item, True, False)
  3919. else:
  3920. is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(),
  3921. event.ShiftDown(),
  3922. event.CmdDown())
  3923. self._selectedNodeWhileMousePressed = None
  3924. if flags & TREE_HITTEST_ONITEM:
  3925. # how should the selection work for this event?
  3926. if item.IsHyperText():
  3927. self.SetItemVisited(item, True)
  3928. self.DoSelectItem(item, unselect_others, extended_select)
  3929. if self._lastOnSame:
  3930. if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasFlag(TR_EDIT_LABELS):
  3931. if self._renameTimer:
  3932. if self._renameTimer.IsRunning():
  3933. self._renameTimer.Stop()
  3934. else:
  3935. self._renameTimer = TreeRenameTimer(self)
  3936. self._renameTimer.Start(_DELAY, True)
  3937. self._lastOnSame = False
  3938. else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
  3939. if not item or not item.IsEnabled():
  3940. if self._textCtrl is not None and item != self._textCtrl.item():
  3941. self._textCtrl.StopEditing()
  3942. return
  3943. if self._textCtrl != None and item != self._textCtrl.item():
  3944. self._textCtrl.StopEditing()
  3945. self._hasFocus = True
  3946. self.SetFocusIgnoringChildren()
  3947. if event.LeftDown():
  3948. self._lastOnSame = item == self._current
  3949. self._selectedNodeWhileMousePressed = item
  3950. # print "event.LeftDown()", repr(item.GetText())
  3951. if flags & TREE_HITTEST_ONITEMBUTTON:
  3952. # only toggle the item for a single click, double click on
  3953. # the button doesn't do anything (it toggles the item twice)
  3954. if event.LeftDown():
  3955. self.Toggle(item)
  3956. # don't select the item if the button was clicked
  3957. return
  3958. if item.GetType() > 0 and (flags & TREE_HITTEST_ONITEMCHECKICON):
  3959. if event.LeftDown():
  3960. self.CheckItem(item, not self.IsItemChecked(item))
  3961. return
  3962. # clear the previously selected items, if the
  3963. # user clicked outside of the present selection.
  3964. # otherwise, perform the deselection on mouse-up.
  3965. # this allows multiple drag and drop to work.
  3966. # but if Cmd is down, toggle selection of the clicked item
  3967. # if not self.IsSelected(item) or event.CmdDown():
  3968. # print "not self.IsSelected(item)"
  3969. # self._dropTarget.SetHilight(False)
  3970. # self.RefreshLine(self._dropTarget)
  3971. # if not (self.GetTreeStyle() & TR_MULTIPLE):
  3972. # self._oldSelection = self.GetSelection()
  3973. #
  3974. # if self._oldSelection:
  3975. # self._oldSelection.SetHilight(False)
  3976. # self.RefreshLine(self._oldSelection)
  3977. #
  3978. # item.SetHilight(True)
  3979. # self.RefreshLine(item)
  3980. #
  3981. # item.SetHilight(False)
  3982. # if self._oldSelection:
  3983. # self._oldSelection.SetHilight(True)
  3984. if event.CmdDown():
  3985. if flags & TREE_HITTEST_ONITEM:
  3986. # how should the selection work for this event?
  3987. if item.IsHyperText():
  3988. self.SetItemVisited(item, True)
  3989. is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(),
  3990. event.ShiftDown(),
  3991. event.CmdDown())
  3992. self.DoSelectItem(item, unselect_others, extended_select)
  3993. # For some reason, Windows isn't recognizing a left double-click,
  3994. # so we need to simulate it here. Allow 200 milliseconds for now.
  3995. if event.LeftDClick():
  3996. # double clicking should not start editing the item label
  3997. if self._renameTimer:
  3998. self._renameTimer.Stop()
  3999. self._lastOnSame = False
  4000. # send activate event first
  4001. nevent = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId())
  4002. nevent._item = item
  4003. nevent._pointDrag = self.CalcScrolledPosition(pt)
  4004. nevent.SetEventObject(self)
  4005. if not self.GetEventHandler().ProcessEvent(nevent):
  4006. # if the user code didn't process the activate event,
  4007. # handle it ourselves by toggling the item when it is
  4008. # double clicked
  4009. ## if item.HasPlus():
  4010. self.Toggle(item)
  4011. def OnInternalIdle(self):
  4012. """Performs operations in idle time (essentially drawing)."""
  4013. # # Check if we need to select the root item
  4014. # # because nothing else has been selected.
  4015. # # Delaying it means that we can invoke event handlers
  4016. # # as required, when a first item is selected.
  4017. # if not self.HasFlag(TR_MULTIPLE) and not self.GetSelection():
  4018. #
  4019. # if self._select_me:
  4020. # self.SelectItem(self._select_me)
  4021. # elif self.GetRootItem():
  4022. # self.SelectItem(self.GetRootItem())
  4023. # after all changes have been done to the tree control,
  4024. # we actually redraw the tree when everything is over
  4025. if not self._dirty:
  4026. return
  4027. if self._freezeCount:
  4028. return
  4029. self._dirty = False
  4030. self.CalculatePositions()
  4031. self.Refresh()
  4032. self.AdjustMyScrollbars()
  4033. def CalculateSize(self, item, dc):
  4034. """Calculates overall position and size of an item."""
  4035. attr = item.GetAttributes()
  4036. if attr and attr.HasFont():
  4037. dc.SetFont(attr.GetFont())
  4038. elif item.IsBold():
  4039. dc.SetFont(self._boldFont)
  4040. else:
  4041. dc.SetFont(self._normalFont)
  4042. text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText())
  4043. text_h+=2
  4044. # restore normal font
  4045. dc.SetFont(self._normalFont)
  4046. image_w, image_h = 0, 0
  4047. image = item.GetCurrentImage()
  4048. if image != _NO_IMAGE:
  4049. if self._imageListNormal:
  4050. image_w, image_h = self._imageListNormal.GetSize(image)
  4051. image_w += 4
  4052. total_h = ((image_h > text_h) and [image_h] or [text_h])[0]
  4053. checkimage = item.GetCurrentCheckedImage()
  4054. if checkimage is not None:
  4055. wcheck, hcheck = self._imageListCheck.GetSize(checkimage)
  4056. wcheck += 4
  4057. else:
  4058. wcheck = 0
  4059. # if total_h < 30:
  4060. # total_h += 2 # at least 2 pixels
  4061. # else:
  4062. # total_h += total_h/10 # otherwise 10% extra spacing
  4063. if total_h > self._lineHeight:
  4064. self._lineHeight = total_h
  4065. if not item.GetWindow():
  4066. item.SetWidth(image_w+text_w+wcheck+2)
  4067. item.SetHeight(total_h)
  4068. else:
  4069. item.SetWidth(item.GetWindowSize()[0]+image_w+text_w+wcheck+2)
  4070. item.SetHeight(max(total_h, item.GetWindowSize()[1]))
  4071. def CalculateLevel(self, item, dc, level, y):
  4072. """Calculates the level of an item."""
  4073. x = level*self._indent
  4074. if not self.HasFlag(TR_HIDE_ROOT):
  4075. x += self._indent
  4076. elif level == 0:
  4077. # a hidden root is not evaluated, but its
  4078. # children are always calculated
  4079. children = item.GetChildren()
  4080. count = len(children)
  4081. level = level + 1
  4082. for n in xrange(count):
  4083. y = self.CalculateLevel(children[n], dc, level, y) # recurse
  4084. return y
  4085. self.CalculateSize(item, dc)
  4086. # set its position
  4087. item.SetX(x+self._spacing)
  4088. item.SetY(y)
  4089. y += self.GetLineHeight(item)
  4090. if not item.IsExpanded():
  4091. # we don't need to calculate collapsed branches
  4092. return y
  4093. children = item.GetChildren()
  4094. count = len(children)
  4095. level = level + 1
  4096. for n in xrange(count):
  4097. y = self.CalculateLevel(children[n], dc, level, y) # recurse
  4098. return y
  4099. def CalculatePositions(self):
  4100. """Calculates all the positions of the visible items."""
  4101. if not self._anchor:
  4102. return
  4103. dc = wx.ClientDC(self)
  4104. self.PrepareDC(dc)
  4105. dc.SetFont(self._normalFont)
  4106. dc.SetPen(self._dottedPen)
  4107. y = 2
  4108. y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion
  4109. def RefreshSubtree(self, item):
  4110. """Refreshes a damaged subtree of an item."""
  4111. if self._dirty:
  4112. return
  4113. if self._freezeCount:
  4114. return
  4115. client = self.GetClientSize()
  4116. rect = wx.Rect()
  4117. x, rect.y = self.CalcScrolledPosition(0, item.GetY())
  4118. rect.width = client.x
  4119. rect.height = client.y
  4120. self.Refresh(True, rect)
  4121. self.AdjustMyScrollbars()
  4122. def RefreshLine(self, item):
  4123. """Refreshes a damaged item line."""
  4124. if self._dirty:
  4125. return
  4126. if self._freezeCount:
  4127. return
  4128. rect = wx.Rect()
  4129. x, rect.y = self.CalcScrolledPosition(0, item.GetY())
  4130. rect.width = self.GetClientSize().x
  4131. rect.height = self.GetLineHeight(item)
  4132. self.Refresh(True, rect)
  4133. def RefreshSelected(self):
  4134. """Refreshes a damaged selected item line."""
  4135. if self._freezeCount:
  4136. return
  4137. # TODO: this is awfully inefficient, we should keep the list of all
  4138. # selected items internally, should be much faster
  4139. if self._anchor:
  4140. self.RefreshSelectedUnder(self._anchor)
  4141. def RefreshSelectedUnder(self, item):
  4142. """Refreshes the selected items under the given item."""
  4143. if self._freezeCount:
  4144. return
  4145. if item.IsSelected():
  4146. self.RefreshLine(item)
  4147. children = item.GetChildren()
  4148. for child in children:
  4149. self.RefreshSelectedUnder(child)
  4150. def Freeze(self):
  4151. """Freeze CustomTreeCtrl."""
  4152. self._freezeCount = self._freezeCount + 1
  4153. def Thaw(self):
  4154. """Thaw CustomTreeCtrl."""
  4155. if self._freezeCount == 0:
  4156. raise Exception("\nERROR: Thawing Unfrozen Tree Control?")
  4157. self._freezeCount = self._freezeCount - 1
  4158. if not self._freezeCount:
  4159. self.Refresh()
  4160. # ----------------------------------------------------------------------------
  4161. # changing colours: we need to refresh the tree control
  4162. # ----------------------------------------------------------------------------
  4163. def SetBackgroundColour(self, colour):
  4164. """Changes the background colour of CustomTreeCtrl."""
  4165. self._backgroundColour = colour
  4166. if not wx.Window.SetBackgroundColour(self, colour):
  4167. return False
  4168. if self._freezeCount:
  4169. return True
  4170. self.Refresh()
  4171. return True
  4172. def SetForegroundColour(self, colour):
  4173. """Changes the foreground colour of CustomTreeCtrl."""
  4174. if not wx.Window.SetForegroundColour(self, colour):
  4175. return False
  4176. if self._freezeCount:
  4177. return True
  4178. self.Refresh()
  4179. return True
  4180. def OnGetToolTip(self, event):
  4181. """
  4182. Process the tooltip event, to speed up event processing. Does not actually
  4183. get a tooltip.
  4184. """
  4185. event.Veto()
  4186. def DoGetBestSize(self):
  4187. """Something is better than nothing..."""
  4188. # something is better than nothing...
  4189. # 100x80 is what the MSW version will get from the default
  4190. # wxControl::DoGetBestSize
  4191. return wx.Size(100, 80)
  4192. def GetClassDefaultAttributes(self):
  4193. """Gets the class default attributes."""
  4194. attr = wx.VisualAttributes()
  4195. attr.colFg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
  4196. attr.colBg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_LISTBOX)
  4197. attr.font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
  4198. return attr
  4199. GetClassDefaultAttributes = classmethod(GetClassDefaultAttributes)