+ Processes an event, searching event tables and calling zero or more
+ suitable event handler function(s). Note that the ProcessEvent
+ method is called from the wxPython docview framework directly since
+ wxPython does not have a virtual ProcessEvent function.
+ """
+ return False
+
+
+ def ProcessUpdateUIEvent(self, event):
+ """
+ Processes a UI event, searching event tables and calling zero or more
+ suitable event handler function(s). Note that the ProcessEvent
+ method is called from the wxPython docview framework directly since
+ wxPython does not have a virtual ProcessEvent function.
+ """
+ return False
+
+
+ def OnCloseFrame(self, event):
+ """
+ Called when the a docview frame is being closed. Override this method
+ so a service can either do cleanup or veto the frame being closed by
+ returning false.
+ """
+ return True
+
+
+ def OnExit(self):
+ """
+ Called when the the docview application is being closed. Override this method
+ so a service can either do cleanup or veto the frame being closed by
+ returning false.
+ """
+ pass
+
+
+ def GetMenuItemPos(self, menu, id):
+ """
+ Utility method used to find the position of a menu item so that services can
+ easily find where to insert a menu item in InstallControls.
+ """
+ menuItems = menu.GetMenuItems()
+ for i, menuItem in enumerate(menuItems):
+ if menuItem.GetId() == id:
+ return i
+ return i
+
+
+ def GetView(self):
+ """
+ Called by WindowMenuService to get views for services that don't
+ have dedicated documents such as the Outline Service.
+ """
+ return None
+
+
+class DocOptionsService(DocService):
+ """
+ A service that implements an options menu item and an options dialog with
+ notebook tabs. New tabs can be added by other services by calling the
+ "AddOptionsPanel" method.
+ """
+
+
+ def __init__(self, showGeneralOptions=True, supportedModes=wx.lib.docview.DOC_SDI & wx.lib.docview.DOC_MDI):
+ """
+ Initializes the options service with the option of suppressing the default
+ general options pane that is included with the options service by setting
+ showGeneralOptions to False. It allowModeChanges is set to False, the
+ default general options pane will allow users to change the document
+ interface mode between SDI and MDI modes.
+ """
+ DocService.__init__(self)
+ self.ClearOptionsPanels()
+ self._supportedModes = supportedModes
+ self._toolOptionsID = wx.NewId()
+ if showGeneralOptions:
+ self.AddOptionsPanel(GeneralOptionsPanel)
+
+
+ def InstallControls(self, frame, menuBar=None, toolBar=None, statusBar=None, document=None):
+ """
+ Installs a "Tools" menu with an "Options" menu item.
+ """
+ toolsMenuIndex = menuBar.FindMenu(_("&Tools"))
+ if toolsMenuIndex > -1:
+ toolsMenu = menuBar.GetMenu(toolsMenuIndex)
+ else:
+ toolsMenu = wx.Menu()
+ if toolsMenuIndex == -1:
+ formatMenuIndex = menuBar.FindMenu(_("&Format"))
+ menuBar.Insert(formatMenuIndex + 1, toolsMenu, _("&Tools"))
+ if toolsMenu:
+ if toolsMenu.GetMenuItemCount():
+ toolsMenu.AppendSeparator()
+ toolsMenu.Append(self._toolOptionsID, _("&Options..."), _("Sets options"))
+ wx.EVT_MENU(frame, self._toolOptionsID, frame.ProcessEvent)
+
+
+ def ProcessEvent(self, event):
+ """
+ Checks to see if the "Options" menu item has been selected.
+ """
+ id = event.GetId()
+ if id == self._toolOptionsID:
+ self.OnOptions(event)
+ return True
+ else:
+ return False
+
+
+ def GetSupportedModes(self):
+ """
+ Return the modes supported by the application. Use docview.DOC_SDI and
+ docview.DOC_MDI flags to check if SDI and/or MDI modes are supported.
+ """
+ return self._supportedModes
+
+
+ def SetSupportedModes(self, _supportedModessupportedModes):
+ """
+ Sets the modes supported by the application. Use docview.DOC_SDI and
+ docview.DOC_MDI flags to set if SDI and/or MDI modes are supported.
+ """
+ self._supportedModes = supportedModes
+
+
+ def ClearOptionsPanels(self):
+ """
+ Clears all of the options panels that have been added into the
+ options dialog.
+ """
+ self._optionsPanels = []
+
+
+ def AddOptionsPanel(self, optionsPanel):
+ """
+ Adds an options panel to the options dialog.
+ """
+ self._optionsPanels.append(optionsPanel)
+
+
+ def OnOptions(self, event):
+ """
+ Shows the options dialog, called when the "Options" menu item is selected.
+ """
+ if len(self._optionsPanels) == 0:
+ return
+ optionsDialog = OptionsDialog(wx.GetApp().GetTopWindow(), self._optionsPanels, self._docManager)
+ if optionsDialog.ShowModal() == wx.ID_OK:
+ optionsDialog.OnOK(optionsDialog) # wxBug: wxDialog should be calling this automatically but doesn't
+ optionsDialog.Destroy()
+
+
+class OptionsDialog(wx.Dialog):
+ """
+ A default options dialog used by the OptionsService that hosts a notebook
+ tab of options panels.
+ """
+
+
+ def __init__(self, parent, optionsPanelClasses, docManager):
+ """
+ Initializes the options dialog with a notebook page that contains new
+ instances of the passed optionsPanelClasses.
+ """
+ wx.Dialog.__init__(self, parent, -1, _("Options"), size = (570, 365))
+
+ self._optionsPanels = []
+ self._docManager = docManager
+
+ HALF_SPACE = 5
+ SPACE = 10
+
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ optionsNotebook = wx.Notebook(self, -1, size=(560, 325))
+ sizer.Add(optionsNotebook, 0, wx.ALL | wx.EXPAND, SPACE)
+ for optionsPanelClass in optionsPanelClasses:
+ optionsPanel = optionsPanelClass(optionsNotebook, -1)
+ self._optionsPanels.append(optionsPanel)
+ sizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
+ self.SetSizer(sizer)
+ self.Layout()
+ if wx.Platform != '__WXMAC__' or len(optionsPanelClasses) < 6: # wxBug: Notebook tabs are truncated and user can't get to them on the Mac
+ self.Fit()
+ wx.CallAfter(self.DoRefresh)
+
+
+ def DoRefresh(self):
+ """
+ wxBug: On Windows XP when using a multiline notebook the default page doesn't get
+ drawn, but it works when using a single line notebook.
+ """
+ self.Refresh()
+
+
+ def GetDocManager(self):
+ """
+ Returns the document manager passed to the OptionsDialog constructor.
+ """
+ return self._docManager
+
+
+ def OnOK(self, event):
+ """
+ Calls the OnOK method of all of the OptionDialog's embedded panels
+ """
+ for optionsPanel in self._optionsPanels:
+ optionsPanel.OnOK(event)
+
+
+class GeneralOptionsPanel(wx.Panel):
+ """
+ A general options panel that is used in the OptionDialog to configure the
+ generic properties of a pydocview application, such as "show tips at startup"
+ and whether to use SDI or MDI for the application.
+ """
+
+
+ def __init__(self, parent, id):
+ """
+ Initializes the panel by adding an "Options" folder tab to the parent notebook and
+ populating the panel with the generic properties of a pydocview application.
+ """
+ wx.Panel.__init__(self, parent, id)
+ SPACE = 10
+ HALF_SPACE = 5
+ config = wx.ConfigBase_Get()
+ self._showTipsCheckBox = wx.CheckBox(self, -1, _("Show tips at start up"))
+ self._showTipsCheckBox.SetValue(config.ReadInt("ShowTipAtStartup", True))
+ if self._AllowModeChanges():
+ supportedModes = wx.GetApp().GetService(DocOptionsService).GetSupportedModes()
+ choices = []
+ self._sdiChoice = _("Show each document in its own window")
+ self._mdiChoice = _("Show all documents in a single window with tabs")
+ self._winMdiChoice = _("Show all documents in a single window with child windows")
+ if supportedModes & wx.lib.docview.DOC_SDI:
+ choices.append(self._sdiChoice)
+ choices.append(self._mdiChoice)
+ if wx.Platform == "__WXMSW__":
+ choices.append(self._winMdiChoice)
+ self._documentRadioBox = wx.RadioBox(self, -1, _("Document Interface"),
+ choices = choices,
+ majorDimension=1,
+ )
+ if config.ReadInt("UseWinMDI", False):
+ self._documentRadioBox.SetStringSelection(self._winMdiChoice)
+ elif config.ReadInt("UseMDI", True):
+ self._documentRadioBox.SetStringSelection(self._mdiChoice)
+ else:
+ self._documentRadioBox.SetStringSelection(self._sdiChoice)
+ def OnDocumentInterfaceSelect(event):
+ if not self._documentInterfaceMessageShown:
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("Document Options")
+ wx.MessageBox("Document interface changes will not appear until the application is restarted.",
+ msgTitle,
+ wx.OK | wx.ICON_INFORMATION,
+ self.GetParent())
+ self._documentInterfaceMessageShown = True
+ wx.EVT_RADIOBOX(self, self._documentRadioBox.GetId(), OnDocumentInterfaceSelect)
+ optionsBorderSizer = wx.BoxSizer(wx.VERTICAL)
+ optionsSizer = wx.BoxSizer(wx.VERTICAL)
+ if self._AllowModeChanges():
+ optionsSizer.Add(self._documentRadioBox, 0, wx.ALL, HALF_SPACE)
+ optionsSizer.Add(self._showTipsCheckBox, 0, wx.ALL, HALF_SPACE)
+ optionsBorderSizer.Add(optionsSizer, 0, wx.ALL, SPACE)
+ self.SetSizer(optionsBorderSizer)
+ self.Layout()
+ self._documentInterfaceMessageShown = False
+ parent.AddPage(self, _("Options"))
+
+
+ def _AllowModeChanges(self):
+ supportedModes = wx.GetApp().GetService(DocOptionsService).GetSupportedModes()
+ return supportedModes & wx.lib.docview.DOC_SDI and supportedModes & wx.lib.docview.DOC_MDI or wx.Platform == "__WXMSW__" and supportedModes & wx.lib.docview.DOC_MDI # More than one mode is supported, allow selection
+
+
+ def OnOK(self, optionsDialog):
+ """
+ Updates the config based on the selections in the options panel.
+ """
+ config = wx.ConfigBase_Get()
+ config.WriteInt("ShowTipAtStartup", self._showTipsCheckBox.GetValue())
+ if self._AllowModeChanges():
+ config.WriteInt("UseMDI", (self._documentRadioBox.GetStringSelection() == self._mdiChoice))
+ config.WriteInt("UseWinMDI", (self._documentRadioBox.GetStringSelection() == self._winMdiChoice))
+
+
+class DocApp(wx.PySimpleApp):
+ """
+ The DocApp class serves as the base class for pydocview applications and offers
+ functionality such as services, creation of SDI and MDI frames, show tips,
+ and a splash screen.
+ """
+
+
+ def OnInit(self):
+ """
+ Initializes the DocApp.
+ """
+ self._services = []
+ self._defaultIcon = None
+ self._registeredCloseEvent = False
+ self._useTabbedMDI = True
+
+ if not hasattr(self, "_debug"): # only set if not already initialized
+ self._debug = False
+ if not hasattr(self, "_singleInstance"): # only set if not already initialized
+ self._singleInstance = True
+
+ # if _singleInstance is TRUE only allow one single instance of app to run.
+ # When user tries to run a second instance of the app, abort startup,
+ # But if user also specifies files to open in command line, send message to running app to open those files
+ if self._singleInstance:
+ # create shared memory temporary file
+ if wx.Platform == '__WXMSW__':
+ tfile = tempfile.TemporaryFile(prefix="ag", suffix="tmp")
+ fno = tfile.fileno()
+ self._sharedMemory = mmap.mmap(fno, 1024, "shared_memory")
+ else:
+ tfile = file(os.path.join(tempfile.gettempdir(), tempfile.gettempprefix() + self.GetAppName() + '-' + wx.GetUserId() + "AGSharedMemory"), 'w+b')
+ tfile.write("*")
+ tfile.seek(1024)
+ tfile.write(" ")
+ tfile.flush()
+ fno = tfile.fileno()
+ self._sharedMemory = mmap.mmap(fno, 1024)
+
+ self._singleInstanceChecker = wx.SingleInstanceChecker(self.GetAppName() + '-' + wx.GetUserId(), tempfile.gettempdir())
+ if self._singleInstanceChecker.IsAnotherRunning():
+ # have running single instance open file arguments
+ data = pickle.dumps(sys.argv[1:])
+ while 1:
+ self._sharedMemory.seek(0)
+ marker = self._sharedMemory.read_byte()
+ if marker == '\0' or marker == '*': # available buffer
+ self._sharedMemory.seek(0)
+ self._sharedMemory.write_byte('-') # set writing marker
+ self._sharedMemory.write(data) # write files we tried to open to shared memory
+ self._sharedMemory.seek(0)
+ self._sharedMemory.write_byte('+') # set finished writing marker
+ self._sharedMemory.flush()
+ break
+ else:
+ time.sleep(1) # give enough time for buffer to be available
+
+ return False
+ else:
+ self._timer = wx.PyTimer(self.DoBackgroundListenAndLoad)
+ self._timer.Start(250)
+
+ return True
+
+
+ def OpenMainFrame(self):
+ docManager = self.GetDocumentManager()
+ if docManager.GetFlags() & wx.lib.docview.DOC_MDI:
+ if self.GetUseTabbedMDI():
+ frame = wx.lib.pydocview.DocTabbedParentFrame(docManager, None, -1, self.GetAppName())
+ else:
+ frame = wx.lib.pydocview.DocMDIParentFrame(docManager, None, -1, self.GetAppName())
+ frame.Show(True)
+
+
+ def DoBackgroundListenAndLoad(self):
+ """
+ Open any files specified in the given command line argument passed in via shared memory
+ """
+ self._timer.Stop()
+
+ self._sharedMemory.seek(0)
+ if self._sharedMemory.read_byte() == '+': # available data
+ data = self._sharedMemory.read(1024-1)
+ self._sharedMemory.seek(0)
+ self._sharedMemory.write_byte("*") # finished reading, set buffer free marker
+ self._sharedMemory.flush()
+ args = pickle.loads(data)
+ for arg in args:
+ if arg[0] != '/' and arg[0] != '-' and os.path.exists(arg):
+ self.GetDocumentManager().CreateDocument(arg, wx.lib.docview.DOC_SILENT)
+
+ # force display of running app
+ topWindow = wx.GetApp().GetTopWindow()
+ if topWindow.IsIconized():
+ topWindow.Iconize(False)
+ else:
+ topWindow.Raise()
+
+
+ self._timer.Start(1000) # 1 second interval
+
+
+ def OpenCommandLineArgs(self):
+ """
+ Called to open files that have been passed to the application from the
+ command line.
+ """
+ args = sys.argv[1:]
+ for arg in args:
+ if arg[0] != '/' and arg[0] != '-' and os.path.exists(arg):
+ self.GetDocumentManager().CreateDocument(arg, wx.lib.docview.DOC_SILENT)
+
+
+ def GetDocumentManager(self):
+ """
+ Returns the document manager associated to the DocApp.
+ """
+ return self._docManager
+
+
+ def SetDocumentManager(self, docManager):
+ """
+ Sets the document manager associated with the DocApp and loads the
+ DocApp's file history into the document manager.
+ """
+ self._docManager = docManager
+ config = wx.ConfigBase_Get()
+ self.GetDocumentManager().FileHistoryLoad(config)
+
+
+ def ProcessEventBeforeWindows(self, event):
+ """
+ Enables services to process an event before the main window has a chance to
+ process the window.
+ """
+ for service in self._services:
+ if service.ProcessEventBeforeWindows(event):
+ return True
+ return False
+
+
+ def ProcessUpdateUIEventBeforeWindows(self, event):
+ """
+ Enables services to process a UI event before the main window has a chance
+ to process the window.