Beispiel #1
0
    def initLayout(self):
        ModularStatusBar(self)
        if self.classprefs.show_popup_status:
            self.popup_status = PopupStatusBar(self)
        else:
            self.popup_status = None

        hsplit = wx.BoxSizer(wx.HORIZONTAL)
        self.SetAutoLayout(True)
        self.SetSizer(hsplit)

        self.spring = SpringTabs(self)

        # tell FrameManager to manage this frame
        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        self.tabs = FrameNotebook(self)
        self._mgr.AddPane(self.tabs,
                          aui.AuiPaneInfo().Name("notebook").CenterPane())
        self.sidebar_panes = []

        # paneinfo can use the C++ style notation for setting flags because
        # each method of AuiPaneInfo returns itself
        paneinfo = aui.AuiPaneInfo().Name("SpringTabs").Caption(
            "SpringTabs").Left().Layer(10).CloseButton(False).CaptionVisible(
                False).LeftDockable(False).RightDockable(False)

        # Stock wxPython distributed with OS X 10.5 is version 2.8.4.0, which
        # apparently doesn't have the DockFixed method.  So, I check for that
        # here before using it.
        if hasattr(paneinfo, 'DockFixed'):
            paneinfo.DockFixed()
        self._mgr.AddPane(self.spring, paneinfo)
Beispiel #2
0
    def initLayout(self):
        ModularStatusBar(self)
        if self.classprefs.show_popup_status:
            self.popup_status = PopupStatusBar(self)
        else:
            self.popup_status = None

        hsplit = wx.BoxSizer(wx.HORIZONTAL)
        self.SetAutoLayout(True)
        self.SetSizer(hsplit)
        
        self.spring = SpringTabs(self)

        # tell FrameManager to manage this frame        
        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        self.tabs = FrameNotebook(self)
        self._mgr.AddPane(self.tabs, aui.AuiPaneInfo().Name("notebook").
                          CenterPane())
        self.sidebar_panes = []
        
        # paneinfo can use the C++ style notation for setting flags because
        # each method of AuiPaneInfo returns itself
        paneinfo = aui.AuiPaneInfo().Name("SpringTabs").Caption("SpringTabs").Left().Layer(10).CloseButton(False).CaptionVisible(False).LeftDockable(False).RightDockable(False)
        
        # Stock wxPython distributed with OS X 10.5 is version 2.8.4.0, which
        # apparently doesn't have the DockFixed method.  So, I check for that
        # here before using it.
        if hasattr(paneinfo, 'DockFixed'):
            paneinfo.DockFixed()
        self._mgr.AddPane(self.spring, paneinfo)
Beispiel #3
0
class BufferFrame(wx.Frame, ClassPrefs, debugmixin):
    frameid = 0
    load_error_count = 0
    size_timer = None

    preferences_tab = "General"
    preferences_label = "Windows"
    if wx.Platform == "__WXMAC__":
        icon = "icons/application_osx.png"
    else:
        icon = "icons/application.png"

    perspectives = {}

    default_classprefs = (
        IntParam('width', 800, 'Width of the main frame in pixels'),
        IntParam('height', 600, 'Height of the main frame in pixels'),
        BoolParam(
            'show_toolbar', True,
            'Show the toolbar on all frames?\nNote: this is a global setting for all frames.'
        ),
        BoolParam(
            'resize_becomes_default', True,
            'Should the new size following a resize become the default size of all subsequently created frames?'
        ),
        BoolParam('show_popup_status', False,
                  'Use experimental popup status bar'),
        # FIXME: this attempt at fast resizing caused Freeze() mismatch
        # problems.  It's not worth that much, so I'm disabling it for
        # now.
        # BoolParam('fast_resize', False, 'Speed up resize events by deferring window\nrepaints until the mouse stops moving'),
    )

    default_menubar = {
        _("File"): 0,
        _("File/Export"): 850,  # position within the parent menu for submenus
        _("Edit"): 0.001,
        _("View"): 0.003,
        _("View/Apply Settings"): -980,
        _("Tools"): 0.004,
        _("Transform"): 0.005,
        _("Project"): 1000.05,
        _("Documents"): 1000.1,
        _("Window"): 1000.2,
        _("&Help"): 1000.3,
    }

    def __init__(self, urls=[], id=-1, buffer=None):
        BufferFrame.frameid += 1
        self.name = "peppy: Window #%d" % BufferFrame.frameid

        # flags to prevent multiple timestamp checks
        self.pending_timestamp_check = False
        self.currently_processing_timestamp = False

        # initialize superclass
        pos, size = self.initPositionAndSize()
        wx.Frame.__init__(self,
                          None,
                          id=-1,
                          title=self.name,
                          pos=pos,
                          size=size,
                          style=wx.DEFAULT_FRAME_STYLE | wx.CLIP_CHILDREN)

        self.initIcon()
        self.initLayout()
        self.initMenu()
        self.loadSidebars()
        self.initEventBindings()
        self.initLoad(buffer, urls)
        self.initRegisterWindow()
        self.Show()

    def initPositionAndSize(self):
        pos = wx.DefaultPosition
        size = (int(self.classprefs.width), int(self.classprefs.height))
        return pos, size

    def initIcon(self):
        icon = wx.EmptyIcon()
        icon.CopyFromBitmap(getIconBitmap('icons/peppy.png'))
        self.SetIcon(icon)

    def initLayout(self):
        ModularStatusBar(self)
        if self.classprefs.show_popup_status:
            self.popup_status = PopupStatusBar(self)
        else:
            self.popup_status = None

        hsplit = wx.BoxSizer(wx.HORIZONTAL)
        self.SetAutoLayout(True)
        self.SetSizer(hsplit)

        self.spring = SpringTabs(self)

        # tell FrameManager to manage this frame
        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        self.tabs = FrameNotebook(self)
        self._mgr.AddPane(self.tabs,
                          aui.AuiPaneInfo().Name("notebook").CenterPane())
        self.sidebar_panes = []

        # paneinfo can use the C++ style notation for setting flags because
        # each method of AuiPaneInfo returns itself
        paneinfo = aui.AuiPaneInfo().Name("SpringTabs").Caption(
            "SpringTabs").Left().Layer(10).CloseButton(False).CaptionVisible(
                False).LeftDockable(False).RightDockable(False)

        # Stock wxPython distributed with OS X 10.5 is version 2.8.4.0, which
        # apparently doesn't have the DockFixed method.  So, I check for that
        # here before using it.
        if hasattr(paneinfo, 'DockFixed'):
            paneinfo.DockFixed()
        self._mgr.AddPane(self.spring, paneinfo)

    def initMenu(self):
        UserActionClassList.setDefaultMenuBarWeights(self.default_menubar)
        menubar = wx.MenuBar()
        if wx.Platform == '__WXMAC__':
            # turn off the automatic generation of the Window menu.
            # We generate one ourselves
            wx.MenuBar.SetAutoWindowMenu(False)
            # Replace the system Help menu with ours by creating this small
            # fake menu in order to register the help menu.  Note that you
            # have to add something to the menubar on the mac (i.e.  the
            # separator) in order for it to actually register with the Mac
            # menu system as being present.  An empty Help menu is ignored as
            # far as SetMacHelpMenuTitleName is concerned.
            help = wx.Menu()
            help.AppendSeparator()
            menubar.Append(help, _("&Help"))
            wx.GetApp().SetMacHelpMenuTitleName(_("&Help"))
        self.SetMenuBar(menubar)

        self.show_toolbar = self.classprefs.show_toolbar
        self.root_accel = Null()

    def initEventBindings(self):
        self.dropTarget = FrameDropTarget(self)
        self.SetDropTarget(self.dropTarget)

        Publisher().subscribe(self.pluginsChanged, 'peppy.plugins.changed')
        wx.GetApp().SetTopWindow(self)
        self.bindEvents()

    def initLoad(self, buffer, urls):
        if buffer:
            self.newBuffer(buffer)
        else:
            self.loadList(urls)

    def initRegisterWindow(self):
        WindowList.append(self)

    def __str__(self):
        return "%s %s id=%s" % (self.__class__.__name__, self.name,
                                hex(id(self)))

    def isOSXMinimalMenuFrame(self):
        return False

    def bindEvents(self):
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_ACTIVATE, self.OnRaise)
        self.Bind(wx.EVT_SIZE, self.OnSize)

    def unbindEvents(self):
        self.Unbind(wx.EVT_CLOSE)
        self.Unbind(wx.EVT_ACTIVATE)
        self.Unbind(wx.EVT_SIZE)

    def loadList(self, urls):
        if urls:
            wx.CallAfter(self.openList, urls)
        else:
            wx.CallAfter(self.titleBuffer)

    def openList(self, urls):
        self.tabs.holdChanges()
        for url in urls:
            #dprint("Opening %s" % url)
            self.open(url, force_new_tab=True)
        # If no pages have been open (due to errors), make sure that a title
        # buffer is displayed.
        if self.tabs.GetPageCount() == 0:
            self.titleBuffer()
        self.tabs.processChanges()

    def addPane(self, win, paneinfo):
        self._mgr.AddPane(win, paneinfo)

    def loadSidebars(self):
        sidebars = Sidebar.getClasses(self)
        for sidebarcls in sidebars:
            if sidebarcls.classprefs.springtab:
                self.spring.addTab(sidebarcls.caption, sidebarcls)
            else:
                self.createSidebar(sidebarcls)
        self.createSidebarList()

    def createSidebar(self, sidebarcls):
        """Create the sidebar and register it with the frame's AUI
        Manager."""

        sidebar = sidebarcls(self)
        paneinfo = sidebar.getPaneInfo()
        self._mgr.AddPane(sidebar, paneinfo)
        sidebar.paneinfo = self._mgr.GetPane(sidebar)
        sidebar.activateSidebar()

    def createSidebarList(self):
        self.sidebar_panes = []
        sidebars = self._mgr.GetAllPanes()
        for sidebar in sidebars:
            assert self.dprint(
                "name=%s caption=%s window=%s state=%s" %
                (sidebar.name, sidebar.caption, sidebar.window, sidebar.state))
            if sidebar.name != "notebook":
                self.sidebar_panes.append(sidebar)
        self.sidebar_panes.sort(key=lambda s: s.caption)

    def closeSidebar(self):
        self.dprint("closing springtabs")
        self.spring.deleteTabs()
        for sidebar in self.sidebar_panes:
            self.dprint("closing sidebar %s" % sidebar.window)
            self._mgr.DetachPane(sidebar.window)
            sidebar.window.Destroy()
            sidebar.window = None
        sidebars = self._mgr.GetAllPanes()
        self.dprint("sidebars remaining: %s" % sidebars)
        self.sidebar_panes = None

    def processNormalIdleEvent(self):
        """Event processing for low priority idle functions.
        
        This event processor is called from the main application's idle event
        handler L{Peppy.OnIdle} with a minimum time between calls to this
        method.  This is for expensive idle event functions or functions that
        don't need to be called that often to still provide good feedback to
        the user.
        """
        if not self.IsActive():
            self.dprint("Top window %s not active.  No idle events." % self)
            return
        mode = self.getActiveMajorMode()
        if mode and mode.isReadyForIdleEvents():
            # Refs #665: this call to forceToolbarUpdate causes the yield to
            # freeze until tabs get switched.  Now this only happens when the
            # mode is ready for idle event processing
            self.root_accel.forceToolbarUpdate()

            # Timestamp checks are performed here for two reasons: 1) windows
            # reports activate events whenever a dialog is popped up, so
            # you get an unending stream of dialogs.  2) when trying to use
            # dialogs after changing tabs on linux, the mouse events seemed to
            # get eaten.  The only workaround I could find was to put the call
            # to isModeChangedOnDisk here wrapped by a CallAfter.
            if self.pending_timestamp_check and not self.currently_processing_timestamp:
                self.currently_processing_timestamp = True
                wx.CallAfter(self.isModeChangedOnDisk)
                self.pending_timestamp_check = False
        #else:
        #    dprint("mode %s not ready for idle events" % mode)

    def processPriorityIdleEvent(self):
        """Event processing that happens at every idle event
        
        This event processor is for high priority idle events that should be
        called as often as possible.  Like L{processNormalIdleEvent}, this
        method is called from L{Peppy.OnIdle} but instead is called at every
        idle event.
        
        Compared to the calls in L{processNormalIdleEvent}, the functions
        called by this method should strive not to use much processing time
        because delays here will be much more noticeable to the user.
        """
        if not self.IsActive():
            self.dprint("Top window %s not active.  No idle events." % self)
            return
        mode = self.getActiveMajorMode()
        if mode and mode.isReadyForIdleEvents():
            #dprint("Idle for mode %s" % mode)
            mode.idleHandler()

    # Overrides of wx methods
    def OnRaise(self, evt):
        self.dprint("Focus! %s" % self.name)
        if evt.GetActive():
            wx.GetApp().SetTopWindow(self)
            if not self.pending_timestamp_check and not self.currently_processing_timestamp:
                self.pending_timestamp_check = True
        else:
            if self.popup_status:
                self.popup_status.clear()
        evt.Skip()

    def OnSize(self, evt):
        # FIXME: this fast resizing was causing problems, so I've disabled it
        # with the embedded False
        if False and self.classprefs.fast_resize:
            if not self.tabs.IsFrozen():
                dprint("not frozen.  Freezing")
                self.tabs.Freeze()
            if not self.__class__.size_timer:
                self.__class__.size_timer = wx.PyTimer(self.OnSizeTimer)
            self.__class__.size_timer.Start(50, oneShot=True)
        else:
            self.rememberFrameSize()
            evt.Skip()

    def rememberFrameSize(self):
        if self.classprefs.resize_becomes_default:
            size = self.GetSize()
            self.classprefs.width = size.GetWidth()
            self.classprefs.height = size.GetHeight()

    def OnSizeTimer(self, evt=None):
        if self.tabs.IsFrozen():
            dprint("frozen.  Thawing")
            # FIXME: for some reason, IsFrozen returns True even when it's
            # not frozen.
            self.tabs.Thaw()

    def OnClose(self, evt=None):
        assert self.dprint(evt)
        if WindowList.canCloseWindow(self):
            self.closeWindow()
        WindowList.resetMacMinimalMenu()

    def Raise(self):
        wx.Frame.Raise(self)
        major = self.getActiveMajorMode()
        if major is not None:
            major.SetFocus()

    def SetStatusText(self, text, index=0, hold=False):
        """Overrides status bar with popup status.
        
        Instead of taking up screen real-estate with a status bar, the
        popup status displays a message for a small amount of time before
        disappearing.  Depending on the value of index, a message can either
        remain on screen until the time expires, or can be overwritten by the
        next call to SetStatusText.
        
        Depending on the value of hold, a message can either remain on screen
        until the time expires, or can be overwritten by the next call to
        SetStatusText.

        @param text: the text (unicode) to display in the popup message
        
        @param index: 0 uses L{PopupStatusBar.showMessage} to display the text
        in the popup and will remain on screen till it expires; anything else
        uses L{PopupStatusBar.showStatusText} to display status info that gets
        overwritten with the next call.
        
        @param hold: if True, ignores expiration time and displays the status
        message until the next status message is displayed.
        """
        if text:
            if self.popup_status:
                if index == 0 and not hold:
                    self.popup_status.showMessage(_(text))
                else:
                    self.popup_status.showStatusText(_(text), hold)
            else:
                self.GetStatusBar().SetStatusText(_(text), index)

    # non-wx methods

    def showErrorDialog(self, msg, title="Error"):
        dlg = wx.MessageDialog(self, msg, title, wx.OK | wx.ICON_ERROR)
        retval = dlg.ShowModal()
        return retval

    def showWarningDialog(self, msg, title="Warning"):
        dlg = wx.MessageDialog(self, msg, title, wx.OK | wx.ICON_WARNING)
        retval = dlg.ShowModal()
        return retval

    def showQuestionDialog(self, msg, title="Question"):
        dlg = wx.MessageDialog(self, msg, title, wx.YES_NO | wx.ICON_QUESTION)
        retval = dlg.ShowModal()
        return retval

    def clearMenumap(self):
        self.root_accel.cleanupAndDelete()

    def setMenumap(self, mode):
        self.clearMenumap()
        self.root_accel = UserAccelerators(self, mode)
        #storeWeakref('menumap', self.menumap)

        self.root_accel.updateActions(self.show_toolbar)
        self._mgr.Update()

    def updateMenumap(self):
        self.root_accel.forceToolbarUpdate()

    def getActiveMajorMode(self):
        if self.tabs is not None:
            wrapper = self.tabs.getCurrent()
            if wrapper:
                major = self.tabs.getCurrent().editwin
                return major
        return None

    def getAllMajorModes(self):
        modes = self.tabs.getAll()
        return modes

    def isOpen(self):
        major = self.getActiveMajorMode()
        #assert self.dprint("major=%s isOpen=%s" % (str(major),str(major!=None)))
        return major is not None

    def isTopWindow(self):
        return wx.GetApp().GetTopWindow() == self

    def closeWindow(self):
        self.unbindEvents()
        self.clearMenumap()
        self.closeSidebar()
        WindowList.remove(self)
        self.Hide()
        self.tabs.closeAllTabs()
        self._mgr.DetachPane(self.tabs)
        self.tabs = None
        self.Destroy()

    @classmethod
    def closeAllWindows(cls):
        frames = WindowList.getFrames()
        for frame in frames:
            frame.closeWindow()

    def closeBuffer(self):
        major = self.getActiveMajorMode()
        if major:
            buffer = major.buffer
            if buffer.permanent:
                self.tabs.closeWrapper(major)
            else:
                if buffer.modified:
                    dlg = wx.MessageDialog(
                        self, "%s\n\nhas unsaved changes.\n\nClose anyway?" %
                        buffer.displayname, "Unsaved Changes",
                        wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
                    retval = dlg.ShowModal()
                    dlg.Destroy()
                else:
                    retval = wx.ID_YES

                if retval == wx.ID_YES:
                    if buffer.numViewers() > 1:
                        dlg = wx.MessageDialog(
                            self,
                            u"Multiple views of:\n\n%s\n\nexist.  Really remove buffer?"
                            % buffer.url, "Remove All Views?",
                            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
                        retval = dlg.ShowModal()
                        dlg.Destroy()
                    else:
                        retval = wx.ID_YES

                    if retval == wx.ID_YES:
                        buffer.removeAllViewsAndDelete()

    def setTitle(self):
        self.SetTitle(self.getTitle())

    def getTitle(self):
        major = self.getActiveMajorMode()
        if major:
            return u"peppy: %s (%s)" % (major.getTabName(),
                                        unicode(major.buffer.url))
        return self.name

    def showModified(self, major):
        current = self.getActiveMajorMode()
        if current:
            self.setTitle()
            self.tabs.updateWrapperTitle(major)

    def titleBuffer(self):
        self.open(wx.GetApp().classprefs.title_page)

    def isTitleBufferOnly(self):
        if len(self.getAllMajorModes()) > 1:
            return False
        mode = self.getActiveMajorMode()
        url = vfs.normalize(wx.GetApp().classprefs.title_page)
        dprint(u"%s == %s => %s" %
               (url, mode.buffer.url, mode.buffer.url == url))
        if mode.buffer.url == url:
            return True
        return False

    def setBuffer(self, buffer, wrapper=None, use_current=None, options=None):
        if wrapper is None:
            if use_current:
                wrapper = self.tabs.getCurrent()
            else:
                # this gets a default view for the selected buffer
                wrapper = self.tabs.getDocumentWrapper()
        mode = self.tabs.newMode(buffer, wrapper=wrapper)
        assert self.dprint("set buffer to new view %s" % mode)
        if not mode.isErrorMode():
            mode.showInitialPosition(buffer.raw_url, options)
        mode.setReadyForIdleEvents()

    def newBuffer(self, buffer):
        # proxy it up to tabs
        self.tabs.newBuffer(buffer.url, buffer)

    def changeMajorMode(self, requested):
        """Change the currently active major mode to the requested mode
        
        This changes the view of the current buffer to the new mode, but
        doesn't change any of the data in the buffer.
        """
        mode = self.getActiveMajorMode()
        cursor_data = mode.getViewPositionData()
        newmode = self.tabs.newMode(mode.buffer, requested, mode)
        if not newmode.isErrorMode():
            newmode.setViewPositionData(cursor_data)
        newmode.setReadyForIdleEvents()

    def makeTabActive(self, url, options=None):
        """Make the tab current that corresponds to the url.
        
        If the url isn't found, nothing happens.
        
        @return: True if URL was found, False if not.
        """
        normalized = vfs.normalize(url)
        self.dprint("url=%s normalized=%s" % (url, normalized))
        mode = self.tabs.moveSelectionToURL(normalized)
        if mode:
            mode.showInitialPosition(normalized)
            if options:
                mode.setViewPositionData(options)
        return mode is not None

    def findTabOrOpen(self, url, options=None):
        """Find a tab that contains this URL, otherwise open a new tab"""
        if not self.makeTabActive(url, options=options):
            self.open(url, options=options)

    def open(self, *args, **kwargs):
        """Open a new tab to edit the given URL.
        
        Driver function that uses L{FileOpener} to open URLs
        """
        try:
            opener = FileOpener(self, *args, **kwargs)
        except FileOpenerExceptionHandled:
            pass
        except vfs.AuthenticationCancelled:
            pass
        except vfs.NetworkError, e:
            self.SetStatusText(e)
Beispiel #4
0
class BufferFrame(wx.Frame, ClassPrefs, debugmixin):
    frameid=0
    load_error_count = 0
    size_timer = None
    
    preferences_tab = "General"
    preferences_label = "Windows"
    if wx.Platform == "__WXMAC__":
        icon = "icons/application_osx.png"
    else:
        icon = "icons/application.png"
    
    perspectives={}

    default_classprefs = (
        IntParam('width', 800, 'Width of the main frame in pixels'),
        IntParam('height', 600, 'Height of the main frame in pixels'),
        BoolParam('show_toolbar', True, 'Show the toolbar on all frames?\nNote: this is a global setting for all frames.'),
        BoolParam('resize_becomes_default', True, 'Should the new size following a resize become the default size of all subsequently created frames?'),
        BoolParam('show_popup_status', False, 'Use experimental popup status bar'),
        # FIXME: this attempt at fast resizing caused Freeze() mismatch
        # problems.  It's not worth that much, so I'm disabling it for
        # now.
        # BoolParam('fast_resize', False, 'Speed up resize events by deferring window\nrepaints until the mouse stops moving'),
        )

    default_menubar = {
        _("File"): 0,
        _("File/Export"): 850, # position within the parent menu for submenus
        _("Edit"): 0.001,
        _("View"): 0.003,
        _("View/Apply Settings"): -980,
        _("Tools"): 0.004,
        _("Transform"): 0.005,
        _("Project"): 1000.05,
        _("Documents"): 1000.1,
        _("Window"): 1000.2,
        _("&Help"): 1000.3,
        }


    def __init__(self, urls=[], id=-1, buffer=None):
        BufferFrame.frameid+=1
        self.name="peppy: Window #%d" % BufferFrame.frameid
        
        # flags to prevent multiple timestamp checks
        self.pending_timestamp_check = False
        self.currently_processing_timestamp = False

        # initialize superclass
        pos, size = self.initPositionAndSize()
        wx.Frame.__init__(self, None, id=-1, title=self.name, pos=pos, size=size, style=wx.DEFAULT_FRAME_STYLE|wx.CLIP_CHILDREN)

        self.initIcon()
        self.initLayout()
        self.initMenu()
        self.loadSidebars()
        self.initEventBindings()
        self.initLoad(buffer, urls)
        self.initRegisterWindow()
        self.Show()
    
    def initPositionAndSize(self):
        pos = wx.DefaultPosition
        size = (int(self.classprefs.width), int(self.classprefs.height))
        return pos, size
    
    def initIcon(self):
        icon = wx.EmptyIcon()
        icon.CopyFromBitmap(getIconBitmap('icons/peppy.png'))
        self.SetIcon(icon)

    def initLayout(self):
        ModularStatusBar(self)
        if self.classprefs.show_popup_status:
            self.popup_status = PopupStatusBar(self)
        else:
            self.popup_status = None

        hsplit = wx.BoxSizer(wx.HORIZONTAL)
        self.SetAutoLayout(True)
        self.SetSizer(hsplit)
        
        self.spring = SpringTabs(self)

        # tell FrameManager to manage this frame        
        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)

        self.tabs = FrameNotebook(self)
        self._mgr.AddPane(self.tabs, aui.AuiPaneInfo().Name("notebook").
                          CenterPane())
        self.sidebar_panes = []
        
        # paneinfo can use the C++ style notation for setting flags because
        # each method of AuiPaneInfo returns itself
        paneinfo = aui.AuiPaneInfo().Name("SpringTabs").Caption("SpringTabs").Left().Layer(10).CloseButton(False).CaptionVisible(False).LeftDockable(False).RightDockable(False)
        
        # Stock wxPython distributed with OS X 10.5 is version 2.8.4.0, which
        # apparently doesn't have the DockFixed method.  So, I check for that
        # here before using it.
        if hasattr(paneinfo, 'DockFixed'):
            paneinfo.DockFixed()
        self._mgr.AddPane(self.spring, paneinfo)

    def initMenu(self):
        UserActionClassList.setDefaultMenuBarWeights(self.default_menubar)
        menubar = wx.MenuBar()
        if wx.Platform == '__WXMAC__':
            # turn off the automatic generation of the Window menu.
            # We generate one ourselves
            wx.MenuBar.SetAutoWindowMenu(False)
            # Replace the system Help menu with ours by creating this small
            # fake menu in order to register the help menu.  Note that you
            # have to add something to the menubar on the mac (i.e.  the
            # separator) in order for it to actually register with the Mac
            # menu system as being present.  An empty Help menu is ignored as
            # far as SetMacHelpMenuTitleName is concerned.
            help = wx.Menu()
            help.AppendSeparator()
            menubar.Append(help, _("&Help"))
            wx.GetApp().SetMacHelpMenuTitleName(_("&Help"))
        self.SetMenuBar(menubar)
        
        self.show_toolbar = self.classprefs.show_toolbar
        self.root_accel = Null()

    def initEventBindings(self):
        self.dropTarget=FrameDropTarget(self)
        self.SetDropTarget(self.dropTarget)        
        
        Publisher().subscribe(self.pluginsChanged, 'peppy.plugins.changed')
        wx.GetApp().SetTopWindow(self)
        self.bindEvents()
        
    def initLoad(self, buffer, urls):
        if buffer:
            self.newBuffer(buffer)
        else:
            self.loadList(urls)
    
    def initRegisterWindow(self):
        WindowList.append(self)

    def __str__(self):
        return "%s %s id=%s" % (self.__class__.__name__, self.name, hex(id(self)))
    
    def isOSXMinimalMenuFrame(self):
        return False
        
    def bindEvents(self):
        self.Bind(wx.EVT_CLOSE,self.OnClose)
        self.Bind(wx.EVT_ACTIVATE, self.OnRaise)
        self.Bind(wx.EVT_SIZE, self.OnSize)

    def unbindEvents(self):
        self.Unbind(wx.EVT_CLOSE)
        self.Unbind(wx.EVT_ACTIVATE)
        self.Unbind(wx.EVT_SIZE)

    def loadList(self, urls):
        if urls:
            wx.CallAfter(self.openList, urls)
        else:
            wx.CallAfter(self.titleBuffer)
    
    def openList(self, urls):
        self.tabs.holdChanges()
        for url in urls:
            #dprint("Opening %s" % url)
            self.open(url, force_new_tab=True)
        # If no pages have been open (due to errors), make sure that a title
        # buffer is displayed.
        if self.tabs.GetPageCount() == 0:
            self.titleBuffer()
        self.tabs.processChanges()
        
    def addPane(self, win, paneinfo):
        self._mgr.AddPane(win, paneinfo)

    def loadSidebars(self):
        sidebars = Sidebar.getClasses(self)
        for sidebarcls in sidebars:
            if sidebarcls.classprefs.springtab:
                self.spring.addTab(sidebarcls.caption, sidebarcls)
            else:
                self.createSidebar(sidebarcls)
        self.createSidebarList()

    def createSidebar(self, sidebarcls):
        """Create the sidebar and register it with the frame's AUI
        Manager."""

        sidebar = sidebarcls(self)
        paneinfo = sidebar.getPaneInfo()
        self._mgr.AddPane(sidebar, paneinfo)
        sidebar.paneinfo = self._mgr.GetPane(sidebar)
        sidebar.activateSidebar()

    def createSidebarList(self):
        self.sidebar_panes = []
        sidebars = self._mgr.GetAllPanes()
        for sidebar in sidebars:
            assert self.dprint("name=%s caption=%s window=%s state=%s" % (sidebar.name, sidebar.caption, sidebar.window, sidebar.state))
            if sidebar.name != "notebook":
                self.sidebar_panes.append(sidebar)
        self.sidebar_panes.sort(key=lambda s:s.caption)

    def closeSidebar(self):
        self.dprint("closing springtabs")
        self.spring.deleteTabs()
        for sidebar in self.sidebar_panes:
            self.dprint("closing sidebar %s" % sidebar.window)
            self._mgr.DetachPane(sidebar.window)
            sidebar.window.Destroy()
            sidebar.window = None
        sidebars = self._mgr.GetAllPanes()
        self.dprint("sidebars remaining: %s" % sidebars)
        self.sidebar_panes = None

    def processNormalIdleEvent(self):
        """Event processing for low priority idle functions.
        
        This event processor is called from the main application's idle event
        handler L{Peppy.OnIdle} with a minimum time between calls to this
        method.  This is for expensive idle event functions or functions that
        don't need to be called that often to still provide good feedback to
        the user.
        """
        if not self.IsActive():
            self.dprint("Top window %s not active.  No idle events." % self)
            return
        mode = self.getActiveMajorMode()
        if mode and mode.isReadyForIdleEvents():
            # Refs #665: this call to forceToolbarUpdate causes the yield to
            # freeze until tabs get switched.  Now this only happens when the
            # mode is ready for idle event processing
            self.root_accel.forceToolbarUpdate()
            
            # Timestamp checks are performed here for two reasons: 1) windows
            # reports activate events whenever a dialog is popped up, so
            # you get an unending stream of dialogs.  2) when trying to use
            # dialogs after changing tabs on linux, the mouse events seemed to
            # get eaten.  The only workaround I could find was to put the call
            # to isModeChangedOnDisk here wrapped by a CallAfter.
            if self.pending_timestamp_check and not self.currently_processing_timestamp:
                self.currently_processing_timestamp = True
                wx.CallAfter(self.isModeChangedOnDisk)
                self.pending_timestamp_check = False
        #else:
        #    dprint("mode %s not ready for idle events" % mode)

    def processPriorityIdleEvent(self):
        """Event processing that happens at every idle event
        
        This event processor is for high priority idle events that should be
        called as often as possible.  Like L{processNormalIdleEvent}, this
        method is called from L{Peppy.OnIdle} but instead is called at every
        idle event.
        
        Compared to the calls in L{processNormalIdleEvent}, the functions
        called by this method should strive not to use much processing time
        because delays here will be much more noticeable to the user.
        """
        if not self.IsActive():
            self.dprint("Top window %s not active.  No idle events." % self)
            return
        mode = self.getActiveMajorMode()
        if mode and mode.isReadyForIdleEvents():
            #dprint("Idle for mode %s" % mode)
            mode.idleHandler()

    # Overrides of wx methods
    def OnRaise(self, evt):
        self.dprint("Focus! %s" % self.name)
        if evt.GetActive():
            wx.GetApp().SetTopWindow(self)
            if not self.pending_timestamp_check and not self.currently_processing_timestamp:
                self.pending_timestamp_check = True
        else:
            if self.popup_status:
                self.popup_status.clear()
        evt.Skip()
    
    def OnSize(self, evt):
        # FIXME: this fast resizing was causing problems, so I've disabled it
        # with the embedded False
        if False and self.classprefs.fast_resize:
            if not self.tabs.IsFrozen():
                dprint("not frozen.  Freezing")
                self.tabs.Freeze()
            if not self.__class__.size_timer:
                self.__class__.size_timer = wx.PyTimer(self.OnSizeTimer)
            self.__class__.size_timer.Start(50, oneShot=True)
        else:
            self.rememberFrameSize()
            evt.Skip()
    
    def rememberFrameSize(self):
        if self.classprefs.resize_becomes_default:
            size = self.GetSize()
            self.classprefs.width = size.GetWidth()
            self.classprefs.height = size.GetHeight()

    def OnSizeTimer(self, evt=None):
        if self.tabs.IsFrozen():
            dprint("frozen.  Thawing")
            # FIXME: for some reason, IsFrozen returns True even when it's
            # not frozen.
            self.tabs.Thaw()

    def OnClose(self, evt=None):
        assert self.dprint(evt)
        if WindowList.canCloseWindow(self):
            self.closeWindow()

    def Raise(self):
        wx.Frame.Raise(self)
        major=self.getActiveMajorMode()
        if major is not None:
            major.SetFocus()
        
    def SetStatusText(self, text, index=0, hold=False):
        """Overrides status bar with popup status.
        
        Instead of taking up screen real-estate with a status bar, the
        popup status displays a message for a small amount of time before
        disappearing.  Depending on the value of index, a message can either
        remain on screen until the time expires, or can be overwritten by the
        next call to SetStatusText.
        
        Depending on the value of hold, a message can either remain on screen
        until the time expires, or can be overwritten by the next call to
        SetStatusText.

        @param text: the text (unicode) to display in the popup message
        
        @param index: 0 uses L{PopupStatusBar.showMessage} to display the text
        in the popup and will remain on screen till it expires; anything else
        uses L{PopupStatusBar.showStatusText} to display status info that gets
        overwritten with the next call.
        
        @param hold: if True, ignores expiration time and displays the status
        message until the next status message is displayed.
        """
        if text:
            if self.popup_status:
                if index == 0 and not hold:
                    self.popup_status.showMessage(_(text))
                else:
                    self.popup_status.showStatusText(_(text), hold)
            else:
                self.GetStatusBar().SetStatusText(_(text), index)
    
    # non-wx methods
    
    def showErrorDialog(self, msg, title="Error"):
        dlg = wx.MessageDialog(self, msg, title, wx.OK | wx.ICON_ERROR )
        retval = dlg.ShowModal()
        return retval
    
    def showWarningDialog(self, msg, title="Warning"):
        dlg = wx.MessageDialog(self, msg, title, wx.OK | wx.ICON_WARNING )
        retval = dlg.ShowModal()
        return retval
    
    def showQuestionDialog(self, msg, title="Question"):
        dlg = wx.MessageDialog(self, msg, title, wx.YES_NO | wx.ICON_QUESTION )
        retval = dlg.ShowModal()
        return retval
    
    def clearMenumap(self):
        self.root_accel.cleanupAndDelete()

    def setMenumap(self, mode):
        self.clearMenumap()
        self.root_accel = UserAccelerators(self, mode)
        #storeWeakref('menumap', self.menumap)
        
        self.root_accel.updateActions(self.show_toolbar)
        self._mgr.Update()
    
    def updateMenumap(self):
        self.root_accel.forceToolbarUpdate()

    def getActiveMajorMode(self):
        if self.tabs is not None:
            wrapper = self.tabs.getCurrent()
            if wrapper:
                major=self.tabs.getCurrent().editwin
                return major
        return None

    def getAllMajorModes(self):
        modes = self.tabs.getAll()
        return modes
    
    def isOpen(self):
        major=self.getActiveMajorMode()
            #assert self.dprint("major=%s isOpen=%s" % (str(major),str(major!=None)))
        return major is not None

    def isTopWindow(self):
        return wx.GetApp().GetTopWindow()==self
    
    def closeWindow(self):
        self.unbindEvents()
        self.clearMenumap()
        self.closeSidebar()
        WindowList.remove(self)
        self.Hide()
        self.tabs.closeAllTabs()
        self._mgr.DetachPane(self.tabs)
        self.tabs = None
        self.Destroy()

    @classmethod
    def closeAllWindows(cls):
        frames = WindowList.getFrames()
        for frame in frames:
            frame.closeWindow()
    
    def closeBuffer(self):
        major=self.getActiveMajorMode()
        if major:
            buffer=major.buffer
            if buffer.permanent:
                self.tabs.closeWrapper(major)
            else:
                if buffer.modified:
                    dlg = wx.MessageDialog(self, "%s\n\nhas unsaved changes.\n\nClose anyway?" % buffer.displayname, "Unsaved Changes", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION )
                    retval=dlg.ShowModal()
                    dlg.Destroy()
                else:
                    retval=wx.ID_YES

                if retval==wx.ID_YES:
                    if buffer.numViewers() > 1:
                        dlg = wx.MessageDialog(self, u"Multiple views of:\n\n%s\n\nexist.  Really remove buffer?" % buffer.url, "Remove All Views?", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION )
                        retval=dlg.ShowModal()
                        dlg.Destroy()
                    else:
                        retval=wx.ID_YES
                        
                    if retval==wx.ID_YES:
                        buffer.removeAllViewsAndDelete()
    
    def setTitle(self):
        self.SetTitle(self.getTitle())

    def getTitle(self):
        major = self.getActiveMajorMode()
        if major:
            return u"peppy: %s (%s)" % (major.getTabName(), unicode(major.buffer.url))
        return self.name
    
    def showModified(self, major):
        current=self.getActiveMajorMode()
        if current:
            self.setTitle()
            self.tabs.updateWrapperTitle(major)

    def titleBuffer(self):
        self.open(wx.GetApp().classprefs.title_page)

    def isTitleBufferOnly(self):
        if len(self.getAllMajorModes()) > 1:
            return False
        mode = self.getActiveMajorMode()
        url = vfs.normalize(wx.GetApp().classprefs.title_page)
        dprint(u"%s == %s => %s" % (url, mode.buffer.url, mode.buffer.url == url))
        if mode.buffer.url == url:
            return True
        return False

    def setBuffer(self, buffer, wrapper=None, use_current=None, options=None):
        if wrapper is None:
            if use_current:
                wrapper = self.tabs.getCurrent()
            else:
                # this gets a default view for the selected buffer
                wrapper = self.tabs.getDocumentWrapper()
        mode = self.tabs.newMode(buffer, wrapper=wrapper)
        assert self.dprint("set buffer to new view %s" % mode)
        if not mode.isErrorMode():
            mode.showInitialPosition(buffer.raw_url, options)
        mode.setReadyForIdleEvents()

    def newBuffer(self, buffer):
        # proxy it up to tabs
        self.tabs.newBuffer(buffer.url, buffer)
    
    def changeMajorMode(self, requested):
        """Change the currently active major mode to the requested mode
        
        This changes the view of the current buffer to the new mode, but
        doesn't change any of the data in the buffer.
        """
        mode = self.getActiveMajorMode()
        cursor_data = mode.getViewPositionData()
        newmode = self.tabs.newMode(mode.buffer, requested, mode)
        if not newmode.isErrorMode():
            newmode.setViewPositionData(cursor_data)
        newmode.setReadyForIdleEvents()

    def makeTabActive(self, url, options=None):
        """Make the tab current that corresponds to the url.
        
        If the url isn't found, nothing happens.
        
        @return: True if URL was found, False if not.
        """
        normalized = vfs.normalize(url)
        self.dprint("url=%s normalized=%s" % (url, normalized))
        mode = self.tabs.moveSelectionToURL(normalized)
        if mode:
            mode.showInitialPosition(normalized)
            if options:
                mode.setViewPositionData(options)
        return mode is not None
    
    def findTabOrOpen(self, url, options=None):
        """Find a tab that contains this URL, otherwise open a new tab"""
        if not self.makeTabActive(url, options=options):
            self.open(url, options=options)

    def open(self, *args, **kwargs):
        """Open a new tab to edit the given URL.
        
        Driver function that uses L{FileOpener} to open URLs
        """
        try:
            opener = FileOpener(self, *args, **kwargs)
        except FileOpenerExceptionHandled:
            pass
        except vfs.AuthenticationCancelled:
            pass
        except vfs.NetworkError, e:
            self.SetStatusText(e)