]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/samples/ide/activegrid/tool/OutlineService.py
Added the ActiveGrid IDE as a sample application
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / OutlineService.py
diff --git a/wxPython/samples/ide/activegrid/tool/OutlineService.py b/wxPython/samples/ide/activegrid/tool/OutlineService.py
new file mode 100644 (file)
index 0000000..95ccd9e
--- /dev/null
@@ -0,0 +1,520 @@
+#----------------------------------------------------------------------------
+# Name:         OutlineService.py
+# Purpose:      Outline View Service for pydocview
+#
+# Author:       Morgan Hua
+#
+# Created:      8/3/04
+# CVS-ID:       $Id$
+# Copyright:    (c) 2004-2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import wx
+import wx.lib.docview
+import wx.lib.pydocview
+import Service
+_ = wx.GetTranslation
+
+
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
+SORT_NONE = 0
+SORT_ASC = 1
+SORT_DESC = 2
+
+class OutlineView(Service.ServiceView):
+    """ Reusable Outline View for any document.
+        As a default, it uses a modified tree control (OutlineTreeCtrl) that allows sorting.
+        Subclass OutlineTreeCtrl to customize the tree control and call SetTreeCtrl to install a customized tree control.
+        When an item is selected, the document view is called back (with DoSelectCallback) to highlight and display the corresponding item in the document view.
+    """
+
+    #----------------------------------------------------------------------------
+    # Overridden methods
+    #----------------------------------------------------------------------------
+
+    def __init__(self, service):
+        Service.ServiceView.__init__(self, service)
+        self._actionOnSelect = True
+
+
+    def _CreateControl(self, parent, id):
+        treeCtrl = OutlineTreeCtrl(parent, id)
+        wx.EVT_TREE_SEL_CHANGED(treeCtrl, treeCtrl.GetId(), self.DoSelection)
+        wx.EVT_SET_FOCUS(treeCtrl, self.DoSelection)
+        wx.EVT_ENTER_WINDOW(treeCtrl, treeCtrl.CallDoLoadOutlineCallback)
+        wx.EVT_RIGHT_DOWN(treeCtrl, self.OnRightClick)
+
+        return treeCtrl
+
+
+    #----------------------------------------------------------------------------
+    # Service specific methods
+    #----------------------------------------------------------------------------
+
+    def OnRightClick(self, event):
+        menu = wx.Menu()
+
+        menu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
+        menu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
+        menu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
+
+        config = wx.ConfigBase_Get()
+        sort = config.ReadInt("OutlineSort", SORT_NONE)
+        if sort == SORT_NONE:
+            menu.Check(OutlineService.SORT_NONE, True)
+        elif sort == SORT_ASC:
+            menu.Check(OutlineService.SORT_ASC, True)
+        elif sort == SORT_DESC:
+            menu.Check(OutlineService.SORT_DESC, True)
+
+        self.GetControl().PopupMenu(menu, event.GetPosition())
+        menu.Destroy()
+
+
+    #----------------------------------------------------------------------------
+    # Tree Methods
+    #----------------------------------------------------------------------------
+
+    def DoSelection(self, event):
+        if not self._actionOnSelect:
+            return
+        item = self.GetControl().GetSelection()
+        if item:
+            self.GetControl().CallDoSelectCallback(item)
+
+
+    def ResumeActionOnSelect(self):
+        self._actionOnSelect = True
+
+
+    def StopActionOnSelect(self):
+        self._actionOnSelect = False
+
+
+    def SetTreeCtrl(self, tree):
+        self.SetControl(tree)
+        wx.EVT_TREE_SEL_CHANGED(self.GetControl(), self.GetControl().GetId(), self.DoSelection)
+        wx.EVT_ENTER_WINDOW(self.GetControl(), treeCtrl.CallDoLoadOutlineCallback)
+        wx.EVT_RIGHT_DOWN(self.GetControl(), self.OnRightClick)
+
+
+    def GetTreeCtrl(self):
+        return self.GetControl()
+
+
+    def OnSort(self, sortOrder):
+        treeCtrl = self.GetControl()
+        treeCtrl.SetSortOrder(sortOrder)
+        treeCtrl.SortAllChildren(treeCtrl.GetRootItem())
+
+
+    def ClearTreeCtrl(self):
+        if self.GetControl():
+            self.GetControl().DeleteAllItems()
+
+
+    def GetExpansionState(self):
+        expanded = []
+
+        treeCtrl = self.GetControl()
+        if not treeCtrl:
+            return expanded
+
+        parentItem = treeCtrl.GetRootItem()
+
+        if not parentItem:
+            return expanded
+
+        if not treeCtrl.IsExpanded(parentItem):
+            return expanded
+
+        expanded.append(treeCtrl.GetItemText(parentItem))
+
+        (child, cookie) = treeCtrl.GetFirstChild(parentItem)
+        while child.IsOk():
+            if treeCtrl.IsExpanded(child):
+                expanded.append(treeCtrl.GetItemText(child))
+            (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
+        return expanded
+
+
+    def SetExpansionState(self, expanded):
+        if not expanded or len(expanded) == 0:
+            return
+
+        treeCtrl = self.GetControl()
+        parentItem = treeCtrl.GetRootItem()
+        if expanded[0] != treeCtrl.GetItemText(parentItem):
+            return
+
+        (child, cookie) = treeCtrl.GetFirstChild(parentItem)
+        while child.IsOk():
+            if treeCtrl.GetItemText(child) in expanded:
+                treeCtrl.Expand(child)
+            (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
+
+        # 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
+        # self.GetControl().EnsureVisible(self.GetControl().GetRootItem())
+        # So doing the following massive hack which forces the treectrl to scroll up to the top item
+        treeCtrl.Collapse(parentItem)
+        treeCtrl.Expand(parentItem)
+
+
+class OutlineTreeCtrl(wx.TreeCtrl):
+    """ Default Tree Control Class for OutlineView.
+        This class has the added functionality of sorting by the labels
+    """
+
+
+    #----------------------------------------------------------------------------
+    # Constants
+    #----------------------------------------------------------------------------
+    ORIG_ORDER = 0
+    VIEW = 1
+    CALLBACKDATA = 2
+
+
+    #----------------------------------------------------------------------------
+    # Overridden Methods
+    #----------------------------------------------------------------------------
+
+    def __init__(self, parent, id, style=wx.TR_HAS_BUTTONS|wx.TR_DEFAULT_STYLE):
+        wx.TreeCtrl.__init__(self, parent, id, style = style)
+        self._origOrderIndex = 0
+        self._sortOrder = SORT_NONE
+
+
+    def DeleteAllItems(self):
+        self._origOrderIndex = 0
+        wx.TreeCtrl.DeleteAllItems(self)
+
+
+    #----------------------------------------------------------------------------
+    # Sort Methods
+    #----------------------------------------------------------------------------
+
+    def SetSortOrder(self, sortOrder = SORT_NONE):
+        """ Sort Order constants are defined at top of file """
+        self._sortOrder = sortOrder
+
+
+    def OnCompareItems(self, item1, item2):
+        if self._sortOrder == SORT_ASC:
+            return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower())  # sort A-Z
+        elif self._sortOrder == SORT_DESC:
+            return cmp(self.GetItemText(item2).lower(), self.GetItemText(item1).lower())  # sort Z-A
+        else:
+            return (self.GetPyData(item1)[self.ORIG_ORDER] > self.GetPyData(item2)[self.ORIG_ORDER]) # unsorted
+
+
+    def SortAllChildren(self, parentItem):
+        if parentItem and self.GetChildrenCount(parentItem, False):
+            self.SortChildren(parentItem)
+            (child, cookie) = self.GetFirstChild(parentItem)
+            while child.IsOk():
+                self.SortAllChildren(child)
+                (child, cookie) = self.GetNextChild(parentItem, cookie)
+
+
+    #----------------------------------------------------------------------------
+    # Select Callback Methods
+    #----------------------------------------------------------------------------
+
+    def CallDoSelectCallback(self, item):
+        """ Invoke the DoSelectCallback of the given view to highlight text in the document view
+        """
+        data = self.GetPyData(item)
+        if not data:
+            return
+
+        view = data[self.VIEW]
+        cbdata = data[self.CALLBACKDATA]
+        if view:
+            view.DoSelectCallback(cbdata)
+
+
+    def SelectClosestItem(self, position):
+        tree = self
+        distances = []
+        items = []
+        self.FindDistanceToTreeItems(tree.GetRootItem(), position, distances, items)
+        mindist = 1000000
+        mindex = -1
+        for index in range(0, len(distances)):
+            if distances[index] <= mindist:
+                mindist = distances[index]
+                mindex = index
+        if mindex != -1:
+            item = items[mindex]
+            self.EnsureVisible(item)
+            os_view = wx.GetApp().GetService(OutlineService).GetView()
+            if os_view:
+               os_view.StopActionOnSelect()
+            self.SelectItem(item)
+            if os_view:
+               os_view.ResumeActionOnSelect()
+
+
+    def FindDistanceToTreeItems(self, item, position, distances, items):
+        data = self.GetPyData(item)
+        this_dist = 1000000
+        if data and data[2]:
+            positionTuple = data[2]
+            if position >= positionTuple[1]:
+                items.append(item)
+                distances.append(position - positionTuple[1])
+
+        if self.ItemHasChildren(item):
+            child, cookie = self.GetFirstChild(item)
+            while child and child.IsOk():
+                self.FindDistanceToTreeItems(child, position, distances, items)
+                child, cookie = self.GetNextChild(item, cookie)
+        return False
+
+
+    def SetDoSelectCallback(self, item, view, callbackdata):
+        """ When an item in the outline view is selected,
+        a method is called to select the respective text in the document view.
+        The view must define the method DoSelectCallback(self, data) in order for this to work
+        """
+        self.SetPyData(item, (self._origOrderIndex, view, callbackdata))
+        self._origOrderIndex = self._origOrderIndex + 1
+
+
+    def CallDoLoadOutlineCallback(self, event):
+        """ Invoke the DoLoadOutlineCallback
+        """
+        rootItem = self.GetRootItem()
+        if rootItem:
+            data = self.GetPyData(rootItem)
+            if data:
+                view = data[self.VIEW]
+                if view and view.DoLoadOutlineCallback():
+                    self.SortAllChildren(self.GetRootItem())
+
+
+    def GetCallbackView(self):
+        rootItem = self.GetRootItem()
+        if rootItem:
+            return self.GetPyData(rootItem)[self.VIEW]
+        else:
+            return None
+
+
+class OutlineService(Service.Service):
+
+
+    #----------------------------------------------------------------------------
+    # Constants
+    #----------------------------------------------------------------------------
+    SHOW_WINDOW = wx.NewId()  # keep this line for each subclass, need unique ID for each Service
+    SORT = wx.NewId()
+    SORT_ASC = wx.NewId()
+    SORT_DESC = wx.NewId()
+    SORT_NONE = wx.NewId()
+
+
+    #----------------------------------------------------------------------------
+    # Overridden methods
+    #----------------------------------------------------------------------------
+
+    def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
+        Service.Service.__init__(self, serviceName, embeddedWindowLocation)
+        self._validTemplates = []
+
+
+    def _CreateView(self):
+        return OutlineView(self)
+
+
+    def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
+        Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
+
+        wx.EVT_MENU(frame, OutlineService.SORT_ASC, frame.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, OutlineService.SORT_ASC, frame.ProcessUpdateUIEvent)
+        wx.EVT_MENU(frame, OutlineService.SORT_DESC, frame.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, OutlineService.SORT_DESC, frame.ProcessUpdateUIEvent)
+        wx.EVT_MENU(frame, OutlineService.SORT_NONE, frame.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, OutlineService.SORT_NONE, frame.ProcessUpdateUIEvent)
+
+
+        if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
+            return True
+
+        viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
+        self._outlineSortMenu = wx.Menu()
+        self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
+        self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
+        self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
+        viewMenu.AppendMenu(wx.NewId(), _("Outline Sort"), self._outlineSortMenu)
+
+        return True
+
+
+    #----------------------------------------------------------------------------
+    # Event Processing Methods
+    #----------------------------------------------------------------------------
+
+    def ProcessEvent(self, event):
+        if Service.Service.ProcessEvent(self, event):
+            return True
+
+        id = event.GetId()
+        if id == OutlineService.SORT_ASC:
+            self.OnSort(event)
+            return True
+        elif id == OutlineService.SORT_DESC:
+            self.OnSort(event)
+            return True
+        elif id == OutlineService.SORT_NONE:
+            self.OnSort(event)
+            return True
+        else:
+            return False
+
+
+    def ProcessUpdateUIEvent(self, event):
+        if Service.Service.ProcessUpdateUIEvent(self, event):
+            return True
+
+        id = event.GetId()
+        if id == OutlineService.SORT_ASC:
+            event.Enable(True)
+
+            config = wx.ConfigBase_Get()
+            sort = config.ReadInt("OutlineSort", SORT_NONE)
+            if sort == SORT_ASC:
+                self._outlineSortMenu.Check(OutlineService.SORT_ASC, True)
+            else:
+                self._outlineSortMenu.Check(OutlineService.SORT_ASC, False)
+
+            return True
+        elif id == OutlineService.SORT_DESC:
+            event.Enable(True)
+
+            config = wx.ConfigBase_Get()
+            sort = config.ReadInt("OutlineSort", SORT_NONE)
+            if sort == SORT_DESC:
+                self._outlineSortMenu.Check(OutlineService.SORT_DESC, True)
+            else:
+                self._outlineSortMenu.Check(OutlineService.SORT_DESC, False)
+
+            return True
+        elif id == OutlineService.SORT_NONE:
+            event.Enable(True)
+
+            config = wx.ConfigBase_Get()
+            sort = config.ReadInt("OutlineSort", SORT_NONE)
+            if sort == SORT_NONE:
+                self._outlineSortMenu.Check(OutlineService.SORT_NONE, True)
+            else:
+                self._outlineSortMenu.Check(OutlineService.SORT_NONE, False)
+
+            return True
+        else:
+            return False
+
+
+    def OnSort(self, event):
+        id = event.GetId()
+        if id == OutlineService.SORT_ASC:
+            wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_ASC)
+            self.GetView().OnSort(SORT_ASC)
+            return True
+        elif id == OutlineService.SORT_DESC:
+            wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_DESC)
+            self.GetView().OnSort(SORT_DESC)
+            return True
+        elif id == OutlineService.SORT_NONE:
+            wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_NONE)
+            self.GetView().OnSort(SORT_NONE)
+            return True
+
+
+    #----------------------------------------------------------------------------
+    # Service specific methods
+    #----------------------------------------------------------------------------
+
+    def LoadOutline(self, view, position=-1, force=False):
+        if not self.GetView():
+            return
+
+        self.SaveExpansionState()
+        if view.DoLoadOutlineCallback(force=force):
+            self.GetView().OnSort(wx.ConfigBase_Get().ReadInt("OutlineSort", SORT_NONE))
+            self.LoadExpansionState()
+        if position >= 0:
+            self.SyncToPosition(position)
+
+
+    def SyncToPosition(self, position):
+        if not self.GetView():
+            return
+        self.GetView().GetTreeCtrl().SelectClosestItem(position)
+
+
+    def OnCloseFrame(self, event):
+        Service.Service.OnCloseFrame(self, event)
+        self.SaveExpansionState(clear = True)
+
+        return True
+
+
+    def SaveExpansionState(self, clear = False):
+        if clear:
+            expanded = []
+        elif self.GetView():
+            expanded = self.GetView().GetExpansionState()
+        wx.ConfigBase_Get().Write("OutlineLastExpanded", expanded.__repr__())
+
+
+    def LoadExpansionState(self):
+        expanded = wx.ConfigBase_Get().Read("OutlineLastExpanded")
+        if expanded:
+            self.GetView().SetExpansionState(eval(expanded))
+
+
+    #----------------------------------------------------------------------------
+    # Timer Methods
+    #----------------------------------------------------------------------------
+
+    def StartBackgroundTimer(self):
+        self._timer = wx.PyTimer(self.DoBackgroundRefresh)
+        self._timer.Start(250)
+
+
+    def DoBackgroundRefresh(self):
+        """ Refresh the outline view periodically """
+        self._timer.Stop()
+        
+        foundRegisteredView = False
+        if self.GetView():
+            currView = wx.GetApp().GetDocumentManager().GetCurrentView()
+            if currView:
+                for template in self._validTemplates:
+                    type = template.GetViewType()
+                    if isinstance(currView, type):
+                        self.LoadOutline(currView)
+                        foundRegisteredView = True
+                        break
+
+                if not foundRegisteredView:
+                    self.GetView().ClearTreeCtrl()
+                    
+        self._timer.Start(1000) # 1 second interval
+
+
+    def AddTemplateForBackgroundHandler(self, template):
+        self._validTemplates.append(template)
+
+
+    def GetTemplatesForBackgroundHandler(self):
+        return self._validTemplates
+
+
+    def RemoveTemplateForBackgroundHandler(self, template):
+        self._validTemplates.remove(template)
+