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