]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/docview.py
MinGW fix.
[wxWidgets.git] / wxPython / wx / lib / docview.py
1 #----------------------------------------------------------------------------
2 # Name: docview.py
3 # Purpose: Port of the wxWidgets docview classes
4 #
5 # Author: Peter Yared
6 #
7 # Created: 5/15/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003-2005 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
10 # License: wxWindows license
11 #----------------------------------------------------------------------------
12
13 """
14 A port of the wxWidgets doc/view classes to Python.
15
16 :see: `pydocview`
17 """
18
19 import os
20 import os.path
21 import wx
22 import sys
23 _ = wx.GetTranslation
24
25
26 #----------------------------------------------------------------------
27 # docview globals
28 #----------------------------------------------------------------------
29
30 DOC_SDI = 1
31 DOC_MDI = 2
32 DOC_NEW = 4
33 DOC_SILENT = 8
34 DOC_OPEN_ONCE = 16
35 DEFAULT_DOCMAN_FLAGS = DOC_SDI & DOC_OPEN_ONCE
36
37 TEMPLATE_VISIBLE = 1
38 TEMPLATE_INVISIBLE = 2
39 DEFAULT_TEMPLATE_FLAGS = TEMPLATE_VISIBLE
40
41 MAX_FILE_HISTORY = 9
42
43
44 #----------------------------------------------------------------------
45 # Convenience functions from wxWindows used in docview
46 #----------------------------------------------------------------------
47
48
49 def FileNameFromPath(path):
50 """
51 Returns the filename for a full path.
52 """
53 return os.path.split(path)[1]
54
55 def FindExtension(path):
56 """
57 Returns the extension of a filename for a full path.
58 """
59 return os.path.splitext(path)[1].lower()
60
61 def FileExists(path):
62 """
63 Returns True if the path exists.
64 """
65 return os.path.isfile(path)
66
67 def PathOnly(path):
68 """
69 Returns the path of a full path without the filename.
70 """
71 return os.path.split(path)[0]
72
73
74 #----------------------------------------------------------------------
75 # Document/View Classes
76 #----------------------------------------------------------------------
77
78
79 class Document(wx.EvtHandler):
80 """
81 The document class can be used to model an application's
82 file-based data. It is part of the document/view framework, and
83 cooperates with the `View`, `DocTemplate` and `DocManager`
84 classes.
85
86 Note this wxPython version also keeps track of the modification date of the
87 document and if it changes on disk outside of the application, we will warn the
88 user before saving to avoid clobbering the file.
89 """
90
91
92 def __init__(self, parent = None):
93 """
94 Constructor. Define your own default constructor to initialize
95 application-specific data.
96 """
97 wx.EvtHandler.__init__(self)
98
99 self._documentParent = parent
100 self._documentTemplate = None
101 self._commandProcessor = None
102 self._savedYet = False
103 self._writeable = True
104
105 self._documentTitle = None
106 self._documentFile = None
107 self._documentTypeName = None
108 self._documentModified = False
109 self._documentModificationDate = None
110 self._documentViews = []
111
112
113 def ProcessEvent(self, event):
114 """
115 Processes an event, searching event tables and calling zero or more
116 suitable event handler function(s). Note that the ProcessEvent
117 method is called from the wxPython docview framework directly since
118 wxPython does not have a virtual ProcessEvent function.
119 """
120 return False
121
122
123 def GetFilename(self):
124 """
125 Gets the filename associated with this document, or "" if none is
126 associated.
127 """
128 return self._documentFile
129
130
131 def GetTitle(self):
132 """
133 Gets the title for this document. The document title is used for an
134 associated frame (if any), and is usually constructed by the framework
135 from the filename.
136 """
137 return self._documentTitle
138
139
140 def SetTitle(self, title):
141 """
142 Sets the title for this document. The document title is used for an
143 associated frame (if any), and is usually constructed by the framework
144 from the filename.
145 """
146 self._documentTitle = title
147
148
149 def GetDocumentName(self):
150 """
151 The document type name given to the `DocTemplate` constructor,
152 copied to this document when the document is created. If several
153 document templates are created that use the same document type, this
154 variable is used in `DocManager.CreateView` to collate a list of
155 alternative view types that can be used on this kind of document.
156 """
157 return self._documentTypeName
158
159
160 def SetDocumentName(self, name):
161 """
162 Sets the document type name given to the `DocTemplate` constructor,
163 copied to this document when the document is created. If several
164 document templates are created that use the same document type, this
165 variable is used in `DocManager.CreateView` to collate a list of
166 alternative view types that can be used on this kind of document. Do
167 not change the value of this variable.
168 """
169 self._documentTypeName = name
170
171
172 def GetDocumentSaved(self):
173 """
174 Returns True if the document has been saved.
175 """
176 return self._savedYet
177
178
179 def SetDocumentSaved(self, saved = True):
180 """
181 Sets whether the document has been saved.
182 """
183 self._savedYet = saved
184
185
186 def GetCommandProcessor(self):
187 """
188 Returns the command processor associated with this document.
189 """
190 return self._commandProcessor
191
192
193 def SetCommandProcessor(self, processor):
194 """
195 Sets the command processor to be used for this document. The document
196 will then be responsible for its deletion. Normally you should not
197 call this; override `OnCreateCommandProcessor` instead.
198 """
199 self._commandProcessor = processor
200
201
202 def IsModified(self):
203 """
204 Returns true if the document has been modified since the last save,
205 false otherwise. You may need to override this if your document view
206 maintains its own record of being modified (for example if using
207 wxTextWindow to view and edit the document).
208 """
209 return self._documentModified
210
211
212 def Modify(self, modify):
213 """
214 Call with true to mark the document as modified since the last save,
215 false otherwise. You may need to override this if your document view
216 maintains its own record of being modified (for example if using
217 `wx.TextCtrl` to view and edit the document).
218 """
219 self._documentModified = modify
220
221
222 def SetDocumentModificationDate(self):
223 """
224 Saves the file's last modification date.
225 This is used to check if the file has been modified outside of the application.
226 """
227 self._documentModificationDate = os.path.getmtime(self.GetFilename())
228
229
230 def GetDocumentModificationDate(self):
231 """
232 Returns the file's modification date when it was loaded from disk.
233 This is used to check if the file has been modified outside of the application.
234 """
235 return self._documentModificationDate
236
237
238 def GetViews(self):
239 """
240 Returns the list whose elements are the views on the document.
241 """
242 return self._documentViews
243
244
245 def GetDocumentTemplate(self):
246 """
247 Returns the template that created the document.
248 """
249 return self._documentTemplate
250
251
252 def SetDocumentTemplate(self, template):
253 """
254 Sets the template that created the document. Should only be called by
255 the framework.
256 """
257 self._documentTemplate = template
258
259
260 def DeleteContents(self):
261 """
262 Deletes the contents of the document. Override this method as
263 necessary.
264 """
265 return True
266
267
268 def Destroy(self):
269 """
270 Destructor. Removes itself from the document manager.
271 """
272 self.DeleteContents()
273 if self.GetDocumentManager():
274 self.GetDocumentManager().RemoveDocument(self)
275 wx.EvtHandler.Destroy(self)
276
277
278 def Close(self):
279 """
280 Closes the document, by calling `OnSaveModified` and then (if
281 this returns True) `OnCloseDocument`. This does not normally
282 delete the document object: use DeleteAllViews to do this
283 implicitly.
284 """
285 if self.OnSaveModified():
286 if self.OnCloseDocument():
287 return True
288 else:
289 return False
290 else:
291 return False
292
293
294 def OnCloseDocument(self):
295 """
296 The default implementation calls `DeleteContents` (an empty
297 implementation) sets the modified flag to false. Override this
298 to supply additional behaviour when the document is closed
299 with `Close`.
300 """
301 self.NotifyClosing()
302 self.DeleteContents()
303 self.Modify(False)
304 return True
305
306
307 def DeleteAllViews(self):
308 """
309 Calls `View.Close` and deletes each view. Deleting the final view will
310 implicitly delete the document itself, because the `View` destructor
311 calls `RemoveView`. This in turns calls `Document.OnChangedViewList`,
312 whose default implemention is to save and delete the document if no
313 views exist.
314 """
315 manager = self.GetDocumentManager()
316 for view in self._documentViews:
317 if not view.Close():
318 return False
319 if self in manager.GetDocuments():
320 self.Destroy()
321 return True
322
323
324 def GetFirstView(self):
325 """
326 A convenience function to get the first view for a document, because
327 in many cases a document will only have a single view.
328 """
329 if len(self._documentViews) == 0:
330 return None
331 return self._documentViews[0]
332
333
334 def GetDocumentManager(self):
335 """
336 Returns the associated document manager.
337 """
338 if self._documentTemplate:
339 return self._documentTemplate.GetDocumentManager()
340 return None
341
342
343 def OnNewDocument(self):
344 """
345 The default implementation calls `OnSaveModified` and
346 `DeleteContents`, makes a default title for the document, and
347 notifies the views that the filename (in fact, the title) has
348 changed.
349 """
350 if not self.OnSaveModified() or not self.OnCloseDocument():
351 return False
352 self.DeleteContents()
353 self.Modify(False)
354 self.SetDocumentSaved(False)
355 name = self.GetDocumentManager().MakeDefaultName()
356 self.SetTitle(name)
357 self.SetFilename(name, notifyViews = True)
358
359
360 def Save(self):
361 """
362 Saves the document by calling `OnSaveDocument` if there is an
363 associated filename, or `SaveAs` if there is no filename.
364 """
365 if not self.IsModified(): # and self._savedYet: This was here, but if it is not modified who cares if it hasn't been saved yet?
366 return True
367
368 """ check for file modification outside of application """
369 if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
370 msgTitle = wx.GetApp().GetAppName()
371 if not msgTitle:
372 msgTitle = _("Application")
373 res = wx.MessageBox(_("'%s' has been modified outside of %s. Overwrite '%s' with current changes?") % (self.GetPrintableName(), msgTitle, self.GetPrintableName()),
374 msgTitle,
375 wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION,
376 self.GetDocumentWindow())
377
378 if res == wx.NO:
379 return True
380 elif res == wx.YES:
381 pass
382 else: # elif res == wx.CANCEL:
383 return False
384
385 if not self._documentFile or not self._savedYet:
386 return self.SaveAs()
387 return self.OnSaveDocument(self._documentFile)
388
389
390 def SaveAs(self):
391 """
392 Prompts the user for a file to save to, and then calls `OnSaveDocument`.
393 """
394 docTemplate = self.GetDocumentTemplate()
395 if not docTemplate:
396 return False
397
398 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
399 filename = wx.FileSelector(_("Save As"),
400 docTemplate.GetDirectory(),
401 FileNameFromPath(self.GetFilename()),
402 docTemplate.GetDefaultExtension(),
403 wildcard = descr,
404 flags = wx.SAVE | wx.OVERWRITE_PROMPT,
405 parent = self.GetDocumentWindow())
406 if filename == "":
407 return False
408
409 name, ext = os.path.splitext(filename)
410 if ext == "":
411 filename += '.' + docTemplate.GetDefaultExtension()
412
413 self.SetFilename(filename)
414 self.SetTitle(FileNameFromPath(filename))
415
416 for view in self._documentViews:
417 view.OnChangeFilename()
418
419 if not self.OnSaveDocument(filename):
420 return False
421
422 if docTemplate.FileMatchesTemplate(filename):
423 self.GetDocumentManager().AddFileToHistory(filename)
424
425 return True
426
427
428 def OnSaveDocument(self, filename):
429 """
430 Constructs an output file for the given filename (which must
431 not be empty), and calls `SaveObject`. If `SaveObject` returns
432 true, the document is set to unmodified; otherwise, an error
433 message box is displayed.
434 """
435 if not filename:
436 return False
437
438 msgTitle = wx.GetApp().GetAppName()
439 if not msgTitle:
440 msgTitle = _("File Error")
441
442 backupFilename = None
443 try:
444 # if current file exists, move it to a safe place temporarily
445 if os.path.exists(filename):
446
447 # Check if read-only.
448 if not os.access(filename, os.W_OK):
449 wx.MessageBox("Could not save '%s'. No write permission to overwrite existing file." % FileNameFromPath(filename),
450 msgTitle,
451 wx.OK | wx.ICON_EXCLAMATION,
452 self.GetDocumentWindow())
453 return False
454
455 i = 1
456 backupFilename = "%s.bak%s" % (filename, i)
457 while os.path.exists(backupFilename):
458 i += 1
459 backupFilename = "%s.bak%s" % (filename, i)
460 os.rename(filename, backupFilename)
461
462 fileObject = file(filename, 'w')
463 self.SaveObject(fileObject)
464
465 if backupFilename:
466 os.remove(backupFilename)
467 except:
468 # save failed, restore old file
469 if backupFilename:
470 os.remove(filename)
471 os.rename(backupFilename, filename)
472 self.SetDocumentModificationDate()
473
474 wx.MessageBox("Could not save '%s'. %s" % (FileNameFromPath(filename), sys.exc_value),
475 msgTitle,
476 wx.OK | wx.ICON_EXCLAMATION,
477 self.GetDocumentWindow())
478 return False
479
480 self.SetFilename(filename, True)
481 self.Modify(False)
482 self.SetDocumentModificationDate()
483 self.SetDocumentSaved(True)
484 #if wx.Platform == '__WXMAC__': # Not yet implemented in wxPython
485 # wx.FileName(file).MacSetDefaultTypeAndCreator()
486 return True
487
488
489 def OnOpenDocument(self, filename):
490 """
491 Constructs an input file for the given filename (which must not
492 be empty), and calls `LoadObject`. If `LoadObject` returns true, the
493 document is set to unmodified; otherwise, an error message box is
494 displayed. The document's views are notified that the filename has
495 changed, to give windows an opportunity to update their titles. All of
496 the document's views are then updated.
497 """
498 if not self.OnSaveModified():
499 return False
500
501 msgTitle = wx.GetApp().GetAppName()
502 if not msgTitle:
503 msgTitle = _("File Error")
504
505 fileObject = file(filename, 'r')
506 try:
507 self.LoadObject(fileObject)
508 except:
509 wx.MessageBox("Could not open '%s'. %s" % (FileNameFromPath(filename), sys.exc_value),
510 msgTitle,
511 wx.OK | wx.ICON_EXCLAMATION,
512 self.GetDocumentWindow())
513 return False
514
515 self.SetFilename(filename, True)
516 self.Modify(False)
517 self.SetDocumentModificationDate()
518 self.SetDocumentSaved(True)
519 self.UpdateAllViews()
520 return True
521
522
523 def LoadObject(self, file):
524 """
525 Override this function and call it from your own `LoadObject`
526 before loading your own data. `LoadObject` is called by the
527 framework automatically when the document contents need to be
528 loaded.
529
530 Note that the wxPython version simply sends you a Python file
531 object, so you can use pickle.
532 """
533 return True
534
535
536 def SaveObject(self, file):
537 """
538 Override this function and call it from your own `SaveObject`
539 before saving your own data. `SaveObject` is called by the
540 framework automatically when the document contents need to be
541 saved.
542
543 Note that the wxPython version simply sends you a Python file
544 object, so you can use pickle.
545 """
546 return True
547
548
549 def Revert(self):
550 """
551 Override this function to revert the document to its last saved state.
552 """
553 return False
554
555
556 def GetPrintableName(self):
557 """
558 Copies a suitable document name into the supplied name buffer.
559 The default function uses the title, or if there is no title, uses the
560 filename; or if no filename, the string 'Untitled'.
561 """
562 if self._documentTitle:
563 return self._documentTitle
564 elif self._documentFile:
565 return FileNameFromPath(self._documentFile)
566 else:
567 return _("Untitled")
568
569
570 def GetDocumentWindow(self):
571 """
572 Intended to return a suitable window for using as a parent for
573 document-related dialog boxes. By default, uses the frame associated
574 with the first view.
575 """
576 if len(self._documentViews) > 0:
577 return self._documentViews[0].GetFrame()
578 else:
579 return wx.GetApp().GetTopWindow()
580
581
582 def OnCreateCommandProcessor(self):
583 """
584 Override this function if you want a different (or no) command
585 processor to be created when the document is created. By
586 default, it returns an instance of `CommandProcessor`.
587 """
588 return CommandProcessor()
589
590
591 def OnSaveModified(self):
592 """
593 If the document has been modified, prompts the user to ask if
594 the changes should be changed. If the user replies Yes, the
595 `Save` function is called. If No, the document is marked as
596 unmodified and the function succeeds. If Cancel, the function
597 fails.
598 """
599 if not self.IsModified():
600 return True
601
602 """ check for file modification outside of application """
603 if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
604 msgTitle = wx.GetApp().GetAppName()
605 if not msgTitle:
606 msgTitle = _("Warning")
607 res = wx.MessageBox(_("'%s' has been modified outside of %s. Overwrite '%s' with current changes?") % (self.GetPrintableName(), msgTitle, self.GetPrintableName()),
608 msgTitle,
609 wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION,
610 self.GetDocumentWindow())
611
612 if res == wx.NO:
613 self.Modify(False)
614 return True
615 elif res == wx.YES:
616 return wx.lib.docview.Document.Save(self)
617 else: # elif res == wx.CANCEL:
618 return False
619
620 msgTitle = wx.GetApp().GetAppName()
621 if not msgTitle:
622 msgTitle = _("Warning")
623
624 res = wx.MessageBox(_("Save changes to '%s'?") % self.GetPrintableName(),
625 msgTitle,
626 wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION,
627 self.GetDocumentWindow())
628
629 if res == wx.NO:
630 self.Modify(False)
631 return True
632 elif res == wx.YES:
633 return self.Save()
634 else: # elif res == wx.CANCEL:
635 return False
636
637
638 def Draw(context):
639 """
640 Called by printing framework to draw the view.
641 """
642 return True
643
644
645 def AddView(self, view):
646 """
647 If the view is not already in the list of views, adds the view and
648 calls `OnChangedViewList`.
649 """
650 if not view in self._documentViews:
651 self._documentViews.append(view)
652 self.OnChangedViewList()
653 return True
654
655
656 def RemoveView(self, view):
657 """
658 Removes the view from the document's list of views, and calls
659 `OnChangedViewList`.
660 """
661 if view in self._documentViews:
662 self._documentViews.remove(view)
663 self.OnChangedViewList()
664 return True
665
666
667 def OnCreate(self, path, flags):
668 """
669 The default implementation calls `DeleteContents` (an empty
670 implementation) sets the modified flag to false. Override this to
671 supply additional behaviour when the document is closed with Close.
672 """
673 return self.GetDocumentTemplate().CreateView(self, flags)
674
675
676 def OnChangedViewList(self):
677 """
678 Called when a view is added to or deleted from this document. The
679 default implementation saves and deletes the document if no views
680 exist (the last one has just been removed).
681 """
682 if len(self._documentViews) == 0:
683 if self.OnSaveModified():
684 pass # C version does a delete but Python will garbage collect
685
686
687 def UpdateAllViews(self, sender = None, hint = None):
688 """
689 Updates all views. If sender is non-None, does not update this view.
690 hint represents optional information to allow a view to optimize its
691 update.
692 """
693 for view in self._documentViews:
694 if view != sender:
695 view.OnUpdate(sender, hint)
696
697
698 def NotifyClosing(self):
699 """
700 Notifies the views that the document is going to close.
701 """
702 for view in self._documentViews:
703 view.OnClosingDocument()
704
705
706 def SetFilename(self, filename, notifyViews = False):
707 """
708 Sets the filename for this document. Usually called by the framework.
709 If notifyViews is true, `View.OnChangeFilename` is called for all
710 views.
711 """
712 self._documentFile = filename
713 if notifyViews:
714 for view in self._documentViews:
715 view.OnChangeFilename()
716
717
718 def GetWriteable(self):
719 """
720 Returns true if the document can be written to its accociated file path.
721 This method has been added to wxPython and is not in wxWindows.
722 """
723 if not self._writeable:
724 return False
725 if not self._documentFile: # Doesn't exist, do a save as
726 return True
727 else:
728 return os.access(self._documentFile, os.W_OK)
729
730
731 def SetWriteable(self, writeable):
732 """
733 Set to False if the document can not be saved. This will
734 disable the ID_SAVE_AS event and is useful for custom
735 documents that should not be saveable. The ID_SAVE event can
736 be disabled by never modifying the document.
737 """
738 self._writeable = writeable
739
740
741 class View(wx.EvtHandler):
742 """
743 The view class can be used to model the viewing and editing
744 component of an application's file-based data. It cooperates
745 with the `Document`, `DocTemplate` and `DocManager` classes.
746 """
747
748 def __init__(self):
749 """
750 Constructor. Define your own default constructor to initialize
751 application-specific data.
752 """
753 wx.EvtHandler.__init__(self)
754 self._viewDocument = None
755 self._viewFrame = None
756
757
758 def Destroy(self):
759 """
760 Destructor. Removes itself from the document's list of views.
761 """
762 if self._viewDocument:
763 self._viewDocument.RemoveView(self)
764 wx.EvtHandler.Destroy(self)
765
766
767 def ProcessEvent(self, event):
768 """
769 Processes an event, searching event tables and calling zero or more
770 suitable event handler function(s). Note that the ProcessEvent
771 method is called from the wxPython docview framework directly since
772 wxPython does not have a virtual ProcessEvent function.
773 """
774 if not self.GetDocument() or not self.GetDocument().ProcessEvent(event):
775 return False
776 else:
777 return True
778
779
780 def ProcessUpdateUIEvent(self, event):
781 """
782 Processes a UI event, searching event tables and calling zero or more
783 suitable event handler function(s). Note that the ProcessEvent
784 method is called from the wxPython docview framework directly since
785 wxPython does not have a virtual ProcessEvent function.
786 """
787 return False
788
789
790 def OnActivateView(self, activate, activeView, deactiveView):
791 """
792 Called when a view is activated by means of `View.Activate`. The
793 default implementation does nothing.
794 """
795 pass
796
797
798 def OnClosingDocument(self):
799 """
800 Override this to clean up the view when the document is being closed.
801 The default implementation does nothing.
802 """
803 pass
804
805
806 def OnDraw(self, dc):
807 """
808 Override this to draw the view for the printing framework. The
809 default implementation does nothing.
810 """
811 pass
812
813
814 def OnPrint(self, dc, info):
815 """
816 Override this to print the view for the printing framework. The
817 default implementation calls `View.OnDraw`.
818 """
819 self.OnDraw(dc)
820
821
822 def OnUpdate(self, sender, hint):
823 """
824 Called when the view should be updated. sender is a reference to the
825 view that sent the update request, or None if no single view requested
826 the update (for instance, when the document is opened). hint is as yet
827 unused but may in future contain application-specific information for
828 making updating more efficient.
829 """
830 pass
831
832
833 def OnChangeFilename(self):
834 """
835 Called when the filename has changed. The default implementation
836 constructs a suitable title and sets the title of the view frame (if
837 any).
838 """
839 if self.GetFrame():
840 appName = wx.GetApp().GetAppName()
841 if not self.GetDocument():
842 if appName:
843 title = appName
844 else:
845 return
846 else:
847 if appName and isinstance(self.GetFrame(), DocChildFrame): # Only need app name in title for SDI
848 title = appName + _(" - ")
849 else:
850 title = ''
851 self.GetFrame().SetTitle(title + self.GetDocument().GetPrintableName())
852
853
854 def GetDocument(self):
855 """
856 Returns the document associated with the view.
857 """
858 return self._viewDocument
859
860
861 def SetDocument(self, doc):
862 """
863 Associates the given document with the view. Normally called by the
864 framework.
865 """
866 self._viewDocument = doc
867 if doc:
868 doc.AddView(self)
869
870
871 def GetViewName(self):
872 """
873 Gets the name associated with the view (passed to the `DocTemplate`
874 constructor). Not currently used by the framework.
875 """
876 return self._viewTypeName
877
878
879 def SetViewName(self, name):
880 """
881 Sets the view type name. Should only be called by the framework.
882 """
883 self._viewTypeName = name
884
885
886 def Close(self, deleteWindow = True):
887 """
888 Closes the view by calling `OnClose`. If deleteWindow is true, this
889 function should delete the window associated with the view.
890 """
891 if self.OnClose(deleteWindow = deleteWindow):
892 return True
893 else:
894 return False
895
896
897 def Activate(self, activate = True):
898 """
899 Call this from your view frame's EVT_ACTIVATE handler to tell
900 the framework which view is currently active. If your
901 windowing system doesn't support EVT_ACTIVATE, you may need to
902 call this function from an EVT_MENU handler, or any place
903 where you know the view must be active, and the framework will
904 need to get the current view.
905 """
906 if self.GetDocument() and self.GetDocumentManager():
907 self.OnActivateView(activate,
908 self,
909 self.GetDocumentManager().GetCurrentView())
910 self.GetDocumentManager().ActivateView(self, activate)
911
912
913 def OnClose(self, deleteWindow = True):
914 """
915 Implements closing behaviour. The default implementation calls
916 `Document.Close` to close the associated document. Does not delete the
917 view. The application may wish to do some cleaning up operations in
918 this function, if a call to `Document.Close` succeeded. For example,
919 if your application's all share the same window, you need to
920 disassociate the window from the view and perhaps clear the window. If
921 deleteWindow is true, delete the frame associated with the view.
922 """
923 if self.GetDocument():
924 return self.GetDocument().Close()
925 else:
926 return True
927
928
929 def OnCreate(self, doc, flags):
930 """
931 `DocManager` or `Document` creates a `View` via a `DocTemplate`. Just
932 after the `DocTemplate` creates the `View`, it calls `View.OnCreate`.
933 In its `OnCreate` member function, the `View` can create a
934 `DocChildFrame` or a derived class. This `DocChildFrame` provides user
935 interface elements to view and/or edit the contents of the `Document`.
936
937 By default, simply returns true. If the function returns false, the
938 view will be deleted.
939 """
940 return True
941
942
943 def OnCreatePrintout(self):
944 """
945 Returns a `wx.Printout` object for the purposes of printing. It should
946 create a new object every time it is called; the framework will delete
947 objects it creates.
948
949 By default, this function returns an instance of `DocPrintout`, which
950 prints and previews one page by calling `View.OnDraw`.
951
952 Override to return an instance of a class other than `DocPrintout`.
953 """
954 return DocPrintout(self)
955
956
957 def GetFrame(self):
958 """
959 Gets the frame associated with the view (if any). Note that this
960 "frame" is not a `wx.Frame` at all in the generic MDI implementation
961 which uses the notebook pages instead of the frames.
962 """
963 return self._viewFrame
964
965
966 def SetFrame(self, frame):
967 """
968 Sets the frame associated with this view. The application should call
969 this if possible, to tell the view about the frame.
970 """
971 self._viewFrame = frame
972
973
974 def GetDocumentManager(self):
975 """
976 Returns the document manager instance associated with this view.
977 """
978 if self._viewDocument:
979 return self.GetDocument().GetDocumentManager()
980 else:
981 return None
982
983
984 class DocTemplate(wx.Object):
985 """
986 The `DocTemplate` class is used to model the relationship between
987 a document class and a view class.
988 """
989
990
991 def __init__(self, manager, description, filter, dir, ext,
992 docTypeName, viewTypeName, docType, viewType,
993 flags = DEFAULT_TEMPLATE_FLAGS, icon = None):
994 """
995 Constructor. Create instances dynamically near the start of your
996 application after creating a `DocManager` instance, and before doing
997 any document or view operations.
998
999 :param manager: the document manager object which manages this template.
1000
1001 :param description: a short description of what the template
1002 is for. This string will be displayed in the file filter
1003 list of Windows file selectors.
1004
1005 :param filter: an appropriate file filter such as \*.txt.
1006
1007 :param dir: the default directory to use for file selectors.
1008
1009 :param ext: the default file extension (such as txt).
1010
1011 :param docTypeName: a name that should be unique for a given
1012 type of document, used for gathering a list of views
1013 relevant to a particular document.
1014
1015 :param viewTypeName: a name that should be unique for a given view.
1016
1017 :param docType: a Python class. If this is not supplied, you
1018 will need to derive a new `DocTemplate` class and override
1019 the `CreateDocument` member to return a new document
1020 instance on demand.
1021
1022 :param viewType: a Python class. If this is not supplied, you
1023 will need to derive a new `DocTemplate` class and override
1024 the `CreateView` member to return a new view instance on
1025 demand.
1026
1027 :param flags: a bit list of the following
1028
1029 * TEMPLATE_VISIBLE: The template may be displayed to the
1030 user in dialogs.
1031
1032 * TEMPLATE_INVISIBLE: The template may not be displayed
1033 to the user in dialogs.
1034
1035 * DEFAULT_TEMPLATE_FLAGS: Defined as TEMPLATE_VISIBLE.
1036
1037 """
1038 self._docManager = manager
1039 self._description = description
1040 self._fileFilter = filter
1041 self._directory = dir
1042 self._defaultExt = ext
1043 self._docTypeName = docTypeName
1044 self._viewTypeName = viewTypeName
1045 self._docType = docType
1046 self._viewType = viewType
1047 self._flags = flags
1048 self._icon = icon
1049
1050 self._docManager.AssociateTemplate(self)
1051
1052
1053 def GetDefaultExtension(self):
1054 """
1055 Returns the default file extension for the document data, as
1056 passed to the document template constructor.
1057 """
1058 return self._defaultExt
1059
1060
1061 def SetDefaultExtension(self, defaultExt):
1062 """
1063 Sets the default file extension.
1064 """
1065 self._defaultExt = defaultExt
1066
1067
1068 def GetDescription(self):
1069 """
1070 Returns the text description of this template, as passed to
1071 the document template constructor.
1072 """
1073 return self._description
1074
1075
1076 def SetDescription(self, description):
1077 """
1078 Sets the template description.
1079 """
1080 self._description = description
1081
1082
1083 def GetDirectory(self):
1084 """
1085 Returns the default directory, as passed to the document
1086 template constructor.
1087 """
1088 return self._directory
1089
1090
1091 def SetDirectory(self, dir):
1092 """
1093 Sets the default directory.
1094 """
1095 self._directory = dir
1096
1097
1098 def GetDocumentManager(self):
1099 """
1100 Returns the document manager instance for which this template
1101 was created.
1102 """
1103 return self._docManager
1104
1105
1106 def SetDocumentManager(self, manager):
1107 """
1108 Sets the document manager instance for which this template was
1109 created. Should not be called by the application.
1110 """
1111 self._docManager = manager
1112
1113
1114 def GetFileFilter(self):
1115 """
1116 Returns the file filter, as passed to the document template
1117 constructor.
1118 """
1119 return self._fileFilter
1120
1121
1122 def SetFileFilter(self, filter):
1123 """
1124 Sets the file filter.
1125 """
1126 self._fileFilter = filter
1127
1128
1129 def GetFlags(self):
1130 """
1131 Returns the flags, as passed to the document template constructor.
1132 (see the constructor description for more details).
1133 """
1134 return self._flags
1135
1136
1137 def SetFlags(self, flags):
1138 """
1139 Sets the internal document template flags (see the constructor
1140 description for more details).
1141 """
1142 self._flags = flags
1143
1144
1145 def GetIcon(self):
1146 """
1147 Returns the icon, as passed to the document template
1148 constructor.
1149 """
1150 return self._icon
1151
1152
1153 def SetIcon(self, flags):
1154 """
1155 Sets the icon.
1156 """
1157 self._icon = icon
1158
1159
1160 def GetDocumentType(self):
1161 """
1162 Returns the Python document class, as passed to the document template
1163 constructor.
1164 """
1165 return self._docType
1166
1167
1168 def GetViewType(self):
1169 """
1170 Returns the Python view class, as passed to the document template
1171 constructor.
1172 """
1173 return self._viewType
1174
1175
1176 def IsVisible(self):
1177 """
1178 Returns true if the document template can be shown in user dialogs,
1179 false otherwise.
1180 """
1181 return (self._flags & TEMPLATE_VISIBLE) == TEMPLATE_VISIBLE
1182
1183
1184 def GetDocumentName(self):
1185 """
1186 Returns the document type name, as passed to the document template
1187 constructor.
1188 """
1189 return self._docTypeName
1190
1191
1192 def GetViewName(self):
1193 """
1194 Returns the view type name, as passed to the document template
1195 constructor.
1196 """
1197 return self._viewTypeName
1198
1199
1200 def CreateDocument(self, path, flags):
1201 """
1202 Creates a new instance of the associated document class. If you have
1203 not supplied a class to the template constructor, you will need to
1204 override this function to return an appropriate document instance.
1205 """
1206 doc = self._docType()
1207 doc.SetFilename(path)
1208 doc.SetDocumentTemplate(self)
1209 self.GetDocumentManager().AddDocument(doc)
1210 doc.SetCommandProcessor(doc.OnCreateCommandProcessor())
1211 if doc.OnCreate(path, flags):
1212 return doc
1213 else:
1214 if doc in self.GetDocumentManager().GetDocuments():
1215 doc.DeleteAllViews()
1216 return None
1217
1218
1219 def CreateView(self, doc, flags):
1220 """
1221 Creates a new instance of the associated document view. If you
1222 have not supplied a class to the template constructor, you
1223 will need to override this function to return an appropriate
1224 view instance.
1225 """
1226 view = self._viewType()
1227 view.SetDocument(doc)
1228 if view.OnCreate(doc, flags):
1229 return view
1230 else:
1231 view.Destroy()
1232 return None
1233
1234
1235 def FileMatchesTemplate(self, path):
1236 """
1237 Returns True if the path's extension matches one of this template's
1238 file filter extensions.
1239 """
1240 ext = FindExtension(path)
1241 if not ext: return False
1242 return ext in self.GetFileFilter()
1243 # return self.GetDefaultExtension() == FindExtension(path)
1244
1245
1246 class DocManager(wx.EvtHandler):
1247 """
1248 The `DocManager` class is part of the document/view framework and
1249 cooperates with the `View`, `Document` and `DocTemplate` classes.
1250 """
1251
1252 def __init__(self, flags = DEFAULT_DOCMAN_FLAGS, initialize = True):
1253 """
1254 Constructor. Create a document manager instance dynamically
1255 near the start of your application before doing any document
1256 or view operations.
1257
1258 :param flags: used to indicate whether the document manager is
1259 in DOC_SDI or DOC_MDI mode.
1260
1261 :param initialize: if true, the `Initialize` function will be
1262 called to create a default history list object. If you
1263 derive from DocManager, you may wish to call the base
1264 constructor with false, and then call `Initialize` in your
1265 own constructor, to allow your own `Initialize` or
1266 `OnCreateFileHistory` functions to be called.
1267 """
1268
1269 wx.EvtHandler.__init__(self)
1270
1271 self._defaultDocumentNameCounter = 1
1272 self._flags = flags
1273 self._currentView = None
1274 self._lastActiveView = None
1275 self._maxDocsOpen = 10000
1276 self._fileHistory = None
1277 self._templates = []
1278 self._docs = []
1279 self._lastDirectory = ""
1280
1281 if initialize:
1282 self.Initialize()
1283
1284 wx.EVT_MENU(self, wx.ID_OPEN, self.OnFileOpen)
1285 wx.EVT_MENU(self, wx.ID_CLOSE, self.OnFileClose)
1286 wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.OnFileCloseAll)
1287 wx.EVT_MENU(self, wx.ID_REVERT, self.OnFileRevert)
1288 wx.EVT_MENU(self, wx.ID_NEW, self.OnFileNew)
1289 wx.EVT_MENU(self, wx.ID_SAVE, self.OnFileSave)
1290 wx.EVT_MENU(self, wx.ID_SAVEAS, self.OnFileSaveAs)
1291 wx.EVT_MENU(self, wx.ID_UNDO, self.OnUndo)
1292 wx.EVT_MENU(self, wx.ID_REDO, self.OnRedo)
1293 wx.EVT_MENU(self, wx.ID_PRINT, self.OnPrint)
1294 wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.OnPrintSetup)
1295 wx.EVT_MENU(self, wx.ID_PREVIEW, self.OnPreview)
1296
1297 wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.OnUpdateFileOpen)
1298 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.OnUpdateFileClose)
1299 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.OnUpdateFileCloseAll)
1300 wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.OnUpdateFileRevert)
1301 wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.OnUpdateFileNew)
1302 wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.OnUpdateFileSave)
1303 wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.OnUpdateFileSaveAs)
1304 wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.OnUpdateUndo)
1305 wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.OnUpdateRedo)
1306 wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.OnUpdatePrint)
1307 wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.OnUpdatePrintSetup)
1308 wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.OnUpdatePreview)
1309
1310
1311 def Destroy(self):
1312 """
1313 Destructor.
1314 """
1315 self.Clear()
1316 wx.EvtHandler.Destroy(self)
1317
1318
1319 def GetFlags(self):
1320 """
1321 Returns the document manager's flags.
1322 """
1323 return self._flags
1324
1325
1326 def CloseDocument(self, doc, force = True):
1327 """
1328 Closes the specified document.
1329 """
1330 if doc.Close() or force:
1331 doc.DeleteAllViews()
1332 if doc in self._docs:
1333 doc.Destroy()
1334 return True
1335 return False
1336
1337
1338 def CloseDocuments(self, force = True):
1339 """
1340 Closes all currently opened documents.
1341 """
1342 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
1343 if not self.CloseDocument(document, force):
1344 return False
1345 document.DeleteAllViews() # Implicitly delete the document when the last view is removed
1346 return True
1347
1348
1349 def Clear(self, force = True):
1350 """
1351 Closes all currently opened document by calling `CloseDocuments` and
1352 clears the document manager's templates.
1353 """
1354 if not self.CloseDocuments(force):
1355 return False
1356 self._templates = []
1357 return True
1358
1359
1360 def Initialize(self):
1361 """
1362 Initializes data; currently just calls `OnCreateFileHistory`. Some data
1363 cannot always be initialized in the constructor because the programmer
1364 must be given the opportunity to override functionality. In fact
1365 Initialize is called from the `DocManager` constructor, but this can
1366 be prevented by passing false to the second argument, allowing the
1367 derived class's constructor to call Initialize, possibly calling a
1368 different `OnCreateFileHistory` from the default.
1369 """
1370 self.OnCreateFileHistory()
1371 return True
1372
1373
1374 def OnCreateFileHistory(self):
1375 """
1376 A hook to allow a derived class to create a different type of file
1377 history. Called from `Initialize`.
1378 """
1379 self._fileHistory = wx.FileHistory()
1380
1381
1382 def OnFileClose(self, event):
1383 """
1384 Closes and deletes the currently active document.
1385 """
1386 doc = self.GetCurrentDocument()
1387 if doc:
1388 doc.DeleteAllViews()
1389 if doc in self._docs:
1390 self._docs.remove(doc)
1391
1392
1393 def OnFileCloseAll(self, event):
1394 """
1395 Closes and deletes all the currently opened documents.
1396 """
1397 return self.CloseDocuments(force = False)
1398
1399
1400 def OnFileNew(self, event):
1401 """
1402 Creates a new document and reads in the selected file.
1403 """
1404 self.CreateDocument('', DOC_NEW)
1405
1406
1407 def OnFileOpen(self, event):
1408 """
1409 Creates a new document and reads in the selected file.
1410 """
1411 if not self.CreateDocument('', DEFAULT_DOCMAN_FLAGS):
1412 self.OnOpenFileFailure()
1413
1414
1415 def OnFileRevert(self, event):
1416 """
1417 Reverts the current document by calling `Document.Save` for
1418 the current document.
1419 """
1420 doc = self.GetCurrentDocument()
1421 if not doc:
1422 return
1423 doc.Revert()
1424
1425
1426 def OnFileSave(self, event):
1427 """
1428 Saves the current document by calling `Document.Save` for the
1429 current document.
1430 """
1431 doc = self.GetCurrentDocument()
1432 if not doc:
1433 return
1434 doc.Save()
1435
1436
1437 def OnFileSaveAs(self, event):
1438 """
1439 Calls `Document.SaveAs` for the current document.
1440 """
1441 doc = self.GetCurrentDocument()
1442 if not doc:
1443 return
1444 doc.SaveAs()
1445
1446
1447 def OnPrint(self, event):
1448 """
1449 Prints the current document by calling its
1450 `View.OnCreatePrintout` method.
1451 """
1452 view = self.GetCurrentView()
1453 if not view:
1454 return
1455
1456 printout = view.OnCreatePrintout()
1457 if printout:
1458 pdd = wx.PrintDialogData()
1459 printer = wx.Printer(pdd)
1460 printer.Print(view.GetFrame(), printout) # , True)
1461
1462
1463 def OnPrintSetup(self, event):
1464 """
1465 Presents the print setup dialog.
1466 """
1467 view = self.GetCurrentView()
1468 if view:
1469 parentWin = view.GetFrame()
1470 else:
1471 parentWin = wx.GetApp().GetTopWindow()
1472
1473 data = wx.PrintDialogData()
1474 printDialog = wx.PrintDialog(parentWin, data)
1475 printDialog.GetPrintDialogData().SetSetupDialog(True)
1476 printDialog.ShowModal()
1477 # TODO: Confirm that we don't have to remember PrintDialogData
1478
1479
1480 def OnPreview(self, event):
1481 """
1482 Previews the current document by calling its `View.OnCreatePrintout`
1483 method.
1484 """
1485 view = self.GetCurrentView()
1486 if not view:
1487 return
1488
1489 printout = view.OnCreatePrintout()
1490 if printout:
1491 # Pass two printout objects: for preview, and possible printing.
1492 preview = wx.PrintPreview(printout, view.OnCreatePrintout())
1493 # 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.
1494 mimicFrame = wx.GetApp().GetTopWindow()
1495 frame = wx.PreviewFrame(preview, mimicFrame, _("Print Preview"), mimicFrame.GetPosition(), mimicFrame.GetSize())
1496 frame.SetIcon(mimicFrame.GetIcon())
1497 frame.SetTitle(mimicFrame.GetTitle() + _(" - Preview"))
1498 frame.Initialize()
1499 frame.Show(True)
1500
1501
1502 def OnUndo(self, event):
1503 """
1504 Issues an Undo command to the current document's command processor.
1505 """
1506 doc = self.GetCurrentDocument()
1507 if not doc:
1508 return
1509 if doc.GetCommandProcessor():
1510 doc.GetCommandProcessor().Undo()
1511
1512
1513 def OnRedo(self, event):
1514 """
1515 Issues a Redo command to the current document's command processor.
1516 """
1517 doc = self.GetCurrentDocument()
1518 if not doc:
1519 return
1520 if doc.GetCommandProcessor():
1521 doc.GetCommandProcessor().Redo()
1522
1523
1524 def OnUpdateFileOpen(self, event):
1525 """
1526 Updates the user interface for the File Open command.
1527 """
1528 event.Enable(True)
1529
1530
1531 def OnUpdateFileClose(self, event):
1532 """
1533 Updates the user interface for the File Close command.
1534 """
1535 event.Enable(self.GetCurrentDocument() != None)
1536
1537
1538 def OnUpdateFileCloseAll(self, event):
1539 """
1540 Updates the user interface for the File Close All command.
1541 """
1542 event.Enable(self.GetCurrentDocument() != None)
1543
1544
1545 def OnUpdateFileRevert(self, event):
1546 """
1547 Updates the user interface for the File Revert command.
1548 """
1549 event.Enable(self.GetCurrentDocument() != None)
1550
1551
1552 def OnUpdateFileNew(self, event):
1553 """
1554 Updates the user interface for the File New command.
1555 """
1556 return True
1557
1558
1559 def OnUpdateFileSave(self, event):
1560 """
1561 Updates the user interface for the File Save command.
1562 """
1563 doc = self.GetCurrentDocument()
1564 event.Enable(doc != None and doc.IsModified())
1565
1566
1567 def OnUpdateFileSaveAs(self, event):
1568 """
1569 Updates the user interface for the File Save As command.
1570 """
1571 event.Enable(self.GetCurrentDocument() != None and self.GetCurrentDocument().GetWriteable())
1572
1573
1574 def OnUpdateUndo(self, event):
1575 """
1576 Updates the user interface for the Undo command.
1577 """
1578 doc = self.GetCurrentDocument()
1579 event.Enable(doc != None and doc.GetCommandProcessor() != None and doc.GetCommandProcessor().CanUndo())
1580 if doc and doc.GetCommandProcessor():
1581 doc.GetCommandProcessor().SetMenuStrings()
1582 else:
1583 event.SetText(_("Undo") + '\t' + _('Ctrl+Z'))
1584
1585
1586 def OnUpdateRedo(self, event):
1587 """
1588 Updates the user interface for the Redo command.
1589 """
1590 doc = self.GetCurrentDocument()
1591 event.Enable(doc != None and doc.GetCommandProcessor() != None and doc.GetCommandProcessor().CanRedo())
1592 if doc and doc.GetCommandProcessor():
1593 doc.GetCommandProcessor().SetMenuStrings()
1594 else:
1595 event.SetText(_("Redo") + '\t' + _('Ctrl+Y'))
1596
1597
1598 def OnUpdatePrint(self, event):
1599 """
1600 Updates the user interface for the Print command.
1601 """
1602 event.Enable(self.GetCurrentDocument() != None)
1603
1604
1605 def OnUpdatePrintSetup(self, event):
1606 """
1607 Updates the user interface for the Print Setup command.
1608 """
1609 return True
1610
1611
1612 def OnUpdatePreview(self, event):
1613 """
1614 Updates the user interface for the Print Preview command.
1615 """
1616 event.Enable(self.GetCurrentDocument() != None)
1617
1618
1619 def GetCurrentView(self):
1620 """
1621 Returns the currently active view.
1622 """
1623 if self._currentView:
1624 return self._currentView
1625 if len(self._docs) == 1:
1626 return self._docs[0].GetFirstView()
1627 return None
1628
1629
1630 def GetLastActiveView(self):
1631 """
1632 Returns the last active view. This is used in the SDI
1633 framework where dialogs can be mistaken for a view and causes
1634 the framework to deactivete the current view. This happens
1635 when something like a custom dialog box used to operate on the
1636 current view is shown.
1637 """
1638 if len(self._docs) >= 1:
1639 return self._lastActiveView
1640 else:
1641 return None
1642
1643
1644 def ProcessEvent(self, event):
1645 """
1646 Processes an event, searching event tables and calling zero or
1647 more suitable event handler function(s). Note that the
1648 ProcessEvent method is called from the wxPython docview
1649 framework directly since wxPython does not have a virtual
1650 ProcessEvent function.
1651 """
1652 view = self.GetCurrentView()
1653 if view:
1654 if view.ProcessEvent(event):
1655 return True
1656 id = event.GetId()
1657 if id == wx.ID_OPEN:
1658 self.OnFileOpen(event)
1659 return True
1660 elif id == wx.ID_CLOSE:
1661 self.OnFileClose(event)
1662 return True
1663 elif id == wx.ID_CLOSE_ALL:
1664 self.OnFileCloseAll(event)
1665 return True
1666 elif id == wx.ID_REVERT:
1667 self.OnFileRevert(event)
1668 return True
1669 elif id == wx.ID_NEW:
1670 self.OnFileNew(event)
1671 return True
1672 elif id == wx.ID_SAVE:
1673 self.OnFileSave(event)
1674 return True
1675 elif id == wx.ID_SAVEAS:
1676 self.OnFileSaveAs(event)
1677 return True
1678 elif id == wx.ID_UNDO:
1679 self.OnUndo(event)
1680 return True
1681 elif id == wx.ID_REDO:
1682 self.OnRedo(event)
1683 return True
1684 elif id == wx.ID_PRINT:
1685 self.OnPrint(event)
1686 return True
1687 elif id == wx.ID_PRINT_SETUP:
1688 self.OnPrintSetup(event)
1689 return True
1690 elif id == wx.ID_PREVIEW:
1691 self.OnPreview(event)
1692 return True
1693 else:
1694 return False
1695
1696
1697 def ProcessUpdateUIEvent(self, event):
1698 """
1699 Processes a UI event, searching event tables and calling zero
1700 or more suitable event handler function(s). Note that the
1701 ProcessEvent method is called from the wxPython docview
1702 framework directly since wxPython does not have a virtual
1703 ProcessEvent function.
1704 """
1705 id = event.GetId()
1706 view = self.GetCurrentView()
1707 if view:
1708 if view.ProcessUpdateUIEvent(event):
1709 return True
1710 if id == wx.ID_OPEN:
1711 self.OnUpdateFileOpen(event)
1712 return True
1713 elif id == wx.ID_CLOSE:
1714 self.OnUpdateFileClose(event)
1715 return True
1716 elif id == wx.ID_CLOSE_ALL:
1717 self.OnUpdateFileCloseAll(event)
1718 return True
1719 elif id == wx.ID_REVERT:
1720 self.OnUpdateFileRevert(event)
1721 return True
1722 elif id == wx.ID_NEW:
1723 self.OnUpdateFileNew(event)
1724 return True
1725 elif id == wx.ID_SAVE:
1726 self.OnUpdateFileSave(event)
1727 return True
1728 elif id == wx.ID_SAVEAS:
1729 self.OnUpdateFileSaveAs(event)
1730 return True
1731 elif id == wx.ID_UNDO:
1732 self.OnUpdateUndo(event)
1733 return True
1734 elif id == wx.ID_REDO:
1735 self.OnUpdateRedo(event)
1736 return True
1737 elif id == wx.ID_PRINT:
1738 self.OnUpdatePrint(event)
1739 return True
1740 elif id == wx.ID_PRINT_SETUP:
1741 self.OnUpdatePrintSetup(event)
1742 return True
1743 elif id == wx.ID_PREVIEW:
1744 self.OnUpdatePreview(event)
1745 return True
1746 else:
1747 return False
1748
1749
1750 def CreateDocument(self, path, flags = 0):
1751 """
1752 Creates a new document in a manner determined by the flags parameter,
1753 which can be:
1754
1755 * DOC_NEW: Creates a fresh document.
1756 * DOC_SILENT: Silently loads the given document file.
1757
1758 If DOC_NEW is present, a new document will be created and
1759 returned, possibly after asking the user for a template to use
1760 if there is more than one document template. If DOC_SILENT is
1761 present, a new document will be created and the given file
1762 loaded into it. If neither of these flags is present, the user
1763 will be presented with a file selector for the file to load,
1764 and the template to use will be determined by the extension
1765 (Windows) or by popping up a template choice list (other
1766 platforms).
1767
1768 If the maximum number of documents has been reached, this
1769 function will delete the oldest currently loaded document
1770 before creating a new one.
1771
1772 wxPython version supports the document manager's DOC_OPEN_ONCE
1773 flag.
1774 """
1775 templates = []
1776 for temp in self._templates:
1777 if temp.IsVisible():
1778 templates.append(temp)
1779 if len(templates) == 0:
1780 return None
1781
1782 if len(self.GetDocuments()) >= self._maxDocsOpen:
1783 doc = self.GetDocuments()[0]
1784 if not self.CloseDocument(doc, False):
1785 return None
1786
1787 if flags & DOC_NEW:
1788 if len(templates) == 1:
1789 temp = templates[0]
1790 newDoc = temp.CreateDocument(path, flags)
1791 if newDoc:
1792 newDoc.SetDocumentName(temp.GetDocumentName())
1793 newDoc.SetDocumentTemplate(temp)
1794 newDoc.OnNewDocument()
1795 return newDoc
1796
1797 temp = self.SelectDocumentType(templates)
1798 if temp:
1799 newDoc = temp.CreateDocument(path, flags)
1800 if newDoc:
1801 newDoc.SetDocumentName(temp.GetDocumentName())
1802 newDoc.SetDocumentTemplate(temp)
1803 newDoc.OnNewDocument()
1804 return newDoc
1805 else:
1806 return None
1807
1808 if path and flags & DOC_SILENT:
1809 temp = self.FindTemplateForPath(path)
1810 else:
1811 temp, path = self.SelectDocumentPath(templates, path, flags)
1812
1813 # Existing document
1814 if self.GetFlags() & DOC_OPEN_ONCE:
1815 for document in self._docs:
1816 if document.GetFilename() == path:
1817 firstView = document.GetFirstView()
1818 if firstView and firstView.GetFrame():
1819 firstView.GetFrame().SetFocus() # Not in wxWindows code but useful nonetheless
1820 if hasattr(firstView.GetFrame(), "IsIconized") and firstView.GetFrame().IsIconized(): # Not in wxWindows code but useful nonetheless
1821 firstView.GetFrame().Iconize(False)
1822 return None
1823
1824 if temp:
1825 newDoc = temp.CreateDocument(path, flags)
1826 if newDoc:
1827 newDoc.SetDocumentName(temp.GetDocumentName())
1828 newDoc.SetDocumentTemplate(temp)
1829 if not newDoc.OnOpenDocument(path):
1830 newDoc.DeleteAllViews() # Implicitly deleted by DeleteAllViews
1831 newDoc.GetFirstView().GetFrame().Destroy() # DeleteAllViews doesn't get rid of the frame, so we'll explicitly destroy it.
1832 return None
1833 self.AddFileToHistory(path)
1834 return newDoc
1835
1836 return None
1837
1838
1839 def CreateView(self, document, flags = 0):
1840 """
1841 Creates a new view for the given document. If more than one view is
1842 allowed for the document (by virtue of multiple templates mentioning
1843 the same document type), a choice of view is presented to the user.
1844 """
1845 templates = []
1846 for temp in self._templates:
1847 if temp.IsVisible():
1848 if temp.GetDocumentName() == doc.GetDocumentName():
1849 templates.append(temp)
1850 if len(templates) == 0:
1851 return None
1852
1853 if len(templates) == 1:
1854 temp = templates[0]
1855 view = temp.CreateView(doc, flags)
1856 if view:
1857 view.SetViewName(temp.GetViewName())
1858 return view
1859
1860 temp = SelectViewType(templates)
1861 if temp:
1862 view = temp.CreateView(doc, flags)
1863 if view:
1864 view.SetViewName(temp.GetViewName())
1865 return view
1866 else:
1867 return None
1868
1869
1870 def DeleteTemplate(self, template, flags):
1871 """
1872 Placeholder, not yet implemented
1873 """
1874 pass
1875
1876
1877 def FlushDoc(self, doc):
1878 """
1879 Placeholder, not yet implemented
1880 """
1881 return False
1882
1883
1884 def MatchTemplate(self, path):
1885 """
1886 Placeholder, not yet implemented
1887 """
1888 return None
1889
1890
1891 def GetCurrentDocument(self):
1892 """
1893 Returns the document associated with the currently active view (if any).
1894 """
1895 view = self.GetCurrentView()
1896 if view:
1897 return view.GetDocument()
1898 else:
1899 return None
1900
1901
1902 def MakeDefaultName(self):
1903 """
1904 Returns a suitable default name. This is implemented by appending an
1905 integer counter to the string "Untitled" and incrementing the counter.
1906 """
1907 name = _("Untitled %d") % self._defaultDocumentNameCounter
1908 self._defaultDocumentNameCounter = self._defaultDocumentNameCounter + 1
1909 return name
1910
1911
1912 def MakeFrameTitle(self):
1913 """
1914 Returns a suitable title for a document frame. This is implemented by
1915 appending the document name to the application name.
1916 """
1917 appName = wx.GetApp().GetAppName()
1918 if not doc:
1919 title = appName
1920 else:
1921 docName = doc.GetPrintableName()
1922 title = docName + _(" - ") + appName
1923 return title
1924
1925
1926 def AddFileToHistory(self, fileName):
1927 """
1928 Adds a file to the file history list, if we have a pointer to an
1929 appropriate file menu.
1930 """
1931 if self._fileHistory:
1932 self._fileHistory.AddFileToHistory(fileName)
1933
1934
1935 def RemoveFileFromHistory(self, i):
1936 """
1937 Removes a file from the file history list, if we have a pointer to an
1938 appropriate file menu.
1939 """
1940 if self._fileHistory:
1941 self._fileHistory.RemoveFileFromHistory(i)
1942
1943
1944 def GetFileHistory(self):
1945 """
1946 Returns the file history.
1947 """
1948 return self._fileHistory
1949
1950
1951 def GetHistoryFile(self, i):
1952 """
1953 Returns the file at index i from the file history.
1954 """
1955 if self._fileHistory:
1956 return self._fileHistory.GetHistoryFile(i)
1957 else:
1958 return None
1959
1960
1961 def FileHistoryUseMenu(self, menu):
1962 """
1963 Use this menu for appending recently-visited document filenames, for
1964 convenient access. Calling this function with a valid menu enables the
1965 history list functionality.
1966
1967 Note that you can add multiple menus using this function, to be
1968 managed by the file history object.
1969 """
1970 if self._fileHistory:
1971 self._fileHistory.UseMenu(menu)
1972
1973
1974 def FileHistoryRemoveMenu(self, menu):
1975 """
1976 Removes the given menu from the list of menus managed by the file
1977 history object.
1978 """
1979 if self._fileHistory:
1980 self._fileHistory.RemoveMenu(menu)
1981
1982
1983 def FileHistoryLoad(self, config):
1984 """
1985 Loads the file history from a config object.
1986 """
1987 if self._fileHistory:
1988 self._fileHistory.Load(config)
1989
1990
1991 def FileHistorySave(self, config):
1992 """
1993 Saves the file history into a config object. This must be called
1994 explicitly by the application.
1995 """
1996 if self._fileHistory:
1997 self._fileHistory.Save(config)
1998
1999
2000 def FileHistoryAddFilesToMenu(self, menu = None):
2001 """
2002 Appends the files in the history list, to all menus managed by the
2003 file history object.
2004
2005 If menu is specified, appends the files in the history list to the
2006 given menu only.
2007 """
2008 if self._fileHistory:
2009 if menu:
2010 self._fileHistory.AddFilesToThisMenu(menu)
2011 else:
2012 self._fileHistory.AddFilesToMenu()
2013
2014
2015 def GetHistoryFilesCount(self):
2016 """
2017 Returns the number of files currently stored in the file history.
2018 """
2019 if self._fileHistory:
2020 return self._fileHistory.GetNoHistoryFiles()
2021 else:
2022 return 0
2023
2024
2025 def FindTemplateForPath(self, path):
2026 """
2027 Given a path, try to find template that matches the extension. This is
2028 only an approximate method of finding a template for creating a
2029 document.
2030 """
2031 for temp in self._templates:
2032 if temp.FileMatchesTemplate(path):
2033 return temp
2034 return None
2035
2036
2037 def FindSuitableParent(self):
2038 """
2039 Returns a parent frame or dialog, either the frame with the current
2040 focus or if there is no current focus the application's top frame.
2041 """
2042 parent = wx.GetApp().GetTopWindow()
2043 focusWindow = wx.Window_FindFocus()
2044 if focusWindow:
2045 while focusWindow and not isinstance(focusWindow, wx.Dialog) and not isinstance(focusWindow, wx.Frame):
2046 focusWindow = focusWindow.GetParent()
2047 if focusWindow:
2048 parent = focusWindow
2049 return parent
2050
2051
2052 def SelectDocumentPath(self, templates, flags, save):
2053 """
2054 Under Windows, pops up a file selector with a list of filters
2055 corresponding to document templates. The wxDocTemplate corresponding
2056 to the selected file's extension is returned.
2057
2058 On other platforms, if there is more than one document template a
2059 choice list is popped up, followed by a file selector.
2060
2061 This function is used in `DocManager.CreateDocument`.
2062 """
2063 if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
2064 allfilter = ''
2065 descr = ''
2066 for temp in templates:
2067 if temp.IsVisible():
2068 if len(descr) > 0:
2069 descr = descr + _('|')
2070 allfilter = allfilter + _(';')
2071 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
2072 allfilter = allfilter + temp.GetFileFilter()
2073 descr = _("All") + _(" (") + allfilter + _(") |") + allfilter + _('|') + descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
2074 else:
2075 descr = _("*.*")
2076
2077 path = wx.FileSelector(_("Select a File"),
2078 self._lastDirectory,
2079 _(""),
2080 wildcard = descr,
2081 flags = wx.HIDE_READONLY,
2082 parent = self.FindSuitableParent())
2083 if path:
2084 if not FileExists(path):
2085 msgTitle = wx.GetApp().GetAppName()
2086 if not msgTitle:
2087 msgTitle = _("File Error")
2088 wx.MessageBox("Could not open '%s'." % FileNameFromPath(path),
2089 msgTitle,
2090 wx.OK | wx.ICON_EXCLAMATION,
2091 parent)
2092 return (None, None)
2093 self._lastDirectory = PathOnly(path)
2094
2095 theTemplate = self.FindTemplateForPath(path)
2096 return (theTemplate, path)
2097
2098 return (None, None)
2099
2100
2101 def OnOpenFileFailure(self):
2102 """
2103 Called when there is an error opening a file.
2104 """
2105 pass
2106
2107
2108 def SelectDocumentType(self, temps, sort = False):
2109 """
2110 Returns a document template by asking the user (if there is more than
2111 one template). This function is used in `DocManager.CreateDocument`.
2112
2113 :param temps: list of templates from which to choose a desired template.
2114
2115 :param sort: If more than one template is passed in in
2116 templates, then this parameter indicates whether the list
2117 of templates that the user will have to choose from is
2118 sorted or not when shown the choice box dialog. Default
2119 is false.
2120 """
2121 templates = []
2122 for temp in temps:
2123 if temp.IsVisible():
2124 want = True
2125 for temp2 in templates:
2126 if temp.GetDocumentName() == temp2.GetDocumentName() and temp.GetViewName() == temp2.GetViewName():
2127 want = False
2128 break
2129 if want:
2130 templates.append(temp)
2131
2132 if len(templates) == 0:
2133 return None
2134 elif len(templates) == 1:
2135 return template[0]
2136
2137 if sort:
2138 def tempcmp(a, b):
2139 return cmp(a.GetDescription(), b.GetDescription())
2140 templates.sort(tempcmp)
2141
2142 strings = []
2143 for temp in templates:
2144 strings.append(temp.GetDescription())
2145
2146 res = wx.GetSingleChoiceIndex(_("Select a document type:"),
2147 _("Documents"),
2148 strings,
2149 self.FindSuitableParent())
2150 if res == -1:
2151 return None
2152 return templates[res]
2153
2154
2155 def SelectViewType(self, temps, sort = False):
2156 """
2157 Returns a document template by asking the user (if there is
2158 more than one template), displaying a list of valid
2159 views. This function is used in `DocManager.CreateView`. The
2160 dialog normally will not appear because the array of templates
2161 only contains those relevant to the document in question, and
2162 often there will only be one such.
2163 """
2164 templates = []
2165 strings = []
2166 for temp in temps:
2167 if temp.IsVisible() and temp.GetViewTypeName():
2168 if temp.GetViewName() not in strings:
2169 templates.append(temp)
2170 strings.append(temp.GetViewTypeName())
2171
2172 if len(templates) == 0:
2173 return None
2174 elif len(templates) == 1:
2175 return templates[0]
2176
2177 if sort:
2178 def tempcmp(a, b):
2179 return cmp(a.GetViewTypeName(), b.GetViewTypeName())
2180 templates.sort(tempcmp)
2181
2182 res = wx.GetSingleChoiceIndex(_("Select a document view:"),
2183 _("Views"),
2184 strings,
2185 self.FindSuitableParent())
2186 if res == -1:
2187 return None
2188 return templates[res]
2189
2190
2191 def GetTemplates(self):
2192 """
2193 Returns the document manager's template list.
2194 """
2195 return self._templates
2196
2197
2198 def AssociateTemplate(self, docTemplate):
2199 """
2200 Adds the template to the document manager's template list.
2201 """
2202 if docTemplate not in self._templates:
2203 self._templates.append(docTemplate)
2204
2205
2206 def DisassociateTemplate(self, docTemplate):
2207 """
2208 Removes the template from the list of templates.
2209 """
2210 self._templates.remove(docTemplate)
2211
2212
2213 def AddDocument(self, document):
2214 """
2215 Adds the document to the list of documents.
2216 """
2217 if document not in self._docs:
2218 self._docs.append(document)
2219
2220
2221 def RemoveDocument(self, doc):
2222 """
2223 Removes the document from the list of documents.
2224 """
2225 if doc in self._docs:
2226 self._docs.remove(doc)
2227
2228
2229 def ActivateView(self, view, activate = True, deleting = False):
2230 """
2231 Sets the current view.
2232 """
2233 if activate:
2234 self._currentView = view
2235 self._lastActiveView = view
2236 else:
2237 self._currentView = None
2238
2239
2240 def GetMaxDocsOpen(self):
2241 """
2242 Returns the number of documents that can be open simultaneously.
2243 """
2244 return self._maxDocsOpen
2245
2246
2247 def SetMaxDocsOpen(self, maxDocsOpen):
2248 """
2249 Sets the maximum number of documents that can be open at a time. By
2250 default, this is 10,000. If you set it to 1, existing documents will
2251 be saved and deleted when the user tries to open or create a new one
2252 (similar to the behaviour of Windows Write, for example). Allowing
2253 multiple documents gives behaviour more akin to MS Word and other
2254 Multiple Document Interface applications.
2255 """
2256 self._maxDocsOpen = maxDocsOpen
2257
2258
2259 def GetDocuments(self):
2260 """
2261 Returns the list of documents.
2262 """
2263 return self._docs
2264
2265
2266 class DocParentFrame(wx.Frame):
2267 """
2268 The DocParentFrame class provides a default top-level frame for
2269 applications using the document/view framework. This class can
2270 only be used for SDI (not MDI) parent frames.
2271
2272 It cooperates with the `View`, `Document`, `DocManager` and
2273 `DocTemplate` classes.
2274 """
2275
2276 def __init__(self, manager, frame, id, title,
2277 pos = wx.DefaultPosition,
2278 size = wx.DefaultSize,
2279 style = wx.DEFAULT_FRAME_STYLE,
2280 name = "frame"):
2281 """
2282 Constructor. Note that the event table must be rebuilt for the
2283 frame since the EvtHandler is not virtual.
2284 """
2285 wx.Frame.__init__(self, frame, id, title, pos, size, style)
2286 self._docManager = manager
2287
2288 wx.EVT_CLOSE(self, self.OnCloseWindow)
2289
2290 wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
2291 wx.EVT_MENU_RANGE(self, wx.ID_FILE1, wx.ID_FILE9, self.OnMRUFile)
2292
2293 wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
2294 wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
2295 wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
2296 wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
2297 wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
2298 wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
2299 wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
2300 wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
2301 wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
2302 wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
2303 wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
2304 wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
2305
2306 wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
2307 wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
2308 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
2309 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
2310 wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
2311 wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
2312 wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
2313 wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
2314 wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
2315 wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
2316 wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
2317 wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
2318
2319
2320 def ProcessEvent(self, event):
2321 """
2322 Processes an event, searching event tables and calling zero or more
2323 suitable event handler function(s). Note that the ProcessEvent
2324 method is called from the wxPython docview framework directly since
2325 wxPython does not have a virtual ProcessEvent function.
2326 """
2327 return self._docManager and self._docManager.ProcessEvent(event)
2328
2329
2330 def ProcessUpdateUIEvent(self, event):
2331 """
2332 Processes a UI event, searching event tables and calling zero or more
2333 suitable event handler function(s). Note that the ProcessEvent
2334 method is called from the wxPython docview framework directly since
2335 wxPython does not have a virtual ProcessEvent function.
2336 """
2337 return self._docManager and self._docManager.ProcessUpdateUIEvent(event)
2338
2339
2340 def OnExit(self, event):
2341 """
2342 Called when File/Exit is chosen and closes the window.
2343 """
2344 self.Close()
2345
2346
2347 def OnMRUFile(self, event):
2348 """
2349 Opens the appropriate file when it is selected from the file history
2350 menu.
2351 """
2352 n = event.GetId() - wx.ID_FILE1
2353 filename = self._docManager.GetHistoryFile(n)
2354 if filename:
2355 self._docManager.CreateDocument(filename, DOC_SILENT)
2356 else:
2357 self._docManager.RemoveFileFromHistory(n)
2358 msgTitle = wx.GetApp().GetAppName()
2359 if not msgTitle:
2360 msgTitle = _("File Error")
2361 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),
2362 msgTitle,
2363 wx.OK | wx.ICON_EXCLAMATION,
2364 self)
2365
2366
2367 def OnCloseWindow(self, event):
2368 """
2369 Deletes all views and documents. If no user input cancelled the
2370 operation, the frame will be destroyed and the application will exit.
2371 """
2372 if self._docManager.Clear(not event.CanVeto()):
2373 self.Destroy()
2374 else:
2375 event.Veto()
2376
2377
2378 class DocChildFrame(wx.Frame):
2379 """
2380 The `DocChildFrame` class provides a default frame for displaying
2381 documents on separate windows. This class can only be used for SDI
2382 (not MDI) child frames.
2383
2384 The class is part of the document/view framework and cooperates
2385 with the `View`, `Document`, `DocManager` and `DocTemplate`
2386 classes.
2387 """
2388
2389
2390 def __init__(self, doc, view, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
2391 """
2392 Constructor. Note that the event table must be rebuilt for the
2393 frame since the EvtHandler is not virtual.
2394 """
2395 wx.Frame.__init__(self, frame, id, title, pos, size, style, name)
2396 wx.EVT_ACTIVATE(self, self.OnActivate)
2397 wx.EVT_CLOSE(self, self.OnCloseWindow)
2398 self._childDocument = doc
2399 self._childView = view
2400 if view:
2401 view.SetFrame(self)
2402
2403 wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
2404 wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
2405 wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
2406 wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
2407 wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
2408 wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
2409 wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
2410 wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
2411 wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
2412 wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
2413 wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
2414 wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
2415
2416 wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
2417 wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
2418 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
2419 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
2420 wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
2421 wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
2422 wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
2423 wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
2424 wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
2425 wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
2426 wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
2427 wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
2428
2429
2430 def ProcessEvent(self, event):
2431 """
2432 Processes an event, searching event tables and calling zero or more
2433 suitable event handler function(s). Note that the ProcessEvent
2434 method is called from the wxPython docview framework directly since
2435 wxPython does not have a virtual ProcessEvent function.
2436 """
2437 if self._childView:
2438 self._childView.Activate(True)
2439 if not self._childView or not self._childView.ProcessEvent(event):
2440 # IsInstance not working, but who cares just send all the commands up since this isn't a real ProcessEvent like wxWindows
2441 # if not isinstance(event, wx.CommandEvent) or not self.GetParent() or not self.GetParent().ProcessEvent(event):
2442 if not self.GetParent() or not self.GetParent().ProcessEvent(event):
2443 return False
2444 else:
2445 return True
2446 else:
2447 return True
2448
2449
2450 def ProcessUpdateUIEvent(self, event):
2451 """
2452 Processes a UI event, searching event tables and calling zero or more
2453 suitable event handler function(s). Note that the ProcessEvent
2454 method is called from the wxPython docview framework directly since
2455 wxPython does not have a virtual ProcessEvent function.
2456 """
2457 if self.GetParent():
2458 self.GetParent().ProcessUpdateUIEvent(event)
2459 else:
2460 return False
2461
2462
2463 def OnActivate(self, event):
2464 """
2465 Activates the current view.
2466 """
2467 # wx.Frame.OnActivate(event) This is in the wxWindows docview demo but there is no such method in wxPython, so do a Raise() instead
2468 if self._childView:
2469 self._childView.Activate(event.GetActive())
2470
2471
2472 def OnCloseWindow(self, event):
2473 """
2474 Closes and deletes the current view and document.
2475 """
2476 if self._childView:
2477 ans = False
2478 if not event.CanVeto():
2479 ans = True
2480 else:
2481 ans = self._childView.Close(deleteWindow = False)
2482
2483 if ans:
2484 self._childView.Activate(False)
2485 self._childView.Destroy()
2486 self._childView = None
2487 if self._childDocument:
2488 self._childDocument.Destroy() # This isn't in the wxWindows codebase but the document needs to be disposed of somehow
2489 self._childDocument = None
2490 self.Destroy()
2491 else:
2492 event.Veto()
2493 else:
2494 event.Veto()
2495
2496
2497 def GetDocument(self):
2498 """
2499 Returns the document associated with this frame.
2500 """
2501 return self._childDocument
2502
2503
2504 def SetDocument(self, document):
2505 """
2506 Sets the document for this frame.
2507 """
2508 self._childDocument = document
2509
2510
2511 def GetView(self):
2512 """
2513 Returns the view associated with this frame.
2514 """
2515 return self._childView
2516
2517
2518 def SetView(self, view):
2519 """
2520 Sets the view for this frame.
2521 """
2522 self._childView = view
2523
2524
2525 class DocMDIParentFrame(wx.MDIParentFrame):
2526 """
2527 The `DocMDIParentFrame` class provides a default top-level frame
2528 for applications using the document/view framework. This class can
2529 only be used for MDI parent frames.
2530
2531 It cooperates with the `View`, `Document`, `DocManager` and
2532 `DocTemplate` classes.
2533 """
2534
2535
2536 def __init__(self, manager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
2537 """
2538 Constructor. Note that the event table must be rebuilt for the
2539 frame since the EvtHandler is not virtual.
2540 """
2541 wx.MDIParentFrame.__init__(self, frame, id, title, pos, size, style, name)
2542 self._docManager = manager
2543
2544 wx.EVT_CLOSE(self, self.OnCloseWindow)
2545
2546 wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit)
2547 wx.EVT_MENU_RANGE(self, wx.ID_FILE1, wx.ID_FILE9, self.OnMRUFile)
2548
2549 wx.EVT_MENU(self, wx.ID_NEW, self.ProcessEvent)
2550 wx.EVT_MENU(self, wx.ID_OPEN, self.ProcessEvent)
2551 wx.EVT_MENU(self, wx.ID_CLOSE_ALL, self.ProcessEvent)
2552 wx.EVT_MENU(self, wx.ID_CLOSE, self.ProcessEvent)
2553 wx.EVT_MENU(self, wx.ID_REVERT, self.ProcessEvent)
2554 wx.EVT_MENU(self, wx.ID_SAVE, self.ProcessEvent)
2555 wx.EVT_MENU(self, wx.ID_SAVEAS, self.ProcessEvent)
2556 wx.EVT_MENU(self, wx.ID_UNDO, self.ProcessEvent)
2557 wx.EVT_MENU(self, wx.ID_REDO, self.ProcessEvent)
2558 wx.EVT_MENU(self, wx.ID_PRINT, self.ProcessEvent)
2559 wx.EVT_MENU(self, wx.ID_PRINT_SETUP, self.ProcessEvent)
2560 wx.EVT_MENU(self, wx.ID_PREVIEW, self.ProcessEvent)
2561
2562 wx.EVT_UPDATE_UI(self, wx.ID_NEW, self.ProcessUpdateUIEvent)
2563 wx.EVT_UPDATE_UI(self, wx.ID_OPEN, self.ProcessUpdateUIEvent)
2564 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE_ALL, self.ProcessUpdateUIEvent)
2565 wx.EVT_UPDATE_UI(self, wx.ID_CLOSE, self.ProcessUpdateUIEvent)
2566 wx.EVT_UPDATE_UI(self, wx.ID_REVERT, self.ProcessUpdateUIEvent)
2567 wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.ProcessUpdateUIEvent)
2568 wx.EVT_UPDATE_UI(self, wx.ID_SAVEAS, self.ProcessUpdateUIEvent)
2569 wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.ProcessUpdateUIEvent)
2570 wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.ProcessUpdateUIEvent)
2571 wx.EVT_UPDATE_UI(self, wx.ID_PRINT, self.ProcessUpdateUIEvent)
2572 wx.EVT_UPDATE_UI(self, wx.ID_PRINT_SETUP, self.ProcessUpdateUIEvent)
2573 wx.EVT_UPDATE_UI(self, wx.ID_PREVIEW, self.ProcessUpdateUIEvent)
2574
2575
2576 def ProcessEvent(self, event):
2577 """
2578 Processes an event, searching event tables and calling zero or more
2579 suitable event handler function(s). Note that the ProcessEvent
2580 method is called from the wxPython docview framework directly since
2581 wxPython does not have a virtual ProcessEvent function.
2582 """
2583 return self._docManager and self._docManager.ProcessEvent(event)
2584
2585
2586 def ProcessUpdateUIEvent(self, event):
2587 """
2588 Processes a UI event, searching event tables and calling zero or more
2589 suitable event handler function(s). Note that the ProcessEvent
2590 method is called from the wxPython docview framework directly since
2591 wxPython does not have a virtual ProcessEvent function.
2592 """
2593 return self._docManager and self._docManager.ProcessUpdateUIEvent(event)
2594
2595
2596 def OnExit(self, event):
2597 """
2598 Called when File/Exit is chosen and closes the window.
2599 """
2600 self.Close()
2601
2602
2603 def OnMRUFile(self, event):
2604 """
2605 Opens the appropriate file when it is selected from the file history
2606 menu.
2607 """
2608 n = event.GetId() - wx.ID_FILE1
2609 filename = self._docManager.GetHistoryFile(n)
2610 if filename:
2611 self._docManager.CreateDocument(filename, DOC_SILENT)
2612 else:
2613 self._docManager.RemoveFileFromHistory(n)
2614 msgTitle = wx.GetApp().GetAppName()
2615 if not msgTitle:
2616 msgTitle = _("File Error")
2617 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),
2618 msgTitle,
2619 wx.OK | wx.ICON_EXCLAMATION,
2620 self)
2621
2622
2623 def OnCloseWindow(self, event):
2624 """
2625 Deletes all views and documents. If no user input cancelled the
2626 operation, the frame will be destroyed and the application will exit.
2627 """
2628 if self._docManager.Clear(not event.CanVeto()):
2629 self.Destroy()
2630 else:
2631 event.Veto()
2632
2633
2634 class DocMDIChildFrame(wx.MDIChildFrame):
2635 """
2636 The `DocMDIChildFrame` class provides a default frame for
2637 displaying documents on separate windows. This class can only be
2638 used for MDI child frames.
2639
2640 The class is part of the document/view framework and cooperates
2641 with the `View`, `Document`, `DocManager` and `DocTemplate`
2642 classes.
2643 """
2644
2645
2646 def __init__(self, doc, view, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "frame"):
2647 """
2648 Constructor. Note that the event table must be rebuilt for the
2649 frame since the EvtHandler is not virtual.
2650 """
2651 wx.MDIChildFrame.__init__(self, frame, id, title, pos, size, style, name)
2652 self._childDocument = doc
2653 self._childView = view
2654 if view:
2655 view.SetFrame(self)
2656 # self.Create(doc, view, frame, id, title, pos, size, style, name)
2657 self._activeEvent = None
2658 self._activated = 0
2659 wx.EVT_ACTIVATE(self, self.OnActivate)
2660 wx.EVT_CLOSE(self, self.OnCloseWindow)
2661
2662 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
2663 mdiChildren = filter(lambda x: isinstance(x, wx.MDIChildFrame), frame.GetChildren())
2664 if len(mdiChildren) == 1:
2665 self.Activate()
2666
2667
2668 ## # Couldn't get this to work, but seems to work fine with single stage construction
2669 ## def Create(self, doc, view, frame, id, title, pos, size, style, name):
2670 ## self._childDocument = doc
2671 ## self._childView = view
2672 ## if wx.MDIChildFrame.Create(self, frame, id, title, pos, size, style, name):
2673 ## if view:
2674 ## view.SetFrame(self)
2675 ## return True
2676 ## return False
2677
2678
2679
2680 def Activate(self): # Need this in case there are embedded sash windows and such, OnActivate is not getting called
2681 """
2682 Activates the current view.
2683 """
2684 if self._childView:
2685 self._childView.Activate(True)
2686
2687
2688 def ProcessEvent(event):
2689 """
2690 Processes an event, searching event tables and calling zero or more
2691 suitable event handler function(s). Note that the ProcessEvent
2692 method is called from the wxPython docview framework directly since
2693 wxPython does not have a virtual ProcessEvent function.
2694 """
2695 if self._activeEvent == event:
2696 return False
2697
2698 self._activeEvent = event # Break recursion loops
2699
2700 if self._childView:
2701 self._childView.Activate(True)
2702
2703 if not self._childView or not self._childView.ProcessEvent(event):
2704 if not isinstance(event, wx.CommandEvent) or not self.GetParent() or not self.GetParent().ProcessEvent(event):
2705 ret = False
2706 else:
2707 ret = True
2708 else:
2709 ret = True
2710
2711 self._activeEvent = None
2712 return ret
2713
2714
2715 def OnActivate(self, event):
2716 """
2717 Sets the currently active view to be the frame's view. You may need to
2718 override (but still call) this function in order to set the keyboard
2719 focus for your subwindow.
2720 """
2721 if self._activated != 0:
2722 return True
2723 self._activated += 1
2724 wx.MDIChildFrame.Activate(self)
2725 if event.GetActive() and self._childView:
2726 self._childView.Activate(event.GetActive())
2727 self._activated = 0
2728
2729 def OnCloseWindow(self, event):
2730 """
2731 Closes and deletes the current view and document.
2732 """
2733 if self._childView:
2734 ans = False
2735 if not event.CanVeto():
2736 ans = True
2737 else:
2738 ans = self._childView.Close(deleteWindow = False)
2739
2740 if ans:
2741 self._childView.Activate(False)
2742 self._childView.Destroy()
2743 self._childView = None
2744 if self._childDocument:
2745 self._childDocument.Destroy() # This isn't in the wxWindows codebase but the document needs to be disposed of somehow
2746 self._childDocument = None
2747 self.Destroy()
2748 else:
2749 event.Veto()
2750 else:
2751 event.Veto()
2752
2753
2754 def GetDocument(self):
2755 """
2756 Returns the document associated with this frame.
2757 """
2758 return self._childDocument
2759
2760
2761 def SetDocument(self, document):
2762 """
2763 Sets the document for this frame.
2764 """
2765 self._childDocument = document
2766
2767
2768 def GetView(self):
2769 """
2770 Returns the view associated with this frame.
2771 """
2772 return self._childView
2773
2774
2775 def SetView(self, view):
2776 """
2777 Sets the view for this frame.
2778 """
2779 self._childView = view
2780
2781
2782 class DocPrintout(wx.Printout):
2783 """
2784 `DocPrintout` is a default Printout that prints the first page of
2785 a document view.
2786 """
2787
2788
2789 def __init__(self, view, title = "Printout"):
2790 """
2791 Constructor.
2792 """
2793 wx.Printout.__init__(self)
2794 self._printoutView = view
2795
2796
2797 def GetView(self):
2798 """
2799 Returns the DocPrintout's view.
2800 """
2801 return self._printoutView
2802
2803
2804 def OnPrintPage(self, page):
2805 """
2806 Prints the first page of the view.
2807 """
2808 dc = self.GetDC()
2809 ppiScreenX, ppiScreenY = self.GetPPIScreen()
2810 ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
2811 scale = ppiPrinterX/ppiScreenX
2812 w, h = dc.GetSize()
2813 pageWidth, pageHeight = self.GetPageSizePixels()
2814 overallScale = scale * w / pageWidth
2815 dc.SetUserScale(overallScale, overallScale)
2816 if self._printoutView:
2817 self._printoutView.OnDraw(dc)
2818 return True
2819
2820
2821 def HasPage(self, pageNum):
2822 """
2823 Indicates that the DocPrintout only has a single page.
2824 """
2825 return pageNum == 1
2826
2827
2828 def OnBeginDocument(self, startPage, endPage):
2829 """
2830 Not quite sure why this was overridden, but it was in wxWindows! :)
2831 """
2832 if not wx.Printout.base_OnBeginDocument(self, startPage, endPage):
2833 return False
2834 return True
2835
2836
2837 def GetPageInfo(self):
2838 """
2839 Indicates that the DocPrintout only has a single page.
2840 """
2841 minPage = 1
2842 maxPage = 1
2843 selPageFrom = 1
2844 selPageTo = 1
2845 return (minPage, maxPage, selPageFrom, selPageTo)
2846
2847
2848 #----------------------------------------------------------------------
2849 # Command Classes
2850 #----------------------------------------------------------------------
2851
2852 class Command(wx.Object):
2853 """
2854 `Command` is a base class for modelling an application command,
2855 which is an action usually performed by selecting a menu item,
2856 pressing a toolbar button or any other means provided by the
2857 application to change the data or view.
2858 """
2859
2860
2861 def __init__(self, canUndo = False, name = None):
2862 """
2863 Constructor. Command is an abstract class, so you will need to
2864 derive a new class and call this constructor from your own
2865 constructor.
2866
2867 :param canUndo: tells the command processor whether this
2868 command is undo-able. You can achieve the same
2869 functionality by overriding the `CanUndo` member function
2870 (if for example the criteria for undoability is context-
2871 dependent).
2872
2873 :param name: must be supplied for the command processor to
2874 display the command name in the application's edit menu.
2875
2876 """
2877 self._canUndo = canUndo
2878 self._name = name
2879
2880
2881 def CanUndo(self):
2882 """
2883 Returns true if the command can be undone, false otherwise.
2884 """
2885 return self._canUndo
2886
2887
2888 def GetName(self):
2889 """
2890 Returns the command name.
2891 """
2892 return self._name
2893
2894
2895 def Do(self):
2896 """
2897 Override this member function to execute the appropriate
2898 action when called. Return true to indicate that the action
2899 has taken place, false otherwise. Returning false will
2900 indicate to the command processor that the action is not
2901 undoable and should not be added to the command history.
2902 """
2903 return True
2904
2905
2906 def Undo(self):
2907 """
2908 Override this member function to un-execute a previous
2909 `Do`. Return true to indicate that the action has taken place,
2910 false otherwise. Returning false will indicate to the command
2911 processor that the action is not redoable and no change should
2912 be made to the command history.
2913
2914 How you implement this command is totally application
2915 dependent, but typical strategies include:
2916
2917 * Perform an inverse operation on the last modified piece
2918 of data in the document. When redone, a copy of data
2919 stored in command is pasted back or some operation
2920 reapplied. This relies on the fact that you know the
2921 ordering of Undos; the user can never Undo at an
2922 arbitrary position in he command history.
2923
2924 * Restore the entire document state (perhaps using
2925 document transactioning). Potentially very inefficient,
2926 but possibly easier to code if the user interface and
2927 data are complex, and an 'inverse execute' operation is
2928 hard to write.
2929 """
2930 return True
2931
2932
2933 class CommandProcessor(wx.Object):
2934 """
2935 `CommandProcessor` is a class that maintains a history of
2936 `Command` instancess, with undo/redo functionality
2937 built-in. Derive a new class from this if you want different
2938 behaviour.
2939 """
2940
2941
2942 def __init__(self, maxCommands = -1):
2943 """
2944 Constructor. maxCommands may be set to a positive integer to limit
2945 the number of commands stored to it, otherwise (and by default) the
2946 list of commands can grow arbitrarily.
2947 """
2948 self._maxCommands = maxCommands
2949 self._editMenu = None
2950 self._undoAccelerator = _("Ctrl+Z")
2951 self._redoAccelerator = _("Ctrl+Y")
2952 self.ClearCommands()
2953
2954
2955 def _GetCurrentCommand(self):
2956 if len(self._commands) == 0:
2957 return None
2958 else:
2959 return self._commands[-1]
2960
2961
2962 def _GetCurrentRedoCommand(self):
2963 if len(self._redoCommands) == 0:
2964 return None
2965 else:
2966 return self._redoCommands[-1]
2967
2968
2969 def GetMaxCommands(self):
2970 """
2971 Returns the maximum number of commands that the command processor
2972 stores.
2973
2974 """
2975 return self._maxCommands
2976
2977
2978 def GetCommands(self):
2979 """
2980 Returns the list of commands.
2981 """
2982 return self._commands
2983
2984
2985 def ClearCommands(self):
2986 """
2987 Deletes all the commands in the list and sets the current command
2988 pointer to None.
2989 """
2990 self._commands = []
2991 self._redoCommands = []
2992
2993
2994 def GetEditMenu(self):
2995 """
2996 Returns the edit menu associated with the command processor.
2997 """
2998 return self._editMenu
2999
3000
3001 def SetEditMenu(self, menu):
3002 """
3003 Tells the command processor to update the Undo and Redo items
3004 on this menu as appropriate. Set this to None if the menu is
3005 about to be destroyed and command operations may still be
3006 performed, or the command processor may try to access an
3007 invalid pointer.
3008 """
3009 self._editMenu = menu
3010
3011
3012 def GetUndoAccelerator(self):
3013 """
3014 Returns the string that will be appended to the Undo menu item.
3015 """
3016 return self._undoAccelerator
3017
3018
3019 def SetUndoAccelerator(self, accel):
3020 """
3021 Sets the string that will be appended to the Redo menu item.
3022 """
3023 self._undoAccelerator = accel
3024
3025
3026 def GetRedoAccelerator(self):
3027 """
3028 Returns the string that will be appended to the Redo menu item.
3029 """
3030 return self._redoAccelerator
3031
3032
3033 def SetRedoAccelerator(self, accel):
3034 """
3035 Sets the string that will be appended to the Redo menu item.
3036 """
3037 self._redoAccelerator = accel
3038
3039
3040 def SetMenuStrings(self):
3041 """
3042 Sets the menu labels according to the currently set menu and
3043 the current command state.
3044 """
3045 if self.GetEditMenu() != None:
3046 undoCommand = self._GetCurrentCommand()
3047 redoCommand = self._GetCurrentRedoCommand()
3048 undoItem = self.GetEditMenu().FindItemById(wx.ID_UNDO)
3049 redoItem = self.GetEditMenu().FindItemById(wx.ID_REDO)
3050 if self.GetUndoAccelerator():
3051 undoAccel = '\t' + self.GetUndoAccelerator()
3052 else:
3053 undoAccel = ''
3054 if self.GetRedoAccelerator():
3055 redoAccel = '\t' + self.GetRedoAccelerator()
3056 else:
3057 redoAccel = ''
3058 if undoCommand and undoItem and undoCommand.CanUndo():
3059 undoItem.SetText(_("Undo ") + undoCommand.GetName() + undoAccel)
3060 #elif undoCommand and not undoCommand.CanUndo():
3061 # undoItem.SetText(_("Can't Undo") + undoAccel)
3062 else:
3063 undoItem.SetText(_("Undo" + undoAccel))
3064 if redoCommand and redoItem:
3065 redoItem.SetText(_("Redo ") + redoCommand.GetName() + redoAccel)
3066 else:
3067 redoItem.SetText(_("Redo") + redoAccel)
3068
3069
3070 def CanUndo(self):
3071 """
3072 Returns true if the currently-active command can be undone, false
3073 otherwise.
3074 """
3075 if self._GetCurrentCommand() == None:
3076 return False
3077 return self._GetCurrentCommand().CanUndo()
3078
3079
3080 def CanRedo(self):
3081 """
3082 Returns true if the currently-active command can be redone, false
3083 otherwise.
3084 """
3085 return self._GetCurrentRedoCommand() != None
3086
3087
3088 def Submit(self, command, storeIt = True):
3089 """
3090 Submits a new command to the command processor. The command processor
3091 calls `Command.Do` to execute the command; if it succeeds, the
3092 command is stored in the history list, and the associated edit menu
3093 (if any) updated appropriately. If it fails, the command is deleted
3094 immediately. Once `Submit` has been called, the passed command should
3095 not be deleted directly by the application.
3096
3097 storeIt indicates whether the successful command should be stored in
3098 the history list.
3099 """
3100 done = command.Do()
3101 if done and storeIt:
3102 self._commands.append(command)
3103 if self._maxCommands > -1:
3104 if len(self._commands) > self._maxCommands:
3105 del self._commands[0]
3106 return done
3107
3108
3109 def Redo(self):
3110 """
3111 Redoes the command just undone.
3112 """
3113 cmd = self._GetCurrentRedoCommand()
3114 if not cmd:
3115 return False
3116 done = cmd.Do()
3117 if done:
3118 self._commands.append(self._redoCommands.pop())
3119 return done
3120
3121
3122 def Undo(self):
3123 """
3124 Undoes the command just executed.
3125 """
3126 cmd = self._GetCurrentCommand()
3127 if not cmd:
3128 return False
3129 done = cmd.Undo()
3130 if done:
3131 self._redoCommands.append(self._commands.pop())
3132 return done
3133
3134