]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/OutlineService.py
Don't use the old wxPython namespace
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / OutlineService.py
1 #----------------------------------------------------------------------------
2 # Name: OutlineService.py
3 # Purpose: Outline View Service for pydocview
4 #
5 # Author: Morgan Hua
6 #
7 # Created: 8/3/04
8 # CVS-ID: $Id$
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.lib.docview
15 import wx.lib.pydocview
16 import Service
17 _ = wx.GetTranslation
18
19
20 #----------------------------------------------------------------------------
21 # Constants
22 #----------------------------------------------------------------------------
23 SORT_NONE = 0
24 SORT_ASC = 1
25 SORT_DESC = 2
26
27 class OutlineView(Service.ServiceView):
28 """ Reusable Outline View for any document.
29 As a default, it uses a modified tree control (OutlineTreeCtrl) that allows sorting.
30 Subclass OutlineTreeCtrl to customize the tree control and call SetTreeCtrl to install a customized tree control.
31 When an item is selected, the document view is called back (with DoSelectCallback) to highlight and display the corresponding item in the document view.
32 """
33
34 #----------------------------------------------------------------------------
35 # Overridden methods
36 #----------------------------------------------------------------------------
37
38 def __init__(self, service):
39 Service.ServiceView.__init__(self, service)
40 self._actionOnSelect = True
41
42
43 def _CreateControl(self, parent, id):
44 treeCtrl = OutlineTreeCtrl(parent, id)
45 wx.EVT_TREE_SEL_CHANGED(treeCtrl, treeCtrl.GetId(), self.DoSelection)
46 wx.EVT_SET_FOCUS(treeCtrl, self.DoSelection)
47 wx.EVT_ENTER_WINDOW(treeCtrl, treeCtrl.CallDoLoadOutlineCallback)
48 wx.EVT_RIGHT_DOWN(treeCtrl, self.OnRightClick)
49
50 return treeCtrl
51
52
53 #----------------------------------------------------------------------------
54 # Service specific methods
55 #----------------------------------------------------------------------------
56
57 def OnRightClick(self, event):
58 menu = wx.Menu()
59
60 menu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
61 menu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
62 menu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
63
64 config = wx.ConfigBase_Get()
65 sort = config.ReadInt("OutlineSort", SORT_NONE)
66 if sort == SORT_NONE:
67 menu.Check(OutlineService.SORT_NONE, True)
68 elif sort == SORT_ASC:
69 menu.Check(OutlineService.SORT_ASC, True)
70 elif sort == SORT_DESC:
71 menu.Check(OutlineService.SORT_DESC, True)
72
73 self.GetControl().PopupMenu(menu, event.GetPosition())
74 menu.Destroy()
75
76
77 #----------------------------------------------------------------------------
78 # Tree Methods
79 #----------------------------------------------------------------------------
80
81 def DoSelection(self, event):
82 if not self._actionOnSelect:
83 return
84 item = self.GetControl().GetSelection()
85 if item:
86 self.GetControl().CallDoSelectCallback(item)
87 event.Skip()
88
89
90 def ResumeActionOnSelect(self):
91 self._actionOnSelect = True
92
93
94 def StopActionOnSelect(self):
95 self._actionOnSelect = False
96
97
98 def SetTreeCtrl(self, tree):
99 self.SetControl(tree)
100 wx.EVT_TREE_SEL_CHANGED(self.GetControl(), self.GetControl().GetId(), self.DoSelection)
101 wx.EVT_ENTER_WINDOW(self.GetControl(), treeCtrl.CallDoLoadOutlineCallback)
102 wx.EVT_RIGHT_DOWN(self.GetControl(), self.OnRightClick)
103
104
105 def GetTreeCtrl(self):
106 return self.GetControl()
107
108
109 def OnSort(self, sortOrder):
110 treeCtrl = self.GetControl()
111 treeCtrl.SetSortOrder(sortOrder)
112 treeCtrl.SortAllChildren(treeCtrl.GetRootItem())
113
114
115 def ClearTreeCtrl(self):
116 if self.GetControl():
117 self.GetControl().DeleteAllItems()
118
119
120 def GetExpansionState(self):
121 expanded = []
122
123 treeCtrl = self.GetControl()
124 if not treeCtrl:
125 return expanded
126
127 parentItem = treeCtrl.GetRootItem()
128
129 if not parentItem:
130 return expanded
131
132 if not treeCtrl.IsExpanded(parentItem):
133 return expanded
134
135 expanded.append(treeCtrl.GetItemText(parentItem))
136
137 (child, cookie) = treeCtrl.GetFirstChild(parentItem)
138 while child.IsOk():
139 if treeCtrl.IsExpanded(child):
140 expanded.append(treeCtrl.GetItemText(child))
141 (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
142 return expanded
143
144
145 def SetExpansionState(self, expanded):
146 if not expanded or len(expanded) == 0:
147 return
148
149 treeCtrl = self.GetControl()
150
151 parentItem = treeCtrl.GetRootItem()
152 if not parentItem:
153 return
154
155 if expanded[0] != treeCtrl.GetItemText(parentItem):
156 return
157
158 (child, cookie) = treeCtrl.GetFirstChild(parentItem)
159 while child.IsOk():
160 if treeCtrl.GetItemText(child) in expanded:
161 treeCtrl.Expand(child)
162 (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
163
164 treeCtrl.EnsureVisible(parentItem)
165
166
167 class OutlineTreeCtrl(wx.TreeCtrl):
168 """ Default Tree Control Class for OutlineView.
169 This class has the added functionality of sorting by the labels
170 """
171
172
173 #----------------------------------------------------------------------------
174 # Constants
175 #----------------------------------------------------------------------------
176 ORIG_ORDER = 0
177 VIEW = 1
178 CALLBACKDATA = 2
179
180
181 #----------------------------------------------------------------------------
182 # Overridden Methods
183 #----------------------------------------------------------------------------
184
185 def __init__(self, parent, id, style=wx.TR_HAS_BUTTONS|wx.TR_DEFAULT_STYLE):
186 wx.TreeCtrl.__init__(self, parent, id, style = style)
187 self._origOrderIndex = 0
188 self._sortOrder = SORT_NONE
189
190
191 def DeleteAllItems(self):
192 self._origOrderIndex = 0
193 wx.TreeCtrl.DeleteAllItems(self)
194
195
196 #----------------------------------------------------------------------------
197 # Sort Methods
198 #----------------------------------------------------------------------------
199
200 def SetSortOrder(self, sortOrder = SORT_NONE):
201 """ Sort Order constants are defined at top of file """
202 self._sortOrder = sortOrder
203
204
205 def OnCompareItems(self, item1, item2):
206 if self._sortOrder == SORT_ASC:
207 return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower()) # sort A-Z
208 elif self._sortOrder == SORT_DESC:
209 return cmp(self.GetItemText(item2).lower(), self.GetItemText(item1).lower()) # sort Z-A
210 else:
211 return (self.GetPyData(item1)[self.ORIG_ORDER] > self.GetPyData(item2)[self.ORIG_ORDER]) # unsorted
212
213
214 def SortAllChildren(self, parentItem):
215 if parentItem and self.GetChildrenCount(parentItem, False):
216 self.SortChildren(parentItem)
217 (child, cookie) = self.GetFirstChild(parentItem)
218 while child.IsOk():
219 self.SortAllChildren(child)
220 (child, cookie) = self.GetNextChild(parentItem, cookie)
221
222
223 #----------------------------------------------------------------------------
224 # Select Callback Methods
225 #----------------------------------------------------------------------------
226
227 def CallDoSelectCallback(self, item):
228 """ Invoke the DoSelectCallback of the given view to highlight text in the document view
229 """
230 data = self.GetPyData(item)
231 if not data:
232 return
233
234 view = data[self.VIEW]
235 cbdata = data[self.CALLBACKDATA]
236 if view:
237 view.DoSelectCallback(cbdata)
238
239
240 def SelectClosestItem(self, position):
241 tree = self
242 distances = []
243 items = []
244 self.FindDistanceToTreeItems(tree.GetRootItem(), position, distances, items)
245 mindist = 1000000
246 mindex = -1
247 for index in range(0, len(distances)):
248 if distances[index] <= mindist:
249 mindist = distances[index]
250 mindex = index
251 if mindex != -1:
252 item = items[mindex]
253 self.EnsureVisible(item)
254 os_view = wx.GetApp().GetService(OutlineService).GetView()
255 if os_view:
256 os_view.StopActionOnSelect()
257 self.SelectItem(item)
258 if os_view:
259 os_view.ResumeActionOnSelect()
260
261
262 def FindDistanceToTreeItems(self, item, position, distances, items):
263 data = self.GetPyData(item)
264 this_dist = 1000000
265 if data and data[2]:
266 positionTuple = data[2]
267 if position >= positionTuple[1]:
268 items.append(item)
269 distances.append(position - positionTuple[1])
270
271 if self.ItemHasChildren(item):
272 child, cookie = self.GetFirstChild(item)
273 while child.IsOk():
274 self.FindDistanceToTreeItems(child, position, distances, items)
275 child, cookie = self.GetNextChild(item, cookie)
276 return False
277
278
279 def SetDoSelectCallback(self, item, view, callbackdata):
280 """ When an item in the outline view is selected,
281 a method is called to select the respective text in the document view.
282 The view must define the method DoSelectCallback(self, data) in order for this to work
283 """
284 self.SetPyData(item, (self._origOrderIndex, view, callbackdata))
285 self._origOrderIndex = self._origOrderIndex + 1
286
287
288 def CallDoLoadOutlineCallback(self, event):
289 """ Invoke the DoLoadOutlineCallback
290 """
291 rootItem = self.GetRootItem()
292 if rootItem:
293 data = self.GetPyData(rootItem)
294 if data:
295 view = data[self.VIEW]
296 if view and view.DoLoadOutlineCallback():
297 self.SortAllChildren(self.GetRootItem())
298
299
300 def GetCallbackView(self):
301 rootItem = self.GetRootItem()
302 if rootItem:
303 return self.GetPyData(rootItem)[self.VIEW]
304 else:
305 return None
306
307
308 class OutlineService(Service.Service):
309
310
311 #----------------------------------------------------------------------------
312 # Constants
313 #----------------------------------------------------------------------------
314 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
315 SORT = wx.NewId()
316 SORT_ASC = wx.NewId()
317 SORT_DESC = wx.NewId()
318 SORT_NONE = wx.NewId()
319
320
321 #----------------------------------------------------------------------------
322 # Overridden methods
323 #----------------------------------------------------------------------------
324
325 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
326 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
327 self._validViewTypes = []
328
329
330 def _CreateView(self):
331 return OutlineView(self)
332
333
334 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
335 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
336
337 wx.EVT_MENU(frame, OutlineService.SORT_ASC, frame.ProcessEvent)
338 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_ASC, frame.ProcessUpdateUIEvent)
339 wx.EVT_MENU(frame, OutlineService.SORT_DESC, frame.ProcessEvent)
340 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_DESC, frame.ProcessUpdateUIEvent)
341 wx.EVT_MENU(frame, OutlineService.SORT_NONE, frame.ProcessEvent)
342 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_NONE, frame.ProcessUpdateUIEvent)
343
344
345 if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
346 return True
347
348 viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
349 self._outlineSortMenu = wx.Menu()
350 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
351 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
352 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
353 viewMenu.AppendMenu(wx.NewId(), _("Outline Sort"), self._outlineSortMenu)
354
355 return True
356
357
358 #----------------------------------------------------------------------------
359 # Event Processing Methods
360 #----------------------------------------------------------------------------
361
362 def ProcessEvent(self, event):
363 if Service.Service.ProcessEvent(self, event):
364 return True
365
366 id = event.GetId()
367 if id == OutlineService.SORT_ASC:
368 self.OnSort(event)
369 return True
370 elif id == OutlineService.SORT_DESC:
371 self.OnSort(event)
372 return True
373 elif id == OutlineService.SORT_NONE:
374 self.OnSort(event)
375 return True
376 else:
377 return False
378
379
380 def ProcessUpdateUIEvent(self, event):
381 if Service.Service.ProcessUpdateUIEvent(self, event):
382 return True
383
384 id = event.GetId()
385 if id == OutlineService.SORT_ASC:
386 event.Enable(True)
387
388 config = wx.ConfigBase_Get()
389 sort = config.ReadInt("OutlineSort", SORT_NONE)
390 if sort == SORT_ASC:
391 self._outlineSortMenu.Check(OutlineService.SORT_ASC, True)
392 else:
393 self._outlineSortMenu.Check(OutlineService.SORT_ASC, False)
394
395 return True
396 elif id == OutlineService.SORT_DESC:
397 event.Enable(True)
398
399 config = wx.ConfigBase_Get()
400 sort = config.ReadInt("OutlineSort", SORT_NONE)
401 if sort == SORT_DESC:
402 self._outlineSortMenu.Check(OutlineService.SORT_DESC, True)
403 else:
404 self._outlineSortMenu.Check(OutlineService.SORT_DESC, False)
405
406 return True
407 elif id == OutlineService.SORT_NONE:
408 event.Enable(True)
409
410 config = wx.ConfigBase_Get()
411 sort = config.ReadInt("OutlineSort", SORT_NONE)
412 if sort == SORT_NONE:
413 self._outlineSortMenu.Check(OutlineService.SORT_NONE, True)
414 else:
415 self._outlineSortMenu.Check(OutlineService.SORT_NONE, False)
416
417 return True
418 else:
419 return False
420
421
422 def OnSort(self, event):
423 id = event.GetId()
424 if id == OutlineService.SORT_ASC:
425 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_ASC)
426 self.GetView().OnSort(SORT_ASC)
427 return True
428 elif id == OutlineService.SORT_DESC:
429 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_DESC)
430 self.GetView().OnSort(SORT_DESC)
431 return True
432 elif id == OutlineService.SORT_NONE:
433 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_NONE)
434 self.GetView().OnSort(SORT_NONE)
435 return True
436
437
438 #----------------------------------------------------------------------------
439 # Service specific methods
440 #----------------------------------------------------------------------------
441
442 def LoadOutline(self, view, position=-1, force=False):
443 if not self.GetView():
444 return
445
446 if hasattr(view, "DoLoadOutlineCallback"):
447 self.SaveExpansionState()
448 if view.DoLoadOutlineCallback(force=force):
449 self.GetView().OnSort(wx.ConfigBase_Get().ReadInt("OutlineSort", SORT_NONE))
450 self.LoadExpansionState()
451 if position >= 0:
452 self.SyncToPosition(position)
453
454
455 def SyncToPosition(self, position):
456 if not self.GetView():
457 return
458 self.GetView().GetTreeCtrl().SelectClosestItem(position)
459
460
461 def OnCloseFrame(self, event):
462 Service.Service.OnCloseFrame(self, event)
463 self.SaveExpansionState(clear = True)
464
465 return True
466
467
468 def SaveExpansionState(self, clear = False):
469 if clear:
470 expanded = []
471 elif self.GetView():
472 expanded = self.GetView().GetExpansionState()
473 wx.ConfigBase_Get().Write("OutlineLastExpanded", expanded.__repr__())
474
475
476 def LoadExpansionState(self):
477 expanded = wx.ConfigBase_Get().Read("OutlineLastExpanded")
478 if expanded:
479 self.GetView().SetExpansionState(eval(expanded))
480
481
482 #----------------------------------------------------------------------------
483 # Timer Methods
484 #----------------------------------------------------------------------------
485
486 def StartBackgroundTimer(self):
487 self._timer = wx.PyTimer(self.DoBackgroundRefresh)
488 self._timer.Start(250)
489
490
491 def DoBackgroundRefresh(self):
492 """ Refresh the outline view periodically """
493 self._timer.Stop()
494
495 foundRegisteredView = False
496 if self.GetView():
497 currView = wx.GetApp().GetDocumentManager().GetCurrentView()
498 if currView:
499 for viewType in self._validViewTypes:
500 if isinstance(currView, viewType):
501 self.LoadOutline(currView)
502 foundRegisteredView = True
503 break
504
505 if not foundRegisteredView:
506 self.GetView().ClearTreeCtrl()
507
508 self._timer.Start(1000) # 1 second interval
509
510
511 def AddViewTypeForBackgroundHandler(self, viewType):
512 self._validViewTypes.append(viewType)
513
514
515 def GetViewTypesForBackgroundHandler(self):
516 return self._validViewTypes
517
518
519 def RemoveViewTypeForBackgroundHandler(self, viewType):
520 self._validViewTypes.remove(viewType)
521