]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/OutlineService.py
858fbade75b3d1c772de4acf2be3135b097eaa70
[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
88
89 def ResumeActionOnSelect(self):
90 self._actionOnSelect = True
91
92
93 def StopActionOnSelect(self):
94 self._actionOnSelect = False
95
96
97 def SetTreeCtrl(self, tree):
98 self.SetControl(tree)
99 wx.EVT_TREE_SEL_CHANGED(self.GetControl(), self.GetControl().GetId(), self.DoSelection)
100 wx.EVT_ENTER_WINDOW(self.GetControl(), treeCtrl.CallDoLoadOutlineCallback)
101 wx.EVT_RIGHT_DOWN(self.GetControl(), self.OnRightClick)
102
103
104 def GetTreeCtrl(self):
105 return self.GetControl()
106
107
108 def OnSort(self, sortOrder):
109 treeCtrl = self.GetControl()
110 treeCtrl.SetSortOrder(sortOrder)
111 treeCtrl.SortAllChildren(treeCtrl.GetRootItem())
112
113
114 def ClearTreeCtrl(self):
115 if self.GetControl():
116 self.GetControl().DeleteAllItems()
117
118
119 def GetExpansionState(self):
120 expanded = []
121
122 treeCtrl = self.GetControl()
123 if not treeCtrl:
124 return expanded
125
126 parentItem = treeCtrl.GetRootItem()
127
128 if not parentItem:
129 return expanded
130
131 if not treeCtrl.IsExpanded(parentItem):
132 return expanded
133
134 expanded.append(treeCtrl.GetItemText(parentItem))
135
136 (child, cookie) = treeCtrl.GetFirstChild(parentItem)
137 while child.IsOk():
138 if treeCtrl.IsExpanded(child):
139 expanded.append(treeCtrl.GetItemText(child))
140 (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
141 return expanded
142
143
144 def SetExpansionState(self, expanded):
145 if not expanded or len(expanded) == 0:
146 return
147
148 treeCtrl = self.GetControl()
149 parentItem = treeCtrl.GetRootItem()
150 if expanded[0] != treeCtrl.GetItemText(parentItem):
151 return
152
153 (child, cookie) = treeCtrl.GetFirstChild(parentItem)
154 while child.IsOk():
155 if treeCtrl.GetItemText(child) in expanded:
156 treeCtrl.Expand(child)
157 (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
158
159 # wxBug: This causes a crash, tried using ScrollTo which crashed as well. Then tried calling it with wx.CallAfter and that crashed as well, with both EnsureVisible and ScrollTo
160 # self.GetControl().EnsureVisible(self.GetControl().GetRootItem())
161 # So doing the following massive hack which forces the treectrl to scroll up to the top item
162 treeCtrl.Collapse(parentItem)
163 treeCtrl.Expand(parentItem)
164
165
166 class OutlineTreeCtrl(wx.TreeCtrl):
167 """ Default Tree Control Class for OutlineView.
168 This class has the added functionality of sorting by the labels
169 """
170
171
172 #----------------------------------------------------------------------------
173 # Constants
174 #----------------------------------------------------------------------------
175 ORIG_ORDER = 0
176 VIEW = 1
177 CALLBACKDATA = 2
178
179
180 #----------------------------------------------------------------------------
181 # Overridden Methods
182 #----------------------------------------------------------------------------
183
184 def __init__(self, parent, id, style=wx.TR_HAS_BUTTONS|wx.TR_DEFAULT_STYLE):
185 wx.TreeCtrl.__init__(self, parent, id, style = style)
186 self._origOrderIndex = 0
187 self._sortOrder = SORT_NONE
188
189
190 def DeleteAllItems(self):
191 self._origOrderIndex = 0
192 wx.TreeCtrl.DeleteAllItems(self)
193
194
195 #----------------------------------------------------------------------------
196 # Sort Methods
197 #----------------------------------------------------------------------------
198
199 def SetSortOrder(self, sortOrder = SORT_NONE):
200 """ Sort Order constants are defined at top of file """
201 self._sortOrder = sortOrder
202
203
204 def OnCompareItems(self, item1, item2):
205 if self._sortOrder == SORT_ASC:
206 return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower()) # sort A-Z
207 elif self._sortOrder == SORT_DESC:
208 return cmp(self.GetItemText(item2).lower(), self.GetItemText(item1).lower()) # sort Z-A
209 else:
210 return (self.GetPyData(item1)[self.ORIG_ORDER] > self.GetPyData(item2)[self.ORIG_ORDER]) # unsorted
211
212
213 def SortAllChildren(self, parentItem):
214 if parentItem and self.GetChildrenCount(parentItem, False):
215 self.SortChildren(parentItem)
216 (child, cookie) = self.GetFirstChild(parentItem)
217 while child.IsOk():
218 self.SortAllChildren(child)
219 (child, cookie) = self.GetNextChild(parentItem, cookie)
220
221
222 #----------------------------------------------------------------------------
223 # Select Callback Methods
224 #----------------------------------------------------------------------------
225
226 def CallDoSelectCallback(self, item):
227 """ Invoke the DoSelectCallback of the given view to highlight text in the document view
228 """
229 data = self.GetPyData(item)
230 if not data:
231 return
232
233 view = data[self.VIEW]
234 cbdata = data[self.CALLBACKDATA]
235 if view:
236 view.DoSelectCallback(cbdata)
237
238
239 def SelectClosestItem(self, position):
240 tree = self
241 distances = []
242 items = []
243 self.FindDistanceToTreeItems(tree.GetRootItem(), position, distances, items)
244 mindist = 1000000
245 mindex = -1
246 for index in range(0, len(distances)):
247 if distances[index] <= mindist:
248 mindist = distances[index]
249 mindex = index
250 if mindex != -1:
251 item = items[mindex]
252 self.EnsureVisible(item)
253 os_view = wx.GetApp().GetService(OutlineService).GetView()
254 if os_view:
255 os_view.StopActionOnSelect()
256 self.SelectItem(item)
257 if os_view:
258 os_view.ResumeActionOnSelect()
259
260
261 def FindDistanceToTreeItems(self, item, position, distances, items):
262 data = self.GetPyData(item)
263 this_dist = 1000000
264 if data and data[2]:
265 positionTuple = data[2]
266 if position >= positionTuple[1]:
267 items.append(item)
268 distances.append(position - positionTuple[1])
269
270 if self.ItemHasChildren(item):
271 child, cookie = self.GetFirstChild(item)
272 while child and child.IsOk():
273 self.FindDistanceToTreeItems(child, position, distances, items)
274 child, cookie = self.GetNextChild(item, cookie)
275 return False
276
277
278 def SetDoSelectCallback(self, item, view, callbackdata):
279 """ When an item in the outline view is selected,
280 a method is called to select the respective text in the document view.
281 The view must define the method DoSelectCallback(self, data) in order for this to work
282 """
283 self.SetPyData(item, (self._origOrderIndex, view, callbackdata))
284 self._origOrderIndex = self._origOrderIndex + 1
285
286
287 def CallDoLoadOutlineCallback(self, event):
288 """ Invoke the DoLoadOutlineCallback
289 """
290 rootItem = self.GetRootItem()
291 if rootItem:
292 data = self.GetPyData(rootItem)
293 if data:
294 view = data[self.VIEW]
295 if view and view.DoLoadOutlineCallback():
296 self.SortAllChildren(self.GetRootItem())
297
298
299 def GetCallbackView(self):
300 rootItem = self.GetRootItem()
301 if rootItem:
302 return self.GetPyData(rootItem)[self.VIEW]
303 else:
304 return None
305
306
307 class OutlineService(Service.Service):
308
309
310 #----------------------------------------------------------------------------
311 # Constants
312 #----------------------------------------------------------------------------
313 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
314 SORT = wx.NewId()
315 SORT_ASC = wx.NewId()
316 SORT_DESC = wx.NewId()
317 SORT_NONE = wx.NewId()
318
319
320 #----------------------------------------------------------------------------
321 # Overridden methods
322 #----------------------------------------------------------------------------
323
324 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
325 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
326 self._validTemplates = []
327
328
329 def _CreateView(self):
330 return OutlineView(self)
331
332
333 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
334 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
335
336 wx.EVT_MENU(frame, OutlineService.SORT_ASC, frame.ProcessEvent)
337 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_ASC, frame.ProcessUpdateUIEvent)
338 wx.EVT_MENU(frame, OutlineService.SORT_DESC, frame.ProcessEvent)
339 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_DESC, frame.ProcessUpdateUIEvent)
340 wx.EVT_MENU(frame, OutlineService.SORT_NONE, frame.ProcessEvent)
341 wx.EVT_UPDATE_UI(frame, OutlineService.SORT_NONE, frame.ProcessUpdateUIEvent)
342
343
344 if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
345 return True
346
347 viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
348 self._outlineSortMenu = wx.Menu()
349 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
350 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
351 self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
352 viewMenu.AppendMenu(wx.NewId(), _("Outline Sort"), self._outlineSortMenu)
353
354 return True
355
356
357 #----------------------------------------------------------------------------
358 # Event Processing Methods
359 #----------------------------------------------------------------------------
360
361 def ProcessEvent(self, event):
362 if Service.Service.ProcessEvent(self, event):
363 return True
364
365 id = event.GetId()
366 if id == OutlineService.SORT_ASC:
367 self.OnSort(event)
368 return True
369 elif id == OutlineService.SORT_DESC:
370 self.OnSort(event)
371 return True
372 elif id == OutlineService.SORT_NONE:
373 self.OnSort(event)
374 return True
375 else:
376 return False
377
378
379 def ProcessUpdateUIEvent(self, event):
380 if Service.Service.ProcessUpdateUIEvent(self, event):
381 return True
382
383 id = event.GetId()
384 if id == OutlineService.SORT_ASC:
385 event.Enable(True)
386
387 config = wx.ConfigBase_Get()
388 sort = config.ReadInt("OutlineSort", SORT_NONE)
389 if sort == SORT_ASC:
390 self._outlineSortMenu.Check(OutlineService.SORT_ASC, True)
391 else:
392 self._outlineSortMenu.Check(OutlineService.SORT_ASC, False)
393
394 return True
395 elif id == OutlineService.SORT_DESC:
396 event.Enable(True)
397
398 config = wx.ConfigBase_Get()
399 sort = config.ReadInt("OutlineSort", SORT_NONE)
400 if sort == SORT_DESC:
401 self._outlineSortMenu.Check(OutlineService.SORT_DESC, True)
402 else:
403 self._outlineSortMenu.Check(OutlineService.SORT_DESC, False)
404
405 return True
406 elif id == OutlineService.SORT_NONE:
407 event.Enable(True)
408
409 config = wx.ConfigBase_Get()
410 sort = config.ReadInt("OutlineSort", SORT_NONE)
411 if sort == SORT_NONE:
412 self._outlineSortMenu.Check(OutlineService.SORT_NONE, True)
413 else:
414 self._outlineSortMenu.Check(OutlineService.SORT_NONE, False)
415
416 return True
417 else:
418 return False
419
420
421 def OnSort(self, event):
422 id = event.GetId()
423 if id == OutlineService.SORT_ASC:
424 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_ASC)
425 self.GetView().OnSort(SORT_ASC)
426 return True
427 elif id == OutlineService.SORT_DESC:
428 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_DESC)
429 self.GetView().OnSort(SORT_DESC)
430 return True
431 elif id == OutlineService.SORT_NONE:
432 wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_NONE)
433 self.GetView().OnSort(SORT_NONE)
434 return True
435
436
437 #----------------------------------------------------------------------------
438 # Service specific methods
439 #----------------------------------------------------------------------------
440
441 def LoadOutline(self, view, position=-1, force=False):
442 if not self.GetView():
443 return
444
445 if hasattr(view, "DoLoadOutlineCallback"):
446 self.SaveExpansionState()
447 if view.DoLoadOutlineCallback(force=force):
448 self.GetView().OnSort(wx.ConfigBase_Get().ReadInt("OutlineSort", SORT_NONE))
449 self.LoadExpansionState()
450 if position >= 0:
451 self.SyncToPosition(position)
452
453
454 def SyncToPosition(self, position):
455 if not self.GetView():
456 return
457 self.GetView().GetTreeCtrl().SelectClosestItem(position)
458
459
460 def OnCloseFrame(self, event):
461 Service.Service.OnCloseFrame(self, event)
462 self.SaveExpansionState(clear = True)
463
464 return True
465
466
467 def SaveExpansionState(self, clear = False):
468 if clear:
469 expanded = []
470 elif self.GetView():
471 expanded = self.GetView().GetExpansionState()
472 wx.ConfigBase_Get().Write("OutlineLastExpanded", expanded.__repr__())
473
474
475 def LoadExpansionState(self):
476 expanded = wx.ConfigBase_Get().Read("OutlineLastExpanded")
477 if expanded:
478 self.GetView().SetExpansionState(eval(expanded))
479
480
481 #----------------------------------------------------------------------------
482 # Timer Methods
483 #----------------------------------------------------------------------------
484
485 def StartBackgroundTimer(self):
486 self._timer = wx.PyTimer(self.DoBackgroundRefresh)
487 self._timer.Start(250)
488
489
490 def DoBackgroundRefresh(self):
491 """ Refresh the outline view periodically """
492 self._timer.Stop()
493
494 foundRegisteredView = False
495 if self.GetView():
496 currView = wx.GetApp().GetDocumentManager().GetCurrentView()
497 if currView:
498 for template in self._validTemplates:
499 type = template.GetViewType()
500 if isinstance(currView, type):
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 AddTemplateForBackgroundHandler(self, template):
512 self._validTemplates.append(template)
513
514
515 def GetTemplatesForBackgroundHandler(self):
516 return self._validTemplates
517
518
519 def RemoveTemplateForBackgroundHandler(self, template):
520 self._validTemplates.remove(template)
521