--- /dev/null
+#----------------------------------------------------------------------------
+# Name: docview.py
+# Purpose: Port of the wxWindows docview classes
+#
+# Author: Peter Yared
+#
+# Created: 5/15/03
+# CVS-ID: $Id$
+# Copyright: (c) 2003-2004 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
+# License: wxWindows license
+#----------------------------------------------------------------------------
+
+
+import os
+import os.path
+import wx
+import sys
+_ = wx.GetTranslation
+
+
+#----------------------------------------------------------------------
+# docview globals
+#----------------------------------------------------------------------
+
+DOC_SDI = 1
+DOC_MDI = 2
+DOC_NEW = 4
+DOC_SILENT = 8
+DOC_OPEN_ONCE = 16
+DEFAULT_DOCMAN_FLAGS = DOC_SDI & DOC_OPEN_ONCE
+
+TEMPLATE_VISIBLE = 1
+TEMPLATE_INVISIBLE = 2
+DEFAULT_TEMPLATE_FLAGS = TEMPLATE_VISIBLE
+
+MAX_FILE_HISTORY = 9
+
+
+#----------------------------------------------------------------------
+# Convenience functions from wxWindows used in docview
+#----------------------------------------------------------------------
+
+
+def FileNameFromPath(path):
+ """
+ Returns the filename for a full path.
+ """
+ return os.path.split(path)[1]
+
+def FindExtension(path):
+ """
+ Returns the extension of a filename for a full path.
+ """
+ return os.path.splitext(path)[1].lower()
+
+def FileExists(path):
+ """
+ Returns True if the path exists.
+ """
+ return os.path.isfile(path)
+
+def PathOnly(path):
+ """
+ Returns the path of a full path without the filename.
+ """
+ return os.path.split(path)[0]
+
+
+#----------------------------------------------------------------------
+# Document/View Classes
+#----------------------------------------------------------------------
+
+
+class Document(wx.EvtHandler):
+ """
+ The document class can be used to model an application's file-based data. It
+ is part of the document/view framework supported by wxWindows, and cooperates
+ with the wxView, wxDocTemplate and wxDocManager classes.
+ """
+
+
+ def __init__(self, parent = None):
+ """
+ Constructor. Define your own default constructor to initialize
+ application-specific data.
+ """
+ wx.EvtHandler.__init__(self)
+
+ self._documentModified = False
+ self._documentParent = parent
+ self._documentTemplate = None
+ self._commandProcessor = None
+ self._savedYet = False
+
+ self._documentTitle = None
+ self._documentFile = None
+ self._documentTypeName = None
+ self._documentModified = False
+ self._documentViews = []
+
+
+ def ProcessEvent(self, event):
+ """
+ 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 GetFilename(self):
+ """
+ Gets the filename associated with this document, or "" if none is
+ associated.
+ """
+ return self._documentFile
+
+
+ def GetTitle(self):
+ """
+ Gets the title for this document. The document title is used for an
+ associated frame (if any), and is usually constructed by the framework
+ from the filename.
+ """
+ return self._documentTitle
+
+
+ def SetTitle(self, title):
+ """
+ Sets the title for this document. The document title is used for an
+ associated frame (if any), and is usually constructed by the framework
+ from the filename.
+ """
+ self._documentTitle = title
+
+
+ def GetDocumentName(self):
+ """
+ The document type name given to the wxDocTemplate constructor,
+ copied to this document when the document is created. If several
+ document templates are created that use the same document type, this
+ variable is used in wxDocManager::CreateView to collate a list of
+ alternative view types that can be used on this kind of document.
+ """
+ return self._documentTypeName
+
+
+ def SetDocumentName(self, name):
+ """
+ Sets he document type name given to the wxDocTemplate constructor,
+ copied to this document when the document is created. If several
+ document templates are created that use the same document type, this
+ variable is used in wxDocManager::CreateView to collate a list of
+ alternative view types that can be used on this kind of document. Do
+ not change the value of this variable.
+ """
+ self._documentTypeName = name
+
+
+ def GetDocumentSaved(self):
+ """
+ Returns True if the document has been saved. This method has been
+ added to wxPython and is not in wxWindows.
+ """
+ return self._savedYet
+
+
+ def SetDocumentSaved(self, saved = True):
+ """
+ Sets whether the document has been saved. This method has been
+ added to wxPython and is not in wxWindows.
+ """
+ self._savedYet = saved
+
+
+ def GetCommandProcessor(self):
+ """
+ Returns the command processor associated with this document.
+ """
+ return self._commandProcessor
+
+
+ def SetCommandProcessor(self, processor):
+ """
+ Sets the command processor to be used for this document. The document
+ will then be responsible for its deletion. Normally you should not
+ call this; override OnCreateCommandProcessor instead.
+ """
+ self._commandProcessor = processor
+
+
+ def IsModified(self):
+ """
+ Returns true if the document has been modified since the last save,
+ false otherwise. You may need to override this if your document view
+ maintains its own record of being modified (for example if using
+ wxTextWindow to view and edit the document).
+ """
+ return self._documentModified
+
+
+ def Modify(self, modify):
+ """
+ Call with true to mark the document as modified since the last save,
+ false otherwise. You may need to override this if your document view
+ maintains its own record of being modified (for example if using
+ xTextWindow to view and edit the document).
+ """
+ self._documentModified = modify
+
+
+ def GetViews(self):
+ """
+ Returns the list whose elements are the views on the document.
+ """
+ return self._documentViews
+
+
+ def GetDocumentTemplate(self):
+ """
+ Returns the template that created the document.
+ """
+ return self._documentTemplate
+
+
+ def SetDocumentTemplate(self, template):
+ """
+ Sets the template that created the document. Should only be called by
+ the framework.
+ """
+ self._documentTemplate = template
+
+
+ def DeleteContents(self):
+ """
+ Deletes the contents of the document. Override this method as
+ necessary.
+ """
+ return True
+
+
+ def Destroy(self):
+ """
+ Destructor. Removes itself from the document manager.
+ """
+ self.DeleteContents()
+ if self.GetDocumentManager():
+ self.GetDocumentManager().RemoveDocument(self)
+ wx.EvtHandler.Destroy(self)
+
+
+ def Close(self):
+ """
+ Closes the document, by calling OnSaveModified and then (if this true)
+ OnCloseDocument. This does not normally delete the document object:
+ use DeleteAllViews to do this implicitly.
+ """
+ if self.OnSaveModified():
+ if self.OnCloseDocument():
+ return True
+ else:
+ return False
+ else:
+ return False
+
+
+ def OnCloseDocument(self):
+ """
+ The default implementation calls DeleteContents (an empty
+ implementation) sets the modified flag to false. Override this to
+ supply additional behaviour when the document is closed with Close.
+ """
+ self.NotifyClosing()
+ self.DeleteContents()
+ self.Modify(False)
+ return True
+
+
+ def DeleteAllViews(self):
+ """
+ Calls wxView.Close and deletes each view. Deleting the final view will
+ implicitly delete the document itself, because the wxView destructor
+ calls RemoveView. This in turns calls wxDocument::OnChangedViewList,
+ whose default implemention is to save and delete the document if no
+ views exist.
+ """
+ manager = self.GetDocumentManager()
+ for view in self._documentViews:
+ if not view.Close():
+ return False
+ if self in manager.GetDocuments():
+ self.Destroy()
+ return True
+
+
+ def GetFirstView(self):
+ """
+ A convenience function to get the first view for a document, because
+ in many cases a document will only have a single view.
+ """
+ if len(self._documentViews) == 0:
+ return None
+ return self._documentViews[0]
+
+
+ def GetDocumentManager(self):
+ """
+ Returns the associated document manager.
+ """
+ if self._documentTemplate:
+ return self._documentTemplate.GetDocumentManager()
+ return None
+
+
+ def OnNewDocument(self):
+ """
+ The default implementation calls OnSaveModified and DeleteContents,
+ makes a default title for the document, and notifies the views that
+ the filename (in fact, the title) has changed.
+ """
+ if not self.OnSaveModified() or not self.OnCloseDocument():
+ return False
+ self.DeleteContents()
+ self.Modify(False)
+ self.SetDocumentSaved(False)
+ name = self.GetDocumentManager().MakeDefaultName()
+ self.SetTitle(name)
+ self.SetFilename(name, notifyViews = True)
+
+
+ def Save(self):
+ """
+ Saves the document by calling OnSaveDocument if there is an associated
+ filename, or SaveAs if there is no filename.
+ """
+ if not self.IsModified() and self._savedYet:
+ return True
+
+ if not self._documentFile or not self._savedYet:
+ return self.SaveAs()
+ return self.OnSaveDocument(self._documentFile)
+
+
+ def SaveAs(self):
+ """
+ Prompts the user for a file to save to, and then calls OnSaveDocument.
+ """
+ docTemplate = self.GetDocumentTemplate()
+ if not docTemplate:
+ return False
+
+ descr = docTemplate.GetDescription() + _(" (") + docTemplate.GetFileFilter() + _(") |") + docTemplate.GetFileFilter() # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+ filename = wx.FileSelector(_("Save As"),
+ docTemplate.GetDirectory(),
+ FileNameFromPath(self.GetFilename()),
+ docTemplate.GetDefaultExtension(),
+ wildcard = descr,
+ flags = wx.SAVE | wx.OVERWRITE_PROMPT,
+ parent = self.GetDocumentWindow())
+ if filename == "":
+ return False
+
+ name, ext = os.path.splitext(filename)
+ if ext == "":
+ filename += '.' + docTemplate.GetDefaultExtension()
+
+ self.SetFilename(filename)
+ self.SetTitle(FileNameFromPath(filename))
+
+ for view in self._documentViews:
+ view.OnChangeFilename()
+
+ if not self.OnSaveDocument(filename):
+ return False
+
+ if docTemplate.FileMatchesTemplate(filename):
+ self.GetDocumentManager().AddFileToHistory(filename)
+
+ return True
+
+
+ def OnSaveDocument(self, filename):
+ """
+ Constructs an output file for the given filename (which must
+ not be empty), and calls SaveObject. If SaveObject returns true, the
+ document is set to unmodified; otherwise, an error message box is
+ displayed.
+ """
+ if not filename:
+ return False
+
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("File Error")
+
+ backupFilename = None
+ try:
+ # if current file exists, move it to a safe place temporarily
+ if os.path.exists(filename):
+
+ # Check if read-only.
+ if not os.access(filename, os.W_OK):
+ wx.MessageBox("Could not save '%s'. No write permission to overwrite existing file." % FileNameFromPath(filename),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ self.GetDocumentWindow())
+ return False
+
+ i = 1
+ backupFilename = "%s.bak%s" % (filename, i)
+ while os.path.exists(backupFilename):
+ i += 1
+ backupFilename = "%s.bak%s" % (filename, i)
+ os.rename(filename, backupFilename)
+
+ fileObject = file(filename, 'w')
+ self.SaveObject(fileObject)
+
+ if backupFilename:
+ os.remove(backupFilename)
+ except:
+ # save failed, restore old file
+ if backupFilename:
+ os.remove(filename)
+ os.rename(backupFilename, filename)
+
+ wx.MessageBox("Could not save '%s'. %s" % (FileNameFromPath(filename), sys.exc_value),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ self.GetDocumentWindow())
+ return False
+
+ self.SetFilename(filename, True)
+ self.Modify(False)
+ self.SetDocumentSaved(True)
+ #if wx.Platform == '__WXMAC__': # Not yet implemented in wxPython
+ # wx.FileName(file).MacSetDefaultTypeAndCreator()
+ return True
+
+
+ def OnOpenDocument(self, filename):
+ """
+ Constructs an input file for the given filename (which must not
+ be empty), and calls LoadObject. If LoadObject returns true, the
+ document is set to unmodified; otherwise, an error message box is
+ displayed. The document's views are notified that the filename has
+ changed, to give windows an opportunity to update their titles. All of
+ the document's views are then updated.
+ """
+ if not self.OnSaveModified():
+ return False
+
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("File Error")
+
+ fileObject = file(filename, 'r')
+ try:
+ self.LoadObject(fileObject)
+ except:
+ wx.MessageBox("Could not open '%s'. %s" % (FileNameFromPath(filename), sys.exc_value),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ self.GetDocumentWindow())
+ return False
+
+ self.SetFilename(filename, True)
+ self.Modify(False)
+ self.SetDocumentSaved(True)
+ self.UpdateAllViews()
+ return True
+
+
+ def LoadObject(self, file):
+ """
+ Override this function and call it from your own LoadObject before
+ loading your own data. LoadObject is called by the framework
+ automatically when the document contents need to be loaded.
+
+ Note that the wxPython version simply sends you a Python file object,
+ so you can use pickle.
+ """
+ return True
+
+
+ def SaveObject(self, file):
+ """
+ Override this function and call it from your own SaveObject before
+ saving your own data. SaveObject is called by the framework
+ automatically when the document contents need to be saved.
+
+ Note that the wxPython version simply sends you a Python file object,
+ so you can use pickle.
+ """
+ return True
+
+
+ def Revert(self):
+ """
+ Override this function to revert the document to its last saved state.
+ """
+ return False
+
+
+ def GetPrintableName(self):
+ """
+ Copies a suitable document name into the supplied name buffer.
+ The default function uses the title, or if there is no title, uses the
+ filename; or if no filename, the string 'Untitled'.
+ """
+ if self._documentTitle:
+ return self._documentTitle
+ elif self._documentFile:
+ return FileNameFromPath(self._documentFile)
+ else:
+ return _("Untitled")
+
+
+ def GetDocumentWindow(self):
+ """
+ Intended to return a suitable window for using as a parent for
+ document-related dialog boxes. By default, uses the frame associated
+ with the first view.
+ """
+ if len(self._documentViews) > 0:
+ return self._documentViews[0].GetFrame()
+ else:
+ return wx.GetApp().GetTopWindow()
+
+
+ def OnCreateCommandProcessor(self):
+ """
+ Override this function if you want a different (or no) command
+ processor to be created when the document is created. By default, it
+ returns an instance of wxCommandProcessor.
+ """
+ return CommandProcessor()
+
+
+ def OnSaveModified(self):
+ """
+ If the document has been modified, prompts the user to ask if the
+ changes should be changed. If the user replies Yes, the Save function
+ is called. If No, the document is marked as unmodified and the
+ function succeeds. If Cancel, the function fails.
+ """
+ if not self.IsModified():
+ return True
+
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("Warning")
+
+ res = wx.MessageBox(_("Save changes to '%s'?") % self.GetPrintableName(),
+ msgTitle,
+ wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION,
+ self.GetDocumentWindow())
+
+ if res == wx.NO:
+ self.Modify(False)
+ return True
+ elif res == wx.YES:
+ return self.Save()
+ else: # elif res == wx.CANCEL:
+ return False
+
+
+ def Draw(context):
+ """
+ Called by printing framework to draw the view.
+ """
+ return True
+
+
+ def AddView(self, view):
+ """
+ If the view is not already in the list of views, adds the view and
+ calls OnChangedViewList.
+ """
+ if not view in self._documentViews:
+ self._documentViews.append(view)
+ self.OnChangedViewList()
+ return True
+
+
+ def RemoveView(self, view):
+ """
+ Removes the view from the document's list of views, and calls
+ OnChangedViewList.
+ """
+ if view in self._documentViews:
+ self._documentViews.remove(view)
+ self.OnChangedViewList()
+ return True
+
+
+ def OnCreate(self, path, flags):
+ """
+ The default implementation calls DeleteContents (an empty
+ implementation) sets the modified flag to false. Override this to
+ supply additional behaviour when the document is closed with Close.
+ """
+ return self.GetDocumentTemplate().CreateView(self, flags)
+
+
+ def OnChangedViewList(self):
+ """
+ Called when a view is added to or deleted from this document. The
+ default implementation saves and deletes the document if no views
+ exist (the last one has just been removed).
+ """
+ if len(self._documentViews) == 0:
+ if self.OnSaveModified():
+ pass # C version does a delete but Python will garbage collect
+
+
+ def UpdateAllViews(self, sender = None, hint = None):
+ """
+ Updates all views. If sender is non-NULL, does not update this view.
+ hint represents optional information to allow a view to optimize its
+ update.
+ """
+ for view in self._documentViews:
+ if view != sender:
+ view.OnUpdate(sender, hint)
+
+
+ def NotifyClosing(self):
+ """
+ Notifies the views that the document is going to close.
+ """
+ for view in self._documentViews:
+ view.OnClosingDocument()
+
+
+ def SetFilename(self, filename, notifyViews = False):
+ """
+ Sets the filename for this document. Usually called by the framework.
+ If notifyViews is true, wxView.OnChangeFilename is called for all
+ views.
+ """
+ self._documentFile = filename
+ if notifyViews:
+ for view in self._documentViews:
+ view.OnChangeFilename()
+
+
+class View(wx.EvtHandler):
+ """
+ The view class can be used to model the viewing and editing component of
+ an application's file-based data. It is part of the document/view
+ framework supported by wxWindows, and cooperates with the wxDocument,
+ wxDocTemplate and wxDocManager classes.
+ """
+
+ def __init__(self):
+ """
+ Constructor. Define your own default constructor to initialize
+ application-specific data.
+ """
+ wx.EvtHandler.__init__(self)
+ self._viewDocument = None
+ self._viewFrame = None
+
+
+ def Destroy(self):
+ """
+ Destructor. Removes itself from the document's list of views.
+ """
+ if self._viewDocument:
+ self._viewDocument.RemoveView(self)
+ wx.EvtHandler.Destroy(self)
+
+
+ def ProcessEvent(self, event):
+ """
+ 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.
+ """
+ if not self.GetDocument() or not self.GetDocument().ProcessEvent(event):
+ return False
+ else:
+ return True
+
+
+ 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 OnActivateView(self, activate, activeView, deactiveView):
+ """
+ Called when a view is activated by means of wxView::Activate. The
+ default implementation does nothing.
+ """
+ pass
+
+
+ def OnClosingDocument(self):
+ """
+ Override this to clean up the view when the document is being closed.
+ The default implementation does nothing.
+ """
+ pass
+
+
+ def OnDraw(self, dc):
+ """
+ Override this to draw the view for the printing framework. The
+ default implementation does nothing.
+ """
+ pass
+
+
+ def OnPrint(self, dc, info):
+ """
+ Override this to print the view for the printing framework. The
+ default implementation calls View.OnDraw.
+ """
+ self.OnDraw(dc)
+
+
+ def OnUpdate(self, sender, hint):
+ """
+ Called when the view should be updated. sender is a pointer to the
+ view that sent the update request, or NULL if no single view requested
+ the update (for instance, when the document is opened). hint is as yet
+ unused but may in future contain application-specific information for
+ making updating more efficient.
+ """
+ pass
+
+
+ def OnChangeFilename(self):
+ """
+ Called when the filename has changed. The default implementation
+ constructs a suitable title and sets the title of the view frame (if
+ any).
+ """
+ if self.GetFrame():
+ appName = wx.GetApp().GetAppName()
+ if not self.GetDocument():
+ if appName:
+ title = appName
+ else:
+ return
+ else:
+ if appName and not isinstance(self.GetFrame(), DocMDIChildFrame): # Don't need appname in title for MDI
+ title = appName + _(" - ")
+ else:
+ title = ''
+ self.GetFrame().SetTitle(title + self.GetDocument().GetPrintableName())
+
+
+ def GetDocument(self):
+ """
+ Returns the document associated with the view.
+ """
+ return self._viewDocument
+
+
+ def SetDocument(self, doc):
+ """
+ Associates the given document with the view. Normally called by the
+ framework.
+ """
+ self._viewDocument = doc
+ if doc:
+ doc.AddView(self)
+
+
+ def GetViewName(self):
+ """
+ Gets the name associated with the view (passed to the wxDocTemplate
+ constructor). Not currently used by the framework.
+ """
+ return self._viewTypeName
+
+
+ def SetViewName(self, name):
+ """
+ Sets the view type name. Should only be called by the framework.
+ """
+ self._viewTypeName = name
+
+
+ def Close(self, deleteWindow = True):
+ """
+ Closes the view by calling OnClose. If deleteWindow is true, this
+ function should delete the window associated with the view.
+ """
+ if self.OnClose(deleteWindow = deleteWindow):
+ return True
+ else:
+ return False
+
+
+ def Activate(self, activate = True):
+ """
+ Call this from your view frame's OnActivate member to tell the
+ framework which view is currently active. If your windowing system
+ doesn't call OnActivate, you may need to call this function from
+ OnMenuCommand or any place where you know the view must be active, and
+ the framework will need to get the current view.
+
+ The prepackaged view frame wxDocChildFrame calls wxView.Activate from
+ its OnActivate member and from its OnMenuCommand member.
+ """
+ if self.GetDocument() and self.GetDocumentManager():
+ self.OnActivateView(activate, self, self.GetDocumentManager().GetCurrentView())
+ self.GetDocumentManager().ActivateView(self, activate)
+
+
+ def OnClose(self, deleteWindow = True):
+ """
+ Implements closing behaviour. The default implementation calls
+ wxDocument.Close to close the associated document. Does not delete the
+ view. The application may wish to do some cleaning up operations in
+ this function, if a call to wxDocument::Close succeeded. For example,
+ if your application's all share the same window, you need to
+ disassociate the window from the view and perhaps clear the window. If
+ deleteWindow is true, delete the frame associated with the view.
+ """
+ if self.GetDocument():
+ return self.GetDocument().Close()
+ else:
+ return True
+
+
+ def OnCreate(self, doc, flags):
+ """
+ wxDocManager or wxDocument creates a wxView via a wxDocTemplate. Just
+ after the wxDocTemplate creates the wxView, it calls wxView::OnCreate.
+ In its OnCreate member function, the wxView can create a
+ wxDocChildFrame or a derived class. This wxDocChildFrame provides user
+ interface elements to view and/or edit the contents of the wxDocument.
+
+ By default, simply returns true. If the function returns false, the
+ view will be deleted.
+ """
+ return True
+
+
+ def OnCreatePrintout(self):
+ """
+ Returns a wxPrintout object for the purposes of printing. It should
+ create a new object every time it is called; the framework will delete
+ objects it creates.
+
+ By default, this function returns an instance of wxDocPrintout, which
+ prints and previews one page by calling wxView.OnDraw.
+
+ Override to return an instance of a class other than wxDocPrintout.
+ """
+ return DocPrintout(self)
+
+
+ def GetFrame(self):
+ """
+ Gets the frame associated with the view (if any). Note that this
+ "frame" is not a wxFrame at all in the generic MDI implementation
+ which uses the notebook pages instead of the frames and this is why
+ this method returns a wxWindow and not a wxFrame.
+ """
+ return self._viewFrame
+
+
+ def SetFrame(self, frame):
+ """
+ Sets the frame associated with this view. The application should call
+ this if possible, to tell the view about the frame. See GetFrame for
+ the explanation about the mismatch between the "Frame" in the method
+ name and the type of its parameter.
+ """
+ self._viewFrame = frame
+
+
+ def GetDocumentManager(self):
+ """
+ Returns the document manager instance associated with this view.
+ """
+ if self._viewDocument:
+ return self.GetDocument().GetDocumentManager()
+ else:
+ return None
+
+
+class DocTemplate(wx.Object):
+ """
+ The wxDocTemplate class is used to model the relationship between a
+ document class and a view class.
+ """
+
+
+ def __init__(self, manager, description, filter, dir, ext, docTypeName, viewTypeName, docType, viewType, flags = DEFAULT_TEMPLATE_FLAGS, icon = None):
+ """
+ Constructor. Create instances dynamically near the start of your
+ application after creating a wxDocManager instance, and before doing
+ any document or view operations.
+
+ manager is the document manager object which manages this template.
+
+ description is a short description of what the template is for. This
+ string will be displayed in the file filter list of Windows file
+ selectors.
+
+ filter is an appropriate file filter such as *.txt.
+
+ dir is the default directory to use for file selectors.
+
+ ext is the default file extension (such as txt).
+
+ docTypeName is a name that should be unique for a given type of
+ document, used for gathering a list of views relevant to a
+ particular document.
+
+ viewTypeName is a name that should be unique for a given view.
+
+ docClass is a Python class. If this is not supplied, you will need to
+ derive a new wxDocTemplate class and override the CreateDocument
+ member to return a new document instance on demand.
+
+ viewClass is a Python class. If this is not supplied, you will need to
+ derive a new wxDocTemplate class and override the CreateView member to
+ return a new view instance on demand.
+
+ flags is a bit list of the following:
+ wx.TEMPLATE_VISIBLE The template may be displayed to the user in
+ dialogs.
+
+ wx.TEMPLATE_INVISIBLE The template may not be displayed to the user in
+ dialogs.
+
+ wx.DEFAULT_TEMPLATE_FLAGS Defined as wxTEMPLATE_VISIBLE.
+ """
+ self._docManager = manager
+ self._description = description
+ self._fileFilter = filter
+ self._directory = dir
+ self._defaultExt = ext
+ self._docTypeName = docTypeName
+ self._viewTypeName = viewTypeName
+ self._docType = docType
+ self._viewType = viewType
+ self._flags = flags
+ self._icon = icon
+
+ self._docManager.AssociateTemplate(self)
+
+
+ def GetDefaultExtension(self):
+ """
+ Returns the default file extension for the document data, as passed to
+ the document template constructor.
+ """
+ return self._defaultExt
+
+
+ def SetDefaultExtension(self, defaultExt):
+ """
+ Sets the default file extension.
+ """
+ self._defaultExt = defaultExt
+
+
+ def GetDescription(self):
+ """
+ Returns the text description of this template, as passed to the
+ document template constructor.
+ """
+ return self._description
+
+
+ def SetDescription(self, description):
+ """
+ Sets the template description.
+ """
+ self._description = description
+
+
+ def GetDirectory(self):
+ """
+ Returns the default directory, as passed to the document template
+ constructor.
+ """
+ return self._directory
+
+
+ def SetDirectory(self, dir):
+ """
+ Sets the default directory.
+ """
+ self._directory = dir
+
+
+ def GetDocumentManager(self):
+ """
+ Returns the document manager instance for which this template was
+ created.
+ """
+ return self._docManager
+
+
+ def SetDocumentManager(self, manager):
+ """
+ Sets the document manager instance for which this template was
+ created. Should not be called by the application.
+ """
+ self._docManager = manager
+
+
+ def GetFileFilter(self):
+ """
+ Returns the file filter, as passed to the document template
+ constructor.
+ """
+ return self._fileFilter
+
+
+ def SetFileFilter(self, filter):
+ """
+ Sets the file filter.
+ """
+ self._fileFilter = filter
+
+
+ def GetFlags(self):
+ """
+ Returns the flags, as passed to the document template constructor.
+ (see the constructor description for more details).
+ """
+ return self._flags
+
+
+ def SetFlags(self, flags):
+ """
+ Sets the internal document template flags (see the constructor
+ description for more details).
+ """
+ self._flags = flags
+
+
+ def GetIcon(self):
+ """
+ Returns the icon, as passed to the document template
+ constructor. This method has been added to wxPython and is
+ not in wxWindows.
+ """
+ return self._icon
+
+
+ def SetIcon(self, flags):
+ """
+ Sets the icon. This method has been added to wxPython and is not
+ in wxWindows.
+ """
+ self._icon = icon
+
+
+ def GetDocumentType(self):
+ """
+ Returns the Python document class, as passed to the document template
+ constructor.
+ """
+ return self._docType
+
+
+ def GetViewType(self):
+ """
+ Returns the Python view class, as passed to the document template
+ constructor.
+ """
+ return self._viewType
+
+
+ def IsVisible(self):
+ """
+ Returns true if the document template can be shown in user dialogs,
+ false otherwise.
+ """
+ return (self._flags & TEMPLATE_VISIBLE) == TEMPLATE_VISIBLE
+
+
+ def GetDocumentName(self):
+ """
+ Returns the document type name, as passed to the document template
+ constructor.
+ """
+ return self._docTypeName
+
+
+ def GetViewName(self):
+ """
+ Returns the view type name, as passed to the document template
+ constructor.
+ """
+ return self._viewTypeName
+
+
+ def CreateDocument(self, path, flags):
+ """
+ Creates a new instance of the associated document class. If you have
+ not supplied a class to the template constructor, you will need to
+ override this function to return an appropriate document instance.
+ """
+ doc = self._docType()
+ doc.SetFilename(path)
+ doc.SetDocumentTemplate(self)
+ self.GetDocumentManager().AddDocument(doc)
+ doc.SetCommandProcessor(doc.OnCreateCommandProcessor())
+ if doc.OnCreate(path, flags):
+ return doc
+ else:
+ if doc in self.GetDocumentManager().GetDocuments():
+ doc.DeleteAllViews()
+ return None
+
+
+ def CreateView(self, doc, flags):
+ """
+ Creates a new instance of the associated document view. If you have
+ not supplied a class to the template constructor, you will need to
+ override this function to return an appropriate view instance.
+ """
+ view = self._viewType()
+ view.SetDocument(doc)
+ if view.OnCreate(doc, flags):
+ return view
+ else:
+ view.Destroy()
+ return None
+
+
+ def FileMatchesTemplate(self, path):
+ """
+ Returns True if the path's extension matches one of this template's
+ file filter extensions.
+ """
+ ext = FindExtension(path)
+ if not ext: return False
+ return ext in self.GetFileFilter()
+ # return self.GetDefaultExtension() == FindExtension(path)
+
+
+class DocManager(wx.EvtHandler):
+ """
+ The wxDocManager class is part of the document/view framework supported by
+ wxWindows, and cooperates with the wxView, wxDocument and wxDocTemplate
+ classes.
+ """
+
+ def __init__(self, flags = DEFAULT_DOCMAN_FLAGS, initialize = True):
+ """
+ Constructor. Create a document manager instance dynamically near the
+ start of your application before doing any document or view operations.
+
+ flags is used in the Python version to indicate whether the document
+ manager is in DOC_SDI or DOC_MDI mode.
+
+ If initialize is true, the Initialize function will be called to
+ create a default history list object. If you derive from wxDocManager,
+ you may wish to call the base constructor with false, and then call
+ Initialize in your own constructor, to allow your own Initialize or
+ OnCreateFileHistory functions to be called.
+ """
+
+ wx.EvtHandler.__init__(self)
+
+ self._defaultDocumentNameCounter = 1
+ self._flags = flags
+ self._currentView = None
+ self._lastActiveView = None
+ self._maxDocsOpen = 10000
+ self._fileHistory = None
+ self._templates = []
+ self._docs = []
+ self._lastDirectory = ""
+
+ if initialize:
+ self.Initialize()
+
+ wx.EVT_MENU(self, wx.ID_OPEN, self.OnFileOpen)
+ wx.EVT_MENU(self, wx.ID_CLOSE, self.OnFileClose)
+ wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.OnFileCloseAll)
+ wx.EVT_MENU(self, wx.ID_REVERT, self.OnFileRevert)
+ wx.EVT_MENU(self, wx.ID_NEW, self.OnFileNew)
+ wx.EVT_MENU(self, wx.ID_SAVE, self.OnFileSave)
+ wx.EVT_MENU(self, wx.ID_SAVEAS, self.OnFileSaveAs)
+ wx.EVT_MENU(self, wx.ID_UNDO, self.OnUndo)
+ wx.EVT_MENU(self, wx.ID_REDO, self.OnRedo)
+ wx.EVT_MENU(self, wx.ID_PRINT, self.OnPrint)
+ wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.OnPrintSetup)
+ wx.EVT_MENU(self, wx.ID_PREVIEW, self.OnPreview)
+
+ wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.OnUpdateFileOpen)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.OnUpdateFileClose)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.OnUpdateFileCloseAll)
+ wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.OnUpdateFileRevert)
+ wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.OnUpdateFileNew)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.OnUpdateFileSave)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.OnUpdateFileSaveAs)
+ wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.OnUpdateUndo)
+ wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.OnUpdateRedo)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.OnUpdatePrint)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.OnUpdatePrintSetup)
+ wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.OnUpdatePreview)
+
+
+ def Destroy(self):
+ """
+ Destructor.
+ """
+ self.Clear()
+ wx.EvtHandler.Destroy(self)
+
+
+ def GetFlags(self):
+ """
+ Returns the document manager's flags. This method has been
+ added to wxPython and is not in wxWindows.
+ """
+ return self._flags
+
+
+ def CloseDocument(self, doc, force = True):
+ """
+ Closes the specified document.
+ """
+ if doc.Close() or force:
+ doc.DeleteAllViews()
+ if doc in self._docs:
+ doc.Destroy()
+ return True
+ return False
+
+
+ def CloseDocuments(self, force = True):
+ """
+ Closes all currently opened documents.
+ """
+ for document in self._docs[::-1]: # Close in lifo (reverse) order. We clone the list to make sure we go through all docs even as they are deleted
+ if not self.CloseDocument(document, force):
+ return False
+ document.DeleteAllViews() # Implicitly delete the document when the last view is removed
+ return True
+
+
+ def Clear(self, force = True):
+ """
+ Closes all currently opened document by callling CloseDocuments and
+ clears the document manager's templates.
+ """
+ if not self.CloseDocuments(force):
+ return False
+ self._templates = []
+ return True
+
+
+ def Initialize(self):
+ """
+ Initializes data; currently just calls OnCreateFileHistory. Some data
+ cannot always be initialized in the constructor because the programmer
+ must be given the opportunity to override functionality. In fact
+ Initialize is called from the wxDocManager constructor, but this can
+ be vetoed by passing false to the second argument, allowing the
+ derived class's constructor to call Initialize, possibly calling a
+ different OnCreateFileHistory from the default.
+
+ The bottom line: if you're not deriving from Initialize, forget it and
+ construct wxDocManager with no arguments.
+ """
+ self.OnCreateFileHistory()
+ return True
+
+
+ def OnCreateFileHistory(self):
+ """
+ A hook to allow a derived class to create a different type of file
+ history. Called from Initialize.
+ """
+ self._fileHistory = wx.FileHistory()
+
+
+ def OnFileClose(self, event):
+ """
+ Closes and deletes the currently active document.
+ """
+ doc = self.GetCurrentDocument()
+ if doc:
+ doc.DeleteAllViews()
+ if doc in self._docs:
+ self._docs.remove(doc)
+
+
+ def OnFileCloseAll(self, event):
+ """
+ Closes and deletes all the currently opened documents.
+ """
+ return self.CloseDocuments(force = False)
+
+
+ def OnFileNew(self, event):
+ """
+ Creates a new document and reads in the selected file.
+ """
+ self.CreateDocument('', DOC_NEW)
+
+
+ def OnFileOpen(self, event):
+ """
+ Creates a new document and reads in the selected file.
+ """
+ if not self.CreateDocument('', 0):
+ self.OnOpenFileFailure()
+
+
+ def OnFileRevert(self, event):
+ """
+ Reverts the current document by calling wxDocument.Save for the current
+ document.
+ """
+ doc = self.GetCurrentDocument()
+ if not doc:
+ return
+ doc.Revert()
+
+
+ def OnFileSave(self, event):
+ """
+ Saves the current document by calling wxDocument.Save for the current
+ document.
+ """
+ doc = self.GetCurrentDocument()
+ if not doc:
+ return
+ doc.Save()
+
+
+ def OnFileSaveAs(self, event):
+ """
+ Calls wxDocument.SaveAs for the current document.
+ """
+ doc = self.GetCurrentDocument()
+ if not doc:
+ return
+ doc.SaveAs()
+
+
+ def OnPrint(self, event):
+ """
+ Prints the current document by calling its View's OnCreatePrintout
+ method.
+ """
+ view = self.GetCurrentView()
+ if not view:
+ return
+
+ printout = view.OnCreatePrintout()
+ if printout:
+ pdd = wx.PrintDialogData()
+ printer = wx.Printer(pdd)
+ printer.Print(view.GetFrame(), printout) # , True)
+
+
+ def OnPrintSetup(self, event):
+ """
+ Presents the print setup dialog.
+ """
+ view = self.GetCurrentView()
+ if view:
+ parentWin = view.GetFrame()
+ else:
+ parentWin = wx.GetApp().GetTopWindow()
+
+ data = wx.PrintDialogData()
+ printDialog = wx.PrintDialog(parentWin, data)
+ printDialog.GetPrintDialogData().SetSetupDialog(True)
+ printDialog.ShowModal()
+ # TODO: Confirm that we don't have to remember PrintDialogData
+
+
+ def OnPreview(self, event):
+ """
+ Previews the current document by calling its View's OnCreatePrintout
+ method.
+ """
+ view = self.GetCurrentView()
+ if not view:
+ return
+
+ printout = view.OnCreatePrintout()
+ if printout:
+ # Pass two printout objects: for preview, and possible printing.
+ preview = wx.PrintPreview(printout, view.OnCreatePrintout())
+ # wxWindows source doesn't use base frame's pos, size, and icon, but did it this way so it would work like MS Office etc.
+ mimicFrame = wx.GetApp().GetTopWindow()
+ frame = wx.PreviewFrame(preview, mimicFrame, _("Print Preview"), mimicFrame.GetPosition(), mimicFrame.GetSize())
+ frame.SetIcon(mimicFrame.GetIcon())
+ frame.SetTitle(mimicFrame.GetTitle() + _(" - Preview"))
+ frame.Initialize()
+ frame.Show(True)
+
+
+ def OnUndo(self, event):
+ """
+ Issues an Undo command to the current document's command processor.
+ """
+ doc = self.GetCurrentDocument()
+ if not doc:
+ return
+ if doc.GetCommandProcessor():
+ doc.GetCommandProcessor().Undo()
+
+
+ def OnRedo(self, event):
+ """
+ Issues a Redo command to the current document's command processor.
+ """
+ doc = self.GetCurrentDocument()
+ if not doc:
+ return
+ if doc.GetCommandProcessor():
+ doc.GetCommandProcessor().Redo()
+
+
+ def OnUpdateFileOpen(self, event):
+ """
+ Updates the user interface for the File Open command.
+ """
+ event.Enable(True)
+
+
+ def OnUpdateFileClose(self, event):
+ """
+ Updates the user interface for the File Close command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def OnUpdateFileCloseAll(self, event):
+ """
+ Updates the user interface for the File Close All command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def OnUpdateFileRevert(self, event):
+ """
+ Updates the user interface for the File Revert command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def OnUpdateFileNew(self, event):
+ """
+ Updates the user interface for the File New command.
+ """
+ return True
+
+
+ def OnUpdateFileSave(self, event):
+ """
+ Updates the user interface for the File Save command.
+ """
+ doc = self.GetCurrentDocument()
+ event.Enable(doc != None and doc.IsModified())
+
+
+ def OnUpdateFileSaveAs(self, event):
+ """
+ Updates the user interface for the File Save As command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def OnUpdateUndo(self, event):
+ """
+ Updates the user interface for the Undo command.
+ """
+ doc = self.GetCurrentDocument()
+ event.Enable(doc != None and doc.GetCommandProcessor() != None and doc.GetCommandProcessor().CanUndo())
+ if doc and doc.GetCommandProcessor():
+ doc.GetCommandProcessor().SetMenuStrings()
+ else:
+ event.SetText(_("Undo") + '\t' + _('Ctrl+Z'))
+
+
+ def OnUpdateRedo(self, event):
+ """
+ Updates the user interface for the Redo command.
+ """
+ doc = self.GetCurrentDocument()
+ event.Enable(doc != None and doc.GetCommandProcessor() != None and doc.GetCommandProcessor().CanRedo())
+ if doc and doc.GetCommandProcessor():
+ doc.GetCommandProcessor().SetMenuStrings()
+ else:
+ event.SetText(_("Redo") + '\t' + _('Ctrl+Y'))
+
+
+ def OnUpdatePrint(self, event):
+ """
+ Updates the user interface for the Print command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def OnUpdatePrintSetup(self, event):
+ """
+ Updates the user interface for the Print Setup command.
+ """
+ return True
+
+
+ def OnUpdatePreview(self, event):
+ """
+ Updates the user interface for the Print Preview command.
+ """
+ event.Enable(self.GetCurrentDocument() != None)
+
+
+ def GetCurrentView(self):
+ """
+ Returns the currently active view.
+ """
+ if self._currentView:
+ return self._currentView
+ if len(self._docs) == 1:
+ return self._docs[0].GetFirstView()
+ return None
+
+
+ def GetLastActiveView(self):
+ """
+ Returns the last active view. This is used in the SDI framework where dialogs can be mistaken for a view
+ and causes the framework to deactivete the current view. This happens when something like a custom dialog box used
+ to operate on the current view is shown.
+ """
+ if len(self._docs) >= 1:
+ return self._lastActiveView
+ else:
+ return None
+
+
+ def ProcessEvent(self, event):
+ """
+ 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.
+ """
+ view = self.GetCurrentView()
+ if view:
+ if view.ProcessEvent(event):
+ return True
+ id = event.GetId()
+ if id == wx.ID_OPEN:
+ self.OnFileOpen(event)
+ return True
+ elif id == wx.ID_CLOSE:
+ self.OnFileClose(event)
+ return True
+ elif id == wx.ID_CLOSE_ALL:
+ self.OnFileCloseAll(event)
+ return True
+ elif id == wx.ID_REVERT:
+ self.OnFileRevert(event)
+ return True
+ elif id == wx.ID_NEW:
+ self.OnFileNew(event)
+ return True
+ elif id == wx.ID_SAVE:
+ self.OnFileSave(event)
+ return True
+ elif id == wx.ID_SAVEAS:
+ self.OnFileSaveAs(event)
+ return True
+ elif id == wx.ID_UNDO:
+ self.OnUndo(event)
+ return True
+ elif id == wx.ID_REDO:
+ self.OnRedo(event)
+ return True
+ elif id == wx.ID_PRINT:
+ self.OnPrint(event)
+ return True
+ elif id == wx.ID_PRINT_SETUP:
+ self.OnPrintSetup(event)
+ return True
+ elif id == wx.ID_PREVIEW:
+ self.OnPreview(event)
+ return True
+ else:
+ 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.
+ """
+ id = event.GetId()
+ view = self.GetCurrentView()
+ if view:
+ if view.ProcessUpdateUIEvent(event):
+ return True
+ if id == wx.ID_OPEN:
+ self.OnUpdateFileOpen(event)
+ return True
+ elif id == wx.ID_CLOSE:
+ self.OnUpdateFileClose(event)
+ return True
+ elif id == wx.ID_CLOSE_ALL:
+ self.OnUpdateFileCloseAll(event)
+ return True
+ elif id == wx.ID_REVERT:
+ self.OnUpdateFileRevert(event)
+ return True
+ elif id == wx.ID_NEW:
+ self.OnUpdateFileNew(event)
+ return True
+ elif id == wx.ID_SAVE:
+ self.OnUpdateFileSave(event)
+ return True
+ elif id == wx.ID_SAVEAS:
+ self.OnUpdateFileSaveAs(event)
+ return True
+ elif id == wx.ID_UNDO:
+ self.OnUpdateUndo(event)
+ return True
+ elif id == wx.ID_REDO:
+ self.OnUpdateRedo(event)
+ return True
+ elif id == wx.ID_PRINT:
+ self.OnUpdatePrint(event)
+ return True
+ elif id == wx.ID_PRINT_SETUP:
+ self.OnUpdatePrintSetup(event)
+ return True
+ elif id == wx.ID_PREVIEW:
+ self.OnUpdatePreview(event)
+ return True
+ else:
+ return False
+
+
+ def CreateDocument(self, path, flags = 0):
+ """
+ Creates a new document in a manner determined by the flags parameter,
+ which can be:
+
+ wx.lib.docview.DOC_NEW Creates a fresh document.
+ wx.lib.docview.DOC_SILENT Silently loads the given document file.
+
+ If wx.lib.docview.DOC_NEW is present, a new document will be created and returned,
+ possibly after asking the user for a template to use if there is more
+ than one document template. If wx.lib.docview.DOC_SILENT is present, a new document
+ will be created and the given file loaded into it. If neither of these
+ flags is present, the user will be presented with a file selector for
+ the file to load, and the template to use will be determined by the
+ extension (Windows) or by popping up a template choice list (other
+ platforms).
+
+ If the maximum number of documents has been reached, this function
+ will delete the oldest currently loaded document before creating a new
+ one.
+
+ wxPython version supports the document manager's wx.lib.docview.DOC_OPEN_ONCE flag.
+ """
+ templates = []
+ for temp in self._templates:
+ if temp.IsVisible():
+ templates.append(temp)
+ if len(templates) == 0:
+ return None
+
+ if len(self.GetDocuments()) >= self._maxDocsOpen:
+ doc = self.GetDocuments()[0]
+ if not self.CloseDocument(doc, False):
+ return None
+
+ if flags & DOC_NEW:
+ if len(templates) == 1:
+ temp = templates[0]
+ newDoc = temp.CreateDocument(path, flags)
+ if newDoc:
+ newDoc.SetDocumentName(temp.GetDocumentName())
+ newDoc.SetDocumentTemplate(temp)
+ newDoc.OnNewDocument()
+ return newDoc
+
+ temp = self.SelectDocumentType(templates)
+ if temp:
+ newDoc = temp.CreateDocument(path, flags)
+ if newDoc:
+ newDoc.SetDocumentName(temp.GetDocumentName())
+ newDoc.SetDocumentTemplate(temp)
+ newDoc.OnNewDocument()
+ return newDoc
+ else:
+ return None
+
+ # Existing document
+ if self.GetFlags() & DOC_OPEN_ONCE:
+ for document in self._docs:
+ if document.GetFilename() == path:
+ firstView = document.GetFirstView()
+ if firstView and firstView.GetFrame():
+ firstView.GetFrame().SetFocus() # Not in wxWindows code but useful nonetheless
+ if hasattr(firstView.GetFrame(), "IsIconized") and firstView.GetFrame().IsIconized(): # Not in wxWindows code but useful nonetheless
+ firstView.GetFrame().Iconize(False)
+ return None
+
+ if flags & DOC_SILENT:
+ temp = self.FindTemplateForPath(path)
+ else:
+ temp, path = self.SelectDocumentPath(templates, path, flags)
+
+ if temp:
+ newDoc = temp.CreateDocument(path, flags)
+ if newDoc:
+ newDoc.SetDocumentName(temp.GetDocumentName())
+ newDoc.SetDocumentTemplate(temp)
+ if not newDoc.OnOpenDocument(path):
+ newDoc.DeleteAllViews() # Implicitly deleted by DeleteAllViews
+ newDoc.GetFirstView().GetFrame().Destroy() # DeleteAllViews doesn't get rid of the frame, so we'll explicitly destroy it.
+ return None
+ self.AddFileToHistory(path)
+ return newDoc
+
+ return None
+
+
+ def CreateView(self, document, flags = 0):
+ """
+ Creates a new view for the given document. If more than one view is
+ allowed for the document (by virtue of multiple templates mentioning
+ the same document type), a choice of view is presented to the user.
+ """
+ templates = []
+ for temp in self._templates:
+ if temp.IsVisible():
+ if temp.GetDocumentName() == doc.GetDocumentName():
+ templates.append(temp)
+ if len(templates) == 0:
+ return None
+
+ if len(templates) == 1:
+ temp = templates[0]
+ view = temp.CreateView(doc, flags)
+ if view:
+ view.SetViewName(temp.GetViewName())
+ return view
+
+ temp = SelectViewType(templates)
+ if temp:
+ view = temp.CreateView(doc, flags)
+ if view:
+ view.SetViewName(temp.GetViewName())
+ return view
+ else:
+ return None
+
+
+ def DeleteTemplate(self, template, flags):
+ """
+ Placeholder, not yet implemented in wxWindows.
+ """
+ pass
+
+
+ def FlushDoc(self, doc):
+ """
+ Placeholder, not yet implemented in wxWindows.
+ """
+ return False
+
+
+ def MatchTemplate(self, path):
+ """
+ Placeholder, not yet implemented in wxWindows.
+ """
+ return None
+
+
+ def GetCurrentDocument(self):
+ """
+ Returns the document associated with the currently active view (if any).
+ """
+ view = self.GetCurrentView()
+ if view:
+ return view.GetDocument()
+ else:
+ return None
+
+
+ def MakeDefaultName(self):
+ """
+ Returns a suitable default name. This is implemented by appending an
+ integer counter to the string "Untitled" and incrementing the counter.
+ """
+ name = _("Untitled %d") % self._defaultDocumentNameCounter
+ self._defaultDocumentNameCounter = self._defaultDocumentNameCounter + 1
+ return name
+
+
+ def MakeFrameTitle(self):
+ """
+ Returns a suitable title for a document frame. This is implemented by
+ appending the document name to the application name.
+ """
+ appName = wx.GetApp().GetAppName()
+ if not doc:
+ title = appName
+ else:
+ docName = doc.GetPrintableName()
+ title = docName + _(" - ") + appName
+ return title
+
+
+ def AddFileToHistory(self, fileName):
+ """
+ Adds a file to the file history list, if we have a pointer to an
+ appropriate file menu.
+ """
+ if self._fileHistory:
+ self._fileHistory.AddFileToHistory(fileName)
+
+
+ def RemoveFileFromHistory(self, i):
+ """
+ Removes a file from the file history list, if we have a pointer to an
+ appropriate file menu.
+ """
+ if self._fileHistory:
+ self._fileHistory.RemoveFileFromHistory(i)
+
+
+ def GetFileHistory(self):
+ """
+ Returns the file history.
+ """
+ return self._fileHistory
+
+
+ def GetHistoryFile(self, i):
+ """
+ Returns the file at index i from the file history.
+ """
+ if self._fileHistory:
+ return self._fileHistory.GetHistoryFile(i)
+ else:
+ return None
+
+
+ def FileHistoryUseMenu(self, menu):
+ """
+ Use this menu for appending recently-visited document filenames, for
+ convenient access. Calling this function with a valid menu enables the
+ history list functionality.
+
+ Note that you can add multiple menus using this function, to be
+ managed by the file history object.
+ """
+ if self._fileHistory:
+ self._fileHistory.UseMenu(menu)
+
+
+ def FileHistoryRemoveMenu(self, menu):
+ """
+ Removes the given menu from the list of menus managed by the file
+ history object.
+ """
+ if self._fileHistory:
+ self._fileHistory.RemoveMenu(menu)
+
+
+ def FileHistoryLoad(self, config):
+ """
+ Loads the file history from a config object.
+ """
+ if self._fileHistory:
+ self._fileHistory.Load(config)
+
+
+ def FileHistorySave(self, config):
+ """
+ Saves the file history into a config object. This must be called
+ explicitly by the application.
+ """
+ if self._fileHistory:
+ self._fileHistory.Save(config)
+
+
+ def FileHistoryAddFilesToMenu(self, menu = None):
+ """
+ Appends the files in the history list, to all menus managed by the
+ file history object.
+
+ If menu is specified, appends the files in the history list to the
+ given menu only.
+ """
+ if self._fileHistory:
+ if menu:
+ self._fileHistory.AddFilesToThisMenu(menu)
+ else:
+ self._fileHistory.AddFilesToMenu()
+
+
+ def GetHistoryFilesCount(self):
+ """
+ Returns the number of files currently stored in the file history.
+ """
+ if self._fileHistory:
+ return self._fileHistory.GetNoHistoryFiles()
+ else:
+ return 0
+
+
+ def FindTemplateForPath(self, path):
+ """
+ Given a path, try to find template that matches the extension. This is
+ only an approximate method of finding a template for creating a
+ document.
+ """
+ for temp in self._templates:
+ if temp.FileMatchesTemplate(path):
+ return temp
+ return None
+
+
+ def FindSuitableParent(self):
+ """
+ Returns a parent frame or dialog, either the frame with the current
+ focus or if there is no current focus the application's top frame.
+ """
+ parent = wx.GetApp().GetTopWindow()
+ focusWindow = wx.Window_FindFocus()
+ if focusWindow:
+ while focusWindow and not isinstance(focusWindow, wx.Dialog) and not isinstance(focusWindow, wx.Frame):
+ focusWindow = focusWindow.GetParent()
+ if focusWindow:
+ parent = focusWindow
+ return parent
+
+
+ def SelectDocumentPath(self, templates, flags, save):
+ """
+ Under Windows, pops up a file selector with a list of filters
+ corresponding to document templates. The wxDocTemplate corresponding
+ to the selected file's extension is returned.
+
+ On other platforms, if there is more than one document template a
+ choice list is popped up, followed by a file selector.
+
+ This function is used in wxDocManager.CreateDocument.
+ """
+ if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
+ allfilter = ''
+ descr = ''
+ for temp in templates:
+ if temp.IsVisible():
+ if len(descr) > 0:
+ descr = descr + _('|')
+ allfilter = allfilter + _(';')
+ descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter() # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+ allfilter = allfilter + temp.GetFileFilter()
+ descr = _("All") + _(" (") + allfilter + _(") |") + allfilter + _('|') + descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+ else:
+ descr = _("*.*")
+
+ path = wx.FileSelector(_("Select a File"),
+ self._lastDirectory,
+ _(""),
+ wildcard = descr,
+ flags = wx.HIDE_READONLY,
+ parent = self.FindSuitableParent())
+ if path:
+ if not FileExists(path):
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("File Error")
+ wx.MessageBox("Could not open '%s'." % FileNameFromPath(path),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ parent)
+ return (None, None)
+ self._lastDirectory = PathOnly(path)
+
+ theTemplate = self.FindTemplateForPath(path)
+ return (theTemplate, path)
+
+ return (None, None)
+
+
+ def OnOpenFileFailure(self):
+ """
+ Called when there is an error opening a file.
+ """
+ pass
+
+
+ def SelectDocumentType(self, temps, sort = False):
+ """
+ Returns a document template by asking the user (if there is more than
+ one template). This function is used in wxDocManager.CreateDocument.
+
+ Parameters
+
+ templates - list of templates from which to choose a desired template.
+
+ sort - If more than one template is passed in in templates, then this
+ parameter indicates whether the list of templates that the user will
+ have to choose from is sorted or not when shown the choice box dialog.
+ Default is false.
+ """
+ templates = []
+ for temp in temps:
+ if temp.IsVisible():
+ want = True
+ for temp2 in templates:
+ if temp.GetDocumentName() == temp2.GetDocumentName() and temp.GetViewName() == temp2.GetViewName():
+ want = False
+ break
+ if want:
+ templates.append(temp)
+
+ if len(templates) == 0:
+ return None
+ elif len(templates) == 1:
+ return template[0]
+
+ if sort:
+ def tempcmp(a, b):
+ return cmp(a.GetDescription(), b.GetDescription())
+ templates.sort(tempcmp)
+
+ strings = []
+ for temp in templates:
+ strings.append(temp.GetDescription())
+
+ res = wx.GetSingleChoiceIndex(_("Select a document type:"),
+ _("Documents"),
+ strings,
+ self.FindSuitableParent())
+ if res == -1:
+ return None
+ return templates[res]
+
+
+ def SelectViewType(self, temps, sort = False):
+ """
+ Returns a document template by asking the user (if there is more than one template), displaying a list of valid views. This function is used in wxDocManager::CreateView. The dialog normally will not appear because the array of templates only contains those relevant to the document in question, and often there will only be one such.
+ """
+ templates = []
+ strings = []
+ for temp in temps:
+ if temp.IsVisible() and temp.GetViewTypeName():
+ if temp.GetViewName() not in strings:
+ templates.append(temp)
+ strings.append(temp.GetViewTypeName())
+
+ if len(templates) == 0:
+ return None
+ elif len(templates) == 1:
+ return templates[0]
+
+ if sort:
+ def tempcmp(a, b):
+ return cmp(a.GetViewTypeName(), b.GetViewTypeName())
+ templates.sort(tempcmp)
+
+ res = wx.GetSingleChoiceIndex(_("Select a document view:"),
+ _("Views"),
+ strings,
+ self.FindSuitableParent())
+ if res == -1:
+ return None
+ return templates[res]
+
+
+ def GetTemplates(self):
+ """
+ Returns the document manager's template list. This method has been added to
+ wxPython and is not in wxWindows.
+ """
+ return self._templates
+
+
+ def AssociateTemplate(self, docTemplate):
+ """
+ Adds the template to the document manager's template list.
+ """
+ if docTemplate not in self._templates:
+ self._templates.append(docTemplate)
+
+
+ def DisassociateTemplate(self, docTemplate):
+ """
+ Removes the template from the list of templates.
+ """
+ self._templates.remove(docTemplate)
+
+
+ def AddDocument(self, document):
+ """
+ Adds the document to the list of documents.
+ """
+ if document not in self._docs:
+ self._docs.append(document)
+
+
+ def RemoveDocument(self, doc):
+ """
+ Removes the document from the list of documents.
+ """
+ if doc in self._docs:
+ self._docs.remove(doc)
+
+
+ def ActivateView(self, view, activate = True, deleting = False):
+ """
+ Sets the current view.
+ """
+ if activate:
+ self._currentView = view
+ self._lastActiveView = view
+ else:
+ self._currentView = None
+
+
+ def GetMaxDocsOpen(self):
+ """
+ Returns the number of documents that can be open simultaneously.
+ """
+ return self._maxDocsOpen
+
+
+ def SetMaxDocsOpen(self, maxDocsOpen):
+ """
+ Sets the maximum number of documents that can be open at a time. By
+ default, this is 10,000. If you set it to 1, existing documents will
+ be saved and deleted when the user tries to open or create a new one
+ (similar to the behaviour of Windows Write, for example). Allowing
+ multiple documents gives behaviour more akin to MS Word and other
+ Multiple Document Interface applications.
+ """
+ self._maxDocsOpen = maxDocsOpen
+
+
+ def GetDocuments(self):
+ """
+ Returns the list of documents.
+ """
+ return self._docs
+
+
+class DocParentFrame(wx.Frame):
+ """
+ The wxDocParentFrame class provides a default top-level frame for
+ applications using the document/view framework. This class can only be
+ used for SDI (not MDI) parent frames.
+
+ It cooperates with the wxView, wxDocument, wxDocManager and wxDocTemplates
+ classes.
+ """
+
+ def __init__(self, manager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
+ """
+ Constructor. Note that the event table must be rebuilt for the
+ frame since the EvtHandler is not virtual.
+ """
+ wx.Frame.__init__(self, frame, id, title, pos, size, style)
+ self._docManager = manager
+
+ wx.EVT_CLOSE(self, self.OnCloseWindow)
+
+ wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
+ wx.EVT_MENU_RANGE(self, wx.ID_FILE1, wx.ID_FILE9, self.OnMRUFile)
+
+ wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
+
+ wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
+
+
+ def ProcessEvent(self, event):
+ """
+ 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 self._docManager and self._docManager.ProcessEvent(event)
+
+
+ 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 self._docManager and self._docManager.ProcessUpdateUIEvent(event)
+
+
+ def OnExit(self, event):
+ """
+ Called when File/Exit is chosen and closes the window.
+ """
+ self.Close()
+
+
+ def OnMRUFile(self, event):
+ """
+ Opens the appropriate file when it is selected from the file history
+ menu.
+ """
+ n = event.GetId() - wx.ID_FILE1
+ filename = self._docManager.GetHistoryFile(n)
+ if filename:
+ self._docManager.CreateDocument(filename, DOC_SILENT)
+ else:
+ self._docManager.RemoveFileFromHistory(n)
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("File Error")
+ wx.MessageBox("The file '%s' doesn't exist and couldn't be opened.\nIt has been removed from the most recently used files list" % FileNameFromPath(file),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ self)
+
+
+ def OnCloseWindow(self, event):
+ """
+ Deletes all views and documents. If no user input cancelled the
+ operation, the frame will be destroyed and the application will exit.
+ """
+ if self._docManager.Clear(not event.CanVeto()):
+ self.Destroy()
+ else:
+ event.Veto()
+
+
+class DocChildFrame(wx.Frame):
+ """
+ The wxDocChildFrame class provides a default frame for displaying
+ documents on separate windows. This class can only be used for SDI (not
+ MDI) child frames.
+
+ The class is part of the document/view framework supported by wxWindows,
+ and cooperates with the wxView, wxDocument, wxDocManager and wxDocTemplate
+ classes.
+ """
+
+
+ def __init__(self, doc, view, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
+ """
+ Constructor. Note that the event table must be rebuilt for the
+ frame since the EvtHandler is not virtual.
+ """
+ wx.Frame.__init__(self, frame, id, title, pos, size, style, name)
+ wx.EVT_ACTIVATE(self, self.OnActivate)
+ wx.EVT_CLOSE(self, self.OnCloseWindow)
+ self._childDocument = doc
+ self._childView = view
+ if view:
+ view.SetFrame(self)
+
+ wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
+
+ wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
+
+
+ def ProcessEvent(self, event):
+ """
+ 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.
+ """
+ if self._childView:
+ self._childView.Activate(True)
+ if not self._childView or not self._childView.ProcessEvent(event):
+ # IsInstance not working, but who cares just send all the commands up since this isn't a real ProcessEvent like wxWindows
+ # if not isinstance(event, wx.CommandEvent) or not self.GetParent() or not self.GetParent().ProcessEvent(event):
+ if not self.GetParent() or not self.GetParent().ProcessEvent(event):
+ return False
+ else:
+ return True
+ else:
+ return True
+
+
+ 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.
+ """
+ if self.GetParent():
+ self.GetParent().ProcessUpdateUIEvent(event)
+ else:
+ return False
+
+
+ def OnActivate(self, event):
+ """
+ Activates the current view.
+ """
+ # wx.Frame.OnActivate(event) This is in the wxWindows docview demo but there is no such method in wxPython, so do a Raise() instead
+ if self._childView:
+ self._childView.Activate(event.GetActive())
+
+
+ def OnCloseWindow(self, event):
+ """
+ Closes and deletes the current view and document.
+ """
+ if self._childView:
+ ans = False
+ if not event.CanVeto():
+ ans = True
+ else:
+ ans = self._childView.Close(deleteWindow = False)
+
+ if ans:
+ self._childView.Activate(False)
+ self._childView.Destroy()
+ self._childView = None
+ if self._childDocument:
+ self._childDocument.Destroy() # This isn't in the wxWindows codebase but the document needs to be disposed of somehow
+ self._childDocument = None
+ self.Destroy()
+ else:
+ event.Veto()
+ else:
+ event.Veto()
+
+
+ def GetDocument(self):
+ """
+ Returns the document associated with this frame.
+ """
+ return self._childDocument
+
+
+ def SetDocument(self, document):
+ """
+ Sets the document for this frame.
+ """
+ self._childDocument = document
+
+
+ def GetView(self):
+ """
+ Returns the view associated with this frame.
+ """
+ return self._childView
+
+
+ def SetView(self, view):
+ """
+ Sets the view for this frame.
+ """
+ self._childView = view
+
+
+class DocMDIParentFrame(wx.MDIParentFrame):
+ """
+ The wxDocMDIParentFrame class provides a default top-level frame for
+ applications using the document/view framework. This class can only be
+ used for MDI parent frames.
+
+ It cooperates with the wxView, wxDocument, wxDocManager and wxDocTemplate
+ classes.
+ """
+
+
+ def __init__(self, manager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
+ """
+ Constructor. Note that the event table must be rebuilt for the
+ frame since the EvtHandler is not virtual.
+ """
+ wx.MDIParentFrame.__init__(self, frame, id, title, pos, size, style, name)
+ self._docManager = manager
+
+ wx.EVT_CLOSE(self, self.OnCloseWindow)
+
+ wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
+ wx.EVT_MENU_RANGE(self, wx.ID_FILE1, wx.ID_FILE9, self.OnMRUFile)
+
+ wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
+ wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
+
+ wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
+ wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
+
+
+ def ProcessEvent(self, event):
+ """
+ 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 self._docManager and self._docManager.ProcessEvent(event)
+
+
+ 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 self._docManager and self._docManager.ProcessUpdateUIEvent(event)
+
+
+ def OnExit(self, event):
+ """
+ Called when File/Exit is chosen and closes the window.
+ """
+ self.Close()
+
+
+ def OnMRUFile(self, event):
+ """
+ Opens the appropriate file when it is selected from the file history
+ menu.
+ """
+ n = event.GetId() - wx.ID_FILE1
+ filename = self._docManager.GetHistoryFile(n)
+ if filename:
+ self._docManager.CreateDocument(filename, DOC_SILENT)
+ else:
+ self._docManager.RemoveFileFromHistory(n)
+ msgTitle = wx.GetApp().GetAppName()
+ if not msgTitle:
+ msgTitle = _("File Error")
+ wx.MessageBox("The file '%s' doesn't exist and couldn't be opened.\nIt has been removed from the most recently used files list" % FileNameFromPath(file),
+ msgTitle,
+ wx.OK | wx.ICON_EXCLAMATION,
+ self)
+
+
+ def OnCloseWindow(self, event):
+ """
+ Deletes all views and documents. If no user input cancelled the
+ operation, the frame will be destroyed and the application will exit.
+ """
+ if self._docManager.Clear(not event.CanVeto()):
+ self.Destroy()
+ else:
+ event.Veto()
+
+
+class DocMDIChildFrame(wx.MDIChildFrame):
+ """
+ The wxDocMDIChildFrame class provides a default frame for displaying
+ documents on separate windows. This class can only be used for MDI child
+ frames.
+
+ The class is part of the document/view framework supported by wxWindows,
+ and cooperates with the wxView, wxDocument, wxDocManager and wxDocTemplate
+ classes.
+ """
+
+
+ def __init__(self, doc, view, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
+ """
+ Constructor. Note that the event table must be rebuilt for the
+ frame since the EvtHandler is not virtual.
+ """
+ wx.MDIChildFrame.__init__(self, frame, id, title, pos, size, style, name)
+ self._childDocument = doc
+ self._childView = view
+ if view:
+ view.SetFrame(self)
+ # self.Create(doc, view, frame, id, title, pos, size, style, name)
+ self._activeEvent = None
+ self._activated = 0
+ wx.EVT_ACTIVATE(self, self.OnActivate)
+ wx.EVT_CLOSE(self, self.OnCloseWindow)
+
+ if frame: # wxBug: For some reason the EVT_ACTIVATE event is not getting triggered for the first mdi client window that is opened so we have to do it manually
+ mdiChildren = filter(lambda x: isinstance(x, wx.MDIChildFrame), frame.GetChildren())
+ if len(mdiChildren) == 1:
+ self.Activate()
+
+
+## # Couldn't get this to work, but seems to work fine with single stage construction
+## def Create(self, doc, view, frame, id, title, pos, size, style, name):
+## self._childDocument = doc
+## self._childView = view
+## if wx.MDIChildFrame.Create(self, frame, id, title, pos, size, style, name):
+## if view:
+## view.SetFrame(self)
+## return True
+## return False
+
+
+
+ def Activate(self): # Need this in case there are embedded sash windows and such, OnActivate is not getting called
+ """
+ Activates the current view.
+ """
+ if self._childView:
+ self._childView.Activate(True)
+
+
+ def ProcessEvent(event):
+ """
+ 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.
+ """
+ if self._activeEvent == event:
+ return False
+
+ self._activeEvent = event # Break recursion loops
+
+ if self._childView:
+ self._childView.Activate(True)
+
+ if not self._childView or not self._childView.ProcessEvent(event):
+ if not isinstance(event, wx.CommandEvent) or not self.GetParent() or not self.GetParent().ProcessEvent(event):
+ ret = False
+ else:
+ ret = True
+ else:
+ ret = True
+
+ self._activeEvent = None
+ return ret
+
+
+ def OnActivate(self, event):
+ """
+ Sets the currently active view to be the frame's view. You may need to
+ override (but still call) this function in order to set the keyboard
+ focus for your subwindow.
+ """
+ if self._activated != 0:
+ return True
+ self._activated += 1
+ wx.MDIChildFrame.Activate(self)
+ if event.GetActive() and self._childView:
+ self._childView.Activate(event.GetActive())
+ self._activated = 0
+
+ def OnCloseWindow(self, event):
+ """
+ Closes and deletes the current view and document.
+ """
+ if self._childView:
+ ans = False
+ if not event.CanVeto():
+ ans = True
+ else:
+ ans = self._childView.Close(deleteWindow = False)
+
+ if ans:
+ self._childView.Activate(False)
+ self._childView.Destroy()
+ self._childView = None
+ if self._childDocument:
+ self._childDocument.Destroy() # This isn't in the wxWindows codebase but the document needs to be disposed of somehow
+ self._childDocument = None
+ self.Destroy()
+ else:
+ event.Veto()
+ else:
+ event.Veto()
+
+
+ def GetDocument(self):
+ """
+ Returns the document associated with this frame.
+ """
+ return self._childDocument
+
+
+ def SetDocument(self, document):
+ """
+ Sets the document for this frame.
+ """
+ self._childDocument = document
+
+
+ def GetView(self):
+ """
+ Returns the view associated with this frame.
+ """
+ return self._childView
+
+
+ def SetView(self, view):
+ """
+ Sets the view for this frame.
+ """
+ self._childView = view
+
+
+class DocPrintout(wx.Printout):
+ """
+ DocPrintout is a default Printout that prints the first page of a document
+ view.
+ """
+
+
+ def __init__(self, view, title = "Printout"):
+ """
+ Constructor.
+ """
+ wx.Printout.__init__(self)
+ self._printoutView = view
+
+
+ def GetView(self):
+ """
+ Returns the DocPrintout's view.
+ """
+ return self._printoutView
+
+
+ def OnPrintPage(self, page):
+ """
+ Prints the first page of the view.
+ """
+ dc = self.GetDC()
+ ppiScreenX, ppiScreenY = self.GetPPIScreen()
+ ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
+ scale = ppiPrinterX/ppiScreenX
+ w, h = dc.GetSize()
+ pageWidth, pageHeight = self.GetPageSizePixels()
+ overallScale = scale * w / pageWidth
+ dc.SetUserScale(overallScale, overallScale)
+ if self._printoutView:
+ self._printoutView.OnDraw(dc)
+ return True
+
+
+ def HasPage(self, pageNum):
+ """
+ Indicates that the DocPrintout only has a single page.
+ """
+ return pageNum == 1
+
+
+ def OnBeginDocument(self, startPage, endPage):
+ """
+ Not quite sure why this was overridden, but it was in wxWindows! :)
+ """
+ if not wx.Printout.base_OnBeginDocument(self, startPage, endPage):
+ return False
+ return True
+
+
+ def GetPageInfo(self):
+ """
+ Indicates that the DocPrintout only has a single page.
+ """
+ minPage = 1
+ maxPage = 1
+ selPageFrom = 1
+ selPageTo = 1
+ return (minPage, maxPage, selPageFrom, selPageTo)
+
+
+#----------------------------------------------------------------------
+# Command Classes
+#----------------------------------------------------------------------
+
+class Command(wx.Object):
+ """
+ wxCommand is a base class for modelling an application command, which is
+ an action usually performed by selecting a menu item, pressing a toolbar
+ button or any other means provided by the application to change the data
+ or view.
+ """
+
+
+ def __init__(self, canUndo = False, name = None):
+ """
+ Constructor. wxCommand is an abstract class, so you will need to
+ derive a new class and call this constructor from your own constructor.
+
+ canUndo tells the command processor whether this command is undo-able.
+ You can achieve the same functionality by overriding the CanUndo member
+ function (if for example the criteria for undoability is context-
+ dependent).
+
+ name must be supplied for the command processor to display the command
+ name in the application's edit menu.
+ """
+ self._canUndo = canUndo
+ self._name = name
+
+
+ def CanUndo(self):
+ """
+ Returns true if the command can be undone, false otherwise.
+ """
+ return self._canUndo
+
+
+ def GetName(self):
+ """
+ Returns the command name.
+ """
+ return self._name
+
+
+ def Do(self):
+ """
+ Override this member function to execute the appropriate action when
+ called. Return true to indicate that the action has taken place, false
+ otherwise. Returning false will indicate to the command processor that
+ the action is not undoable and should not be added to the command
+ history.
+ """
+ return True
+
+
+ def Undo(self):
+ """
+ Override this member function to un-execute a previous Do. Return true
+ to indicate that the action has taken place, false otherwise. Returning
+ false will indicate to the command processor that the action is not
+ redoable and no change should be made to the command history.
+
+ How you implement this command is totally application dependent, but
+ typical strategies include:
+
+ Perform an inverse operation on the last modified piece of data in the
+ document. When redone, a copy of data stored in command is pasted back
+ or some operation reapplied. This relies on the fact that you know the
+ ordering of Undos; the user can never Undo at an arbitrary position in
+ he command history.
+
+ Restore the entire document state (perhaps using document
+ transactioning). Potentially very inefficient, but possibly easier to
+ code if the user interface and data are complex, and an 'inverse
+ execute' operation is hard to write.
+ """
+ return True
+
+
+class CommandProcessor(wx.Object):
+ """
+ wxCommandProcessor is a class that maintains a history of wxCommands, with
+ undo/redo functionality built-in. Derive a new class from this if you want
+ different behaviour.
+ """
+
+
+ def __init__(self, maxCommands = -1):
+ """
+ Constructor. maxCommands may be set to a positive integer to limit
+ the number of commands stored to it, otherwise (and by default) the
+ list of commands can grow arbitrarily.
+ """
+ self._maxCommands = maxCommands
+ self._editMenu = None
+ self._undoAccelerator = _("Ctrl+Z")
+ self._redoAccelerator = _("Ctrl+Y")
+ self.ClearCommands()
+
+
+ def _GetCurrentCommand(self):
+ if len(self._commands) == 0:
+ return None
+ else:
+ return self._commands[-1]
+
+
+ def _GetCurrentRedoCommand(self):
+ if len(self._redoCommands) == 0:
+ return None
+ else:
+ return self._redoCommands[-1]
+
+
+ def GetMaxCommands(self):
+ """
+ Returns the maximum number of commands that the command processor
+ stores.
+
+ """
+ return self._maxCommands
+
+
+ def GetCommands(self):
+ """
+ Returns the list of commands.
+ """
+ return self._commands
+
+
+ def ClearCommands(self):
+ """
+ Deletes all the commands in the list and sets the current command
+ pointer to None.
+ """
+ self._commands = []
+ self._redoCommands = []
+
+
+ def GetEditMenu(self):
+ """
+ Returns the edit menu associated with the command processor.
+ """
+ return self._editMenu
+
+
+ def SetEditMenu(self, menu):
+ """
+ Tells the command processor to update the Undo and Redo items on this
+ menu as appropriate. Set this to NULL if the menu is about to be
+ destroyed and command operations may still be performed, or the
+ command processor may try to access an invalid pointer.
+ """
+ self._editMenu = menu
+
+
+ def GetUndoAccelerator(self):
+ """
+ Returns the string that will be appended to the Undo menu item.
+ """
+ return self._undoAccelerator
+
+
+ def SetUndoAccelerator(self, accel):
+ """
+ Sets the string that will be appended to the Redo menu item.
+ """
+ self._undoAccelerator = accel
+
+
+ def GetRedoAccelerator(self):
+ """
+ Returns the string that will be appended to the Redo menu item.
+ """
+ return self._redoAccelerator
+
+
+ def SetRedoAccelerator(self, accel):
+ """
+ Sets the string that will be appended to the Redo menu item.
+ """
+ self._redoAccelerator = accel
+
+
+ def SetEditMenu(self, menu):
+ """
+ Tells the command processor to update the Undo and Redo items on this
+ menu as appropriate. Set this to NULL if the menu is about to be
+ destroyed and command operations may still be performed, or the
+ command processor may try to access an invalid pointer.
+ """
+ self._editMenu = menu
+
+
+ def SetMenuStrings(self):
+ """
+ Sets the menu labels according to the currently set menu and the
+ current command state.
+ """
+ if self.GetEditMenu() != None:
+ undoCommand = self._GetCurrentCommand()
+ redoCommand = self._GetCurrentRedoCommand()
+ undoItem = self.GetEditMenu().FindItemById(wx.ID_UNDO)
+ redoItem = self.GetEditMenu().FindItemById(wx.ID_REDO)
+ if self.GetUndoAccelerator():
+ undoAccel = '\t' + self.GetUndoAccelerator()
+ else:
+ undoAccel = ''
+ if self.GetRedoAccelerator():
+ redoAccel = '\t' + self.GetRedoAccelerator()
+ else:
+ redoAccel = ''
+ if undoCommand and undoItem and undoCommand.CanUndo():
+ undoItem.SetText(_("Undo ") + undoCommand.GetName() + undoAccel)
+ #elif undoCommand and not undoCommand.CanUndo():
+ # undoItem.SetText(_("Can't Undo") + undoAccel)
+ else:
+ undoItem.SetText(_("Undo" + undoAccel))
+ if redoCommand and redoItem:
+ redoItem.SetText(_("Redo ") + redoCommand.GetName() + redoAccel)
+ else:
+ redoItem.SetText(_("Redo") + redoAccel)
+
+
+ def CanUndo(self):
+ """
+ Returns true if the currently-active command can be undone, false
+ otherwise.
+ """
+ if self._GetCurrentCommand() == None:
+ return False
+ return self._GetCurrentCommand().CanUndo()
+
+
+ def CanRedo(self):
+ """
+ Returns true if the currently-active command can be redone, false
+ otherwise.
+ """
+ return self._GetCurrentRedoCommand() != None
+
+
+ def Submit(self, command, storeIt = True):
+ """
+ Submits a new command to the command processor. The command processor
+ calls wxCommand::Do to execute the command; if it succeeds, the
+ command is stored in the history list, and the associated edit menu
+ (if any) updated appropriately. If it fails, the command is deleted
+ immediately. Once Submit has been called, the passed command should
+ not be deleted directly by the application.
+
+ storeIt indicates whether the successful command should be stored in
+ the history list.
+ """
+ done = command.Do()
+ if done and storeIt:
+ self._commands.append(command)
+ if self._maxCommands > -1:
+ if len(self._commands) > self._maxCommands:
+ del self._commands[0]
+ return done
+
+
+ def Redo(self):
+ """
+ Redoes the command just undone.
+ """
+ cmd = self._GetCurrentRedoCommand()
+ if not cmd:
+ return False
+ done = cmd.Do()
+ if done:
+ self._commands.append(self._redoCommands.pop())
+ return done
+
+
+ def Undo(self):
+ """
+ Undoes the command just executed.
+ """
+ cmd = self._GetCurrentCommand()
+ if not cmd:
+ return False
+ done = cmd.Undo()
+ if done:
+ self._redoCommands.append(self._commands.pop())
+ return done
+
+