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