X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/18c45cd6dc74b8c37bf6e0c920cd010d3b77135b..d1dc2b32e00303839aec0077aefcbc60bd712f00:/wxPython/wx/lib/docview.py diff --git a/wxPython/wx/lib/docview.py b/wxPython/wx/lib/docview.py new file mode 100644 index 0000000000..8f5f038ba7 --- /dev/null +++ b/wxPython/wx/lib/docview.py @@ -0,0 +1,3033 @@ +#---------------------------------------------------------------------------- +# 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 + +