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