don't call neither OnSaveModified() nor Modify(false) from On{New,Open}Document(...
[wxWidgets.git] / src / common / docview.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/docview.cpp
3 // Purpose: Document/view classes
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_DOC_VIEW_ARCHITECTURE
28
29 #include "wx/docview.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/list.h"
33 #include "wx/string.h"
34 #include "wx/utils.h"
35 #include "wx/app.h"
36 #include "wx/dc.h"
37 #include "wx/dialog.h"
38 #include "wx/menu.h"
39 #include "wx/filedlg.h"
40 #include "wx/intl.h"
41 #include "wx/log.h"
42 #include "wx/msgdlg.h"
43 #include "wx/mdi.h"
44 #include "wx/choicdlg.h"
45 #endif
46
47 #if wxUSE_PRINTING_ARCHITECTURE
48 #include "wx/prntbase.h"
49 #include "wx/printdlg.h"
50 #endif
51
52 #include "wx/confbase.h"
53 #include "wx/filename.h"
54 #include "wx/file.h"
55 #include "wx/ffile.h"
56 #include "wx/cmdproc.h"
57 #include "wx/tokenzr.h"
58 #include "wx/filename.h"
59 #include "wx/stdpaths.h"
60 #include "wx/vector.h"
61 #include "wx/scopedarray.h"
62 #include "wx/scopedptr.h"
63
64 #if wxUSE_STD_IOSTREAM
65 #include "wx/ioswrap.h"
66 #include "wx/beforestd.h"
67 #if wxUSE_IOSTREAMH
68 #include <fstream.h>
69 #else
70 #include <fstream>
71 #endif
72 #include "wx/afterstd.h"
73 #else
74 #include "wx/wfstream.h"
75 #endif
76
77 typedef wxVector<wxDocTemplate *> wxDocTemplates;
78
79 // ----------------------------------------------------------------------------
80 // wxWidgets macros
81 // ----------------------------------------------------------------------------
82
83 IMPLEMENT_ABSTRACT_CLASS(wxDocument, wxEvtHandler)
84 IMPLEMENT_ABSTRACT_CLASS(wxView, wxEvtHandler)
85 IMPLEMENT_ABSTRACT_CLASS(wxDocTemplate, wxObject)
86 IMPLEMENT_DYNAMIC_CLASS(wxDocManager, wxEvtHandler)
87 IMPLEMENT_CLASS(wxDocChildFrame, wxFrame)
88 IMPLEMENT_CLASS(wxDocParentFrame, wxFrame)
89
90 #if wxUSE_PRINTING_ARCHITECTURE
91 IMPLEMENT_DYNAMIC_CLASS(wxDocPrintout, wxPrintout)
92 #endif
93
94 IMPLEMENT_DYNAMIC_CLASS(wxFileHistory, wxObject)
95
96 // ============================================================================
97 // implementation
98 // ============================================================================
99
100 // ----------------------------------------------------------------------------
101 // private helpers
102 // ----------------------------------------------------------------------------
103
104 namespace
105 {
106
107 wxWindow *wxFindSuitableParent()
108 {
109 wxWindow * const win = wxGetTopLevelParent(wxWindow::FindFocus());
110
111 return win ? win : wxTheApp->GetTopWindow();
112 }
113
114 wxString FindExtension(const wxString& path)
115 {
116 wxString ext;
117 wxFileName::SplitPath(path, NULL, NULL, &ext);
118
119 // VZ: extensions are considered not case sensitive - is this really a good
120 // idea?
121 return ext.MakeLower();
122 }
123
124 // return the string used for the MRU list items in the menu
125 //
126 // NB: the index n is 0-based, as usual, but the strings start from 1
127 wxString GetMRUEntryLabel(int n, const wxString& path)
128 {
129 // we need to quote '&' characters which are used for mnemonics
130 wxString pathInMenu(path);
131 pathInMenu.Replace("&", "&&");
132
133 return wxString::Format("&%d %s", n + 1, pathInMenu);
134 }
135
136 } // anonymous namespace
137
138 // ----------------------------------------------------------------------------
139 // Definition of wxDocument
140 // ----------------------------------------------------------------------------
141
142 wxDocument::wxDocument(wxDocument *parent)
143 {
144 m_documentModified = false;
145 m_documentParent = parent;
146 m_documentTemplate = NULL;
147 m_commandProcessor = NULL;
148 m_savedYet = false;
149 }
150
151 bool wxDocument::DeleteContents()
152 {
153 return true;
154 }
155
156 wxDocument::~wxDocument()
157 {
158 DeleteContents();
159
160 delete m_commandProcessor;
161
162 if (GetDocumentManager())
163 GetDocumentManager()->RemoveDocument(this);
164
165 // Not safe to do here, since it'll invoke virtual view functions
166 // expecting to see valid derived objects: and by the time we get here,
167 // we've called destructors higher up.
168 //DeleteAllViews();
169 }
170
171 bool wxDocument::Close()
172 {
173 if ( !OnSaveModified() )
174 return false;
175
176 return OnCloseDocument();
177 }
178
179 bool wxDocument::OnCloseDocument()
180 {
181 // Tell all views that we're about to close
182 NotifyClosing();
183 DeleteContents();
184 Modify(false);
185 return true;
186 }
187
188 // Note that this implicitly deletes the document when the last view is
189 // deleted.
190 bool wxDocument::DeleteAllViews()
191 {
192 wxDocManager* manager = GetDocumentManager();
193
194 // first check if all views agree to be closed
195 const wxList::iterator end = m_documentViews.end();
196 for ( wxList::iterator i = m_documentViews.begin(); i != end; ++i )
197 {
198 wxView *view = (wxView *)*i;
199 if ( !view->Close() )
200 return false;
201 }
202
203 // all views agreed to close, now do close them
204 if ( m_documentViews.empty() )
205 {
206 // normally the document would be implicitly deleted when the last view
207 // is, but if don't have any views, do it here instead
208 if ( manager && manager->GetDocuments().Member(this) )
209 delete this;
210 }
211 else // have views
212 {
213 // as we delete elements we iterate over, don't use the usual "from
214 // begin to end" loop
215 for ( ;; )
216 {
217 wxView *view = (wxView *)*m_documentViews.begin();
218
219 bool isLastOne = m_documentViews.size() == 1;
220
221 // this always deletes the node implicitly and if this is the last
222 // view also deletes this object itself (also implicitly, great),
223 // so we can't test for m_documentViews.empty() after calling this!
224 delete view;
225
226 if ( isLastOne )
227 break;
228 }
229 }
230
231 return true;
232 }
233
234 wxView *wxDocument::GetFirstView() const
235 {
236 if ( m_documentViews.empty() )
237 return NULL;
238
239 return static_cast<wxView *>(m_documentViews.GetFirst()->GetData());
240 }
241
242 wxDocManager *wxDocument::GetDocumentManager() const
243 {
244 return m_documentTemplate ? m_documentTemplate->GetDocumentManager() : NULL;
245 }
246
247 bool wxDocument::OnNewDocument()
248 {
249 // notice that there is no need to neither reset nor even check the
250 // modified flag here as the document itself is a new object (this is only
251 // called from CreateDocument()) and so it shouldn't be saved anyhow even
252 // if it is modified -- this could happen if the user code creates
253 // documents pre-filled with some user-entered (and which hence must not be
254 // lost) information
255
256 SetDocumentSaved(false);
257
258 const wxString name = GetDocumentManager()->MakeNewDocumentName();
259 SetTitle(name);
260 SetFilename(name, true);
261
262 return true;
263 }
264
265 bool wxDocument::Save()
266 {
267 if ( AlreadySaved() )
268 return true;
269
270 if ( m_documentFile.empty() || !m_savedYet )
271 return SaveAs();
272
273 return OnSaveDocument(m_documentFile);
274 }
275
276 bool wxDocument::SaveAs()
277 {
278 wxDocTemplate *docTemplate = GetDocumentTemplate();
279 if (!docTemplate)
280 return false;
281
282 #ifdef wxHAS_MULTIPLE_FILEDLG_FILTERS
283 wxString filter = docTemplate->GetDescription() + wxT(" (") +
284 docTemplate->GetFileFilter() + wxT(")|") +
285 docTemplate->GetFileFilter();
286
287 // Now see if there are some other template with identical view and document
288 // classes, whose filters may also be used.
289 if (docTemplate->GetViewClassInfo() && docTemplate->GetDocClassInfo())
290 {
291 wxList::compatibility_iterator
292 node = docTemplate->GetDocumentManager()->GetTemplates().GetFirst();
293 while (node)
294 {
295 wxDocTemplate *t = (wxDocTemplate*) node->GetData();
296
297 if (t->IsVisible() && t != docTemplate &&
298 t->GetViewClassInfo() == docTemplate->GetViewClassInfo() &&
299 t->GetDocClassInfo() == docTemplate->GetDocClassInfo())
300 {
301 // add a '|' to separate this filter from the previous one
302 if ( !filter.empty() )
303 filter << wxT('|');
304
305 filter << t->GetDescription()
306 << wxT(" (") << t->GetFileFilter() << wxT(") |")
307 << t->GetFileFilter();
308 }
309
310 node = node->GetNext();
311 }
312 }
313 #else
314 wxString filter = docTemplate->GetFileFilter() ;
315 #endif
316
317 wxString defaultDir = docTemplate->GetDirectory();
318 if ( defaultDir.empty() )
319 {
320 defaultDir = wxPathOnly(GetFilename());
321 if ( defaultDir.empty() )
322 defaultDir = GetDocumentManager()->GetLastDirectory();
323 }
324
325 wxString fileName = wxFileSelector(_("Save As"),
326 defaultDir,
327 wxFileNameFromPath(GetFilename()),
328 docTemplate->GetDefaultExtension(),
329 filter,
330 wxFD_SAVE | wxFD_OVERWRITE_PROMPT,
331 GetDocumentWindow());
332
333 if (fileName.empty())
334 return false; // cancelled by user
335
336 wxString ext;
337 wxFileName::SplitPath(fileName, NULL, NULL, &ext);
338
339 if (ext.empty())
340 {
341 fileName += wxT(".");
342 fileName += docTemplate->GetDefaultExtension();
343 }
344
345 // Files that were not saved correctly are not added to the FileHistory.
346 if (!OnSaveDocument(fileName))
347 return false;
348
349 SetTitle(wxFileNameFromPath(fileName));
350 SetFilename(fileName, true); // will call OnChangeFileName automatically
351
352 // A file that doesn't use the default extension of its document template
353 // cannot be opened via the FileHistory, so we do not add it.
354 if (docTemplate->FileMatchesTemplate(fileName))
355 {
356 GetDocumentManager()->AddFileToHistory(fileName);
357 }
358 //else: the user will probably not be able to open the file again, so we
359 // could warn about the wrong file-extension here
360
361 return true;
362 }
363
364 bool wxDocument::OnSaveDocument(const wxString& file)
365 {
366 if ( !file )
367 return false;
368
369 if ( !DoSaveDocument(file) )
370 return false;
371
372 Modify(false);
373 SetFilename(file);
374 SetDocumentSaved(true);
375 #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
376 wxFileName fn(file) ;
377 fn.MacSetDefaultTypeAndCreator() ;
378 #endif
379 return true;
380 }
381
382 bool wxDocument::OnOpenDocument(const wxString& file)
383 {
384 // notice that there is no need to check the modified flag here for the
385 // reasons explained in OnNewDocument()
386
387 if ( !DoOpenDocument(file) )
388 return false;
389
390 SetFilename(file, true);
391
392 // stretching the logic a little this does make sense because the document
393 // had been saved into the file we just loaded it from, it just could have
394 // happened during a previous program execution, it's just that the name of
395 // this method is a bit unfortunate, it should probably have been called
396 // HasAssociatedFileName()
397 SetDocumentSaved(true);
398
399 UpdateAllViews();
400
401 return true;
402 }
403
404 #if wxUSE_STD_IOSTREAM
405 wxSTD istream& wxDocument::LoadObject(wxSTD istream& stream)
406 #else
407 wxInputStream& wxDocument::LoadObject(wxInputStream& stream)
408 #endif
409 {
410 return stream;
411 }
412
413 #if wxUSE_STD_IOSTREAM
414 wxSTD ostream& wxDocument::SaveObject(wxSTD ostream& stream)
415 #else
416 wxOutputStream& wxDocument::SaveObject(wxOutputStream& stream)
417 #endif
418 {
419 return stream;
420 }
421
422 bool wxDocument::Revert()
423 {
424 return false;
425 }
426
427
428 // Get title, or filename if no title, else unnamed
429 #if WXWIN_COMPATIBILITY_2_8
430 bool wxDocument::GetPrintableName(wxString& buf) const
431 {
432 // this function can not only be overridden by the user code but also
433 // called by it so we need to ensure that we return the same thing as
434 // GetUserReadableName() but we can't call it because this would result in
435 // an infinite recursion, hence we use the helper DoGetUserReadableName()
436 buf = DoGetUserReadableName();
437
438 return true;
439 }
440 #endif // WXWIN_COMPATIBILITY_2_8
441
442 wxString wxDocument::GetUserReadableName() const
443 {
444 #if WXWIN_COMPATIBILITY_2_8
445 // we need to call the old virtual function to ensure that the overridden
446 // version of it is still called
447 wxString name;
448 if ( GetPrintableName(name) )
449 return name;
450 #endif // WXWIN_COMPATIBILITY_2_8
451
452 return DoGetUserReadableName();
453 }
454
455 wxString wxDocument::DoGetUserReadableName() const
456 {
457 if ( !m_documentTitle.empty() )
458 return m_documentTitle;
459
460 if ( !m_documentFile.empty() )
461 return wxFileNameFromPath(m_documentFile);
462
463 return _("unnamed");
464 }
465
466 wxWindow *wxDocument::GetDocumentWindow() const
467 {
468 wxView * const view = GetFirstView();
469
470 return view ? view->GetFrame() : wxTheApp->GetTopWindow();
471 }
472
473 wxCommandProcessor *wxDocument::OnCreateCommandProcessor()
474 {
475 return new wxCommandProcessor;
476 }
477
478 // true if safe to close
479 bool wxDocument::OnSaveModified()
480 {
481 if ( IsModified() )
482 {
483 switch ( wxMessageBox
484 (
485 wxString::Format
486 (
487 _("Do you want to save changes to %s?"),
488 GetUserReadableName()
489 ),
490 wxTheApp->GetAppDisplayName(),
491 wxYES_NO | wxCANCEL | wxICON_QUESTION | wxCENTRE,
492 wxFindSuitableParent()
493 ) )
494 {
495 case wxNO:
496 Modify(false);
497 break;
498
499 case wxYES:
500 return Save();
501
502 case wxCANCEL:
503 return false;
504 }
505 }
506
507 return true;
508 }
509
510 bool wxDocument::Draw(wxDC& WXUNUSED(context))
511 {
512 return true;
513 }
514
515 bool wxDocument::AddView(wxView *view)
516 {
517 if ( !m_documentViews.Member(view) )
518 {
519 m_documentViews.Append(view);
520 OnChangedViewList();
521 }
522 return true;
523 }
524
525 bool wxDocument::RemoveView(wxView *view)
526 {
527 (void)m_documentViews.DeleteObject(view);
528 OnChangedViewList();
529 return true;
530 }
531
532 bool wxDocument::OnCreate(const wxString& WXUNUSED(path), long flags)
533 {
534 return GetDocumentTemplate()->CreateView(this, flags) != NULL;
535 }
536
537 // Called after a view is added or removed.
538 // The default implementation deletes the document if
539 // there are no more views.
540 void wxDocument::OnChangedViewList()
541 {
542 if ( m_documentViews.empty() && OnSaveModified() )
543 delete this;
544 }
545
546 void wxDocument::UpdateAllViews(wxView *sender, wxObject *hint)
547 {
548 wxList::compatibility_iterator node = m_documentViews.GetFirst();
549 while (node)
550 {
551 wxView *view = (wxView *)node->GetData();
552 if (view != sender)
553 view->OnUpdate(sender, hint);
554 node = node->GetNext();
555 }
556 }
557
558 void wxDocument::NotifyClosing()
559 {
560 wxList::compatibility_iterator node = m_documentViews.GetFirst();
561 while (node)
562 {
563 wxView *view = (wxView *)node->GetData();
564 view->OnClosingDocument();
565 node = node->GetNext();
566 }
567 }
568
569 void wxDocument::SetFilename(const wxString& filename, bool notifyViews)
570 {
571 m_documentFile = filename;
572 OnChangeFilename(notifyViews);
573 }
574
575 void wxDocument::OnChangeFilename(bool notifyViews)
576 {
577 if ( notifyViews )
578 {
579 // Notify the views that the filename has changed
580 wxList::compatibility_iterator node = m_documentViews.GetFirst();
581 while (node)
582 {
583 wxView *view = (wxView *)node->GetData();
584 view->OnChangeFilename();
585 node = node->GetNext();
586 }
587 }
588 }
589
590 bool wxDocument::DoSaveDocument(const wxString& file)
591 {
592 #if wxUSE_STD_IOSTREAM
593 wxSTD ofstream store(file.mb_str(), wxSTD ios::binary);
594 if ( !store )
595 #else
596 wxFileOutputStream store(file);
597 if ( store.GetLastError() != wxSTREAM_NO_ERROR )
598 #endif
599 {
600 wxLogError(_("File \"%s\" could not be opened for writing."), file);
601 return false;
602 }
603
604 if (!SaveObject(store))
605 {
606 wxLogError(_("Failed to save document to the file \"%s\"."), file);
607 return false;
608 }
609
610 return true;
611 }
612
613 bool wxDocument::DoOpenDocument(const wxString& file)
614 {
615 #if wxUSE_STD_IOSTREAM
616 wxSTD ifstream store(file.mb_str(), wxSTD ios::binary);
617 if ( !store )
618 #else
619 wxFileInputStream store(file);
620 if (store.GetLastError() != wxSTREAM_NO_ERROR || !store.IsOk())
621 #endif
622 {
623 wxLogError(_("File \"%s\" could not be opened for reading."), file);
624 return false;
625 }
626
627 #if wxUSE_STD_IOSTREAM
628 LoadObject(store);
629 if ( !store )
630 #else
631 int res = LoadObject(store).GetLastError();
632 if ( res != wxSTREAM_NO_ERROR && res != wxSTREAM_EOF )
633 #endif
634 {
635 wxLogError(_("Failed to read document from the file \"%s\"."), file);
636 return false;
637 }
638
639 return true;
640 }
641
642
643 // ----------------------------------------------------------------------------
644 // Document view
645 // ----------------------------------------------------------------------------
646
647 wxView::wxView()
648 {
649 m_viewDocument = NULL;
650
651 m_viewFrame = NULL;
652
653 m_docChildFrame = NULL;
654 }
655
656 wxView::~wxView()
657 {
658 GetDocumentManager()->ActivateView(this, false);
659
660 // reset our frame view first, before removing it from the document as
661 // SetView(NULL) is a simple call while RemoveView() may result in user
662 // code being executed and this user code can, for example, show a message
663 // box which would result in an activation event for m_docChildFrame and so
664 // could reactivate the view being destroyed -- unless we reset it first
665 if ( m_docChildFrame && m_docChildFrame->GetView() == this )
666 m_docChildFrame->SetView(NULL);
667
668 if ( m_viewDocument )
669 m_viewDocument->RemoveView(this);
670 }
671
672 bool wxView::TryBefore(wxEvent& event)
673 {
674 wxDocument * const doc = GetDocument();
675 return doc && doc->ProcessEventHere(event);
676 }
677
678 void wxView::OnActivateView(bool WXUNUSED(activate),
679 wxView *WXUNUSED(activeView),
680 wxView *WXUNUSED(deactiveView))
681 {
682 }
683
684 void wxView::OnPrint(wxDC *dc, wxObject *WXUNUSED(info))
685 {
686 OnDraw(dc);
687 }
688
689 void wxView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint))
690 {
691 }
692
693 void wxView::OnChangeFilename()
694 {
695 // GetFrame can return wxWindow rather than wxTopLevelWindow due to
696 // generic MDI implementation so use SetLabel rather than SetTitle.
697 // It should cause SetTitle() for top level windows.
698 wxWindow *win = GetFrame();
699 if (!win) return;
700
701 wxDocument *doc = GetDocument();
702 if (!doc) return;
703
704 win->SetLabel(doc->GetUserReadableName());
705 }
706
707 void wxView::SetDocument(wxDocument *doc)
708 {
709 m_viewDocument = doc;
710 if (doc)
711 doc->AddView(this);
712 }
713
714 bool wxView::Close(bool deleteWindow)
715 {
716 return OnClose(deleteWindow);
717 }
718
719 void wxView::Activate(bool activate)
720 {
721 if (GetDocument() && GetDocumentManager())
722 {
723 OnActivateView(activate, this, GetDocumentManager()->GetCurrentView());
724 GetDocumentManager()->ActivateView(this, activate);
725 }
726 }
727
728 bool wxView::OnClose(bool WXUNUSED(deleteWindow))
729 {
730 return GetDocument() ? GetDocument()->Close() : true;
731 }
732
733 #if wxUSE_PRINTING_ARCHITECTURE
734 wxPrintout *wxView::OnCreatePrintout()
735 {
736 return new wxDocPrintout(this);
737 }
738 #endif // wxUSE_PRINTING_ARCHITECTURE
739
740 // ----------------------------------------------------------------------------
741 // wxDocTemplate
742 // ----------------------------------------------------------------------------
743
744 wxDocTemplate::wxDocTemplate(wxDocManager *manager,
745 const wxString& descr,
746 const wxString& filter,
747 const wxString& dir,
748 const wxString& ext,
749 const wxString& docTypeName,
750 const wxString& viewTypeName,
751 wxClassInfo *docClassInfo,
752 wxClassInfo *viewClassInfo,
753 long flags)
754 {
755 m_documentManager = manager;
756 m_description = descr;
757 m_directory = dir;
758 m_defaultExt = ext;
759 m_fileFilter = filter;
760 m_flags = flags;
761 m_docTypeName = docTypeName;
762 m_viewTypeName = viewTypeName;
763 m_documentManager->AssociateTemplate(this);
764
765 m_docClassInfo = docClassInfo;
766 m_viewClassInfo = viewClassInfo;
767 }
768
769 wxDocTemplate::~wxDocTemplate()
770 {
771 m_documentManager->DisassociateTemplate(this);
772 }
773
774 // Tries to dynamically construct an object of the right class.
775 wxDocument *wxDocTemplate::CreateDocument(const wxString& path, long flags)
776 {
777 wxDocument * const doc = DoCreateDocument();
778
779 // VZ: this code doesn't delete doc if InitDocument() (i.e. doc->OnCreate())
780 // fails, is this intentional?
781
782 return doc && InitDocument(doc, path, flags) ? doc : NULL;
783 }
784
785 bool
786 wxDocTemplate::InitDocument(wxDocument* doc, const wxString& path, long flags)
787 {
788 doc->SetFilename(path);
789 doc->SetDocumentTemplate(this);
790 GetDocumentManager()->AddDocument(doc);
791 doc->SetCommandProcessor(doc->OnCreateCommandProcessor());
792
793 if (doc->OnCreate(path, flags))
794 return true;
795
796 if (GetDocumentManager()->GetDocuments().Member(doc))
797 doc->DeleteAllViews();
798 return false;
799 }
800
801 wxView *wxDocTemplate::CreateView(wxDocument *doc, long flags)
802 {
803 wxScopedPtr<wxView> view(DoCreateView());
804 if ( !view )
805 return NULL;
806
807 view->SetDocument(doc);
808 if ( !view->OnCreate(doc, flags) )
809 return NULL;
810
811 return view.release();
812 }
813
814 // The default (very primitive) format detection: check is the extension is
815 // that of the template
816 bool wxDocTemplate::FileMatchesTemplate(const wxString& path)
817 {
818 wxStringTokenizer parser (GetFileFilter(), wxT(";"));
819 wxString anything = wxT ("*");
820 while (parser.HasMoreTokens())
821 {
822 wxString filter = parser.GetNextToken();
823 wxString filterExt = FindExtension (filter);
824 if ( filter.IsSameAs (anything) ||
825 filterExt.IsSameAs (anything) ||
826 filterExt.IsSameAs (FindExtension (path)) )
827 return true;
828 }
829 return GetDefaultExtension().IsSameAs(FindExtension(path));
830 }
831
832 wxDocument *wxDocTemplate::DoCreateDocument()
833 {
834 if (!m_docClassInfo)
835 return NULL;
836
837 return static_cast<wxDocument *>(m_docClassInfo->CreateObject());
838 }
839
840 wxView *wxDocTemplate::DoCreateView()
841 {
842 if (!m_viewClassInfo)
843 return NULL;
844
845 return static_cast<wxView *>(m_viewClassInfo->CreateObject());
846 }
847
848 // ----------------------------------------------------------------------------
849 // wxDocManager
850 // ----------------------------------------------------------------------------
851
852 BEGIN_EVENT_TABLE(wxDocManager, wxEvtHandler)
853 EVT_MENU(wxID_OPEN, wxDocManager::OnFileOpen)
854 EVT_MENU(wxID_CLOSE, wxDocManager::OnFileClose)
855 EVT_MENU(wxID_CLOSE_ALL, wxDocManager::OnFileCloseAll)
856 EVT_MENU(wxID_REVERT, wxDocManager::OnFileRevert)
857 EVT_MENU(wxID_NEW, wxDocManager::OnFileNew)
858 EVT_MENU(wxID_SAVE, wxDocManager::OnFileSave)
859 EVT_MENU(wxID_SAVEAS, wxDocManager::OnFileSaveAs)
860 EVT_MENU(wxID_UNDO, wxDocManager::OnUndo)
861 EVT_MENU(wxID_REDO, wxDocManager::OnRedo)
862
863 EVT_UPDATE_UI(wxID_OPEN, wxDocManager::OnUpdateFileOpen)
864 EVT_UPDATE_UI(wxID_CLOSE, wxDocManager::OnUpdateDisableIfNoDoc)
865 EVT_UPDATE_UI(wxID_CLOSE_ALL, wxDocManager::OnUpdateDisableIfNoDoc)
866 EVT_UPDATE_UI(wxID_REVERT, wxDocManager::OnUpdateDisableIfNoDoc)
867 EVT_UPDATE_UI(wxID_NEW, wxDocManager::OnUpdateFileNew)
868 EVT_UPDATE_UI(wxID_SAVE, wxDocManager::OnUpdateFileSave)
869 EVT_UPDATE_UI(wxID_SAVEAS, wxDocManager::OnUpdateDisableIfNoDoc)
870 EVT_UPDATE_UI(wxID_UNDO, wxDocManager::OnUpdateUndo)
871 EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo)
872
873 #if wxUSE_PRINTING_ARCHITECTURE
874 EVT_MENU(wxID_PRINT, wxDocManager::OnPrint)
875 EVT_MENU(wxID_PREVIEW, wxDocManager::OnPreview)
876
877 EVT_UPDATE_UI(wxID_PRINT, wxDocManager::OnUpdateDisableIfNoDoc)
878 EVT_UPDATE_UI(wxID_PREVIEW, wxDocManager::OnUpdateDisableIfNoDoc)
879 #endif
880 END_EVENT_TABLE()
881
882 wxDocManager* wxDocManager::sm_docManager = NULL;
883
884 wxDocManager::wxDocManager(long WXUNUSED(flags), bool initialize)
885 {
886 wxASSERT_MSG( !sm_docManager, "multiple wxDocManagers not allowed" );
887
888 sm_docManager = this;
889
890 m_defaultDocumentNameCounter = 1;
891 m_currentView = NULL;
892 m_maxDocsOpen = INT_MAX;
893 m_fileHistory = NULL;
894 if ( initialize )
895 Initialize();
896 }
897
898 wxDocManager::~wxDocManager()
899 {
900 Clear();
901 delete m_fileHistory;
902 sm_docManager = NULL;
903 }
904
905 // closes the specified document
906 bool wxDocManager::CloseDocument(wxDocument* doc, bool force)
907 {
908 if ( !doc->Close() && !force )
909 return false;
910
911 // Implicitly deletes the document when
912 // the last view is deleted
913 doc->DeleteAllViews();
914
915 // Check we're really deleted
916 if (m_docs.Member(doc))
917 delete doc;
918
919 return true;
920 }
921
922 bool wxDocManager::CloseDocuments(bool force)
923 {
924 wxList::compatibility_iterator node = m_docs.GetFirst();
925 while (node)
926 {
927 wxDocument *doc = (wxDocument *)node->GetData();
928 wxList::compatibility_iterator next = node->GetNext();
929
930 if (!CloseDocument(doc, force))
931 return false;
932
933 // This assumes that documents are not connected in
934 // any way, i.e. deleting one document does NOT
935 // delete another.
936 node = next;
937 }
938 return true;
939 }
940
941 bool wxDocManager::Clear(bool force)
942 {
943 if (!CloseDocuments(force))
944 return false;
945
946 m_currentView = NULL;
947
948 wxList::compatibility_iterator node = m_templates.GetFirst();
949 while (node)
950 {
951 wxDocTemplate *templ = (wxDocTemplate*) node->GetData();
952 wxList::compatibility_iterator next = node->GetNext();
953 delete templ;
954 node = next;
955 }
956 return true;
957 }
958
959 bool wxDocManager::Initialize()
960 {
961 m_fileHistory = OnCreateFileHistory();
962 return true;
963 }
964
965 wxString wxDocManager::GetLastDirectory() const
966 {
967 // use the system-dependent default location for the document files if
968 // we're being opened for the first time
969 if ( m_lastDirectory.empty() )
970 {
971 wxDocManager * const self = const_cast<wxDocManager *>(this);
972 self->m_lastDirectory = wxStandardPaths::Get().GetAppDocumentsDir();
973 }
974
975 return m_lastDirectory;
976 }
977
978 wxFileHistory *wxDocManager::OnCreateFileHistory()
979 {
980 return new wxFileHistory;
981 }
982
983 void wxDocManager::OnFileClose(wxCommandEvent& WXUNUSED(event))
984 {
985 wxDocument *doc = GetCurrentDocument();
986 if (!doc)
987 return;
988 if (doc->Close())
989 {
990 doc->DeleteAllViews();
991 if (m_docs.Member(doc))
992 delete doc;
993 }
994 }
995
996 void wxDocManager::OnFileCloseAll(wxCommandEvent& WXUNUSED(event))
997 {
998 CloseDocuments(false);
999 }
1000
1001 void wxDocManager::OnFileNew(wxCommandEvent& WXUNUSED(event))
1002 {
1003 CreateNewDocument();
1004 }
1005
1006 void wxDocManager::OnFileOpen(wxCommandEvent& WXUNUSED(event))
1007 {
1008 if ( !CreateDocument("") )
1009 {
1010 OnOpenFileFailure();
1011 }
1012 }
1013
1014 void wxDocManager::OnFileRevert(wxCommandEvent& WXUNUSED(event))
1015 {
1016 wxDocument *doc = GetCurrentDocument();
1017 if (!doc)
1018 return;
1019 doc->Revert();
1020 }
1021
1022 void wxDocManager::OnFileSave(wxCommandEvent& WXUNUSED(event))
1023 {
1024 wxDocument *doc = GetCurrentDocument();
1025 if (!doc)
1026 return;
1027 doc->Save();
1028 }
1029
1030 void wxDocManager::OnFileSaveAs(wxCommandEvent& WXUNUSED(event))
1031 {
1032 wxDocument *doc = GetCurrentDocument();
1033 if (!doc)
1034 return;
1035 doc->SaveAs();
1036 }
1037
1038 void wxDocManager::OnPrint(wxCommandEvent& WXUNUSED(event))
1039 {
1040 #if wxUSE_PRINTING_ARCHITECTURE
1041 wxView *view = GetActiveView();
1042 if (!view)
1043 return;
1044
1045 wxPrintout *printout = view->OnCreatePrintout();
1046 if (printout)
1047 {
1048 wxPrinter printer;
1049 printer.Print(view->GetFrame(), printout, true);
1050
1051 delete printout;
1052 }
1053 #endif // wxUSE_PRINTING_ARCHITECTURE
1054 }
1055
1056 void wxDocManager::OnPreview(wxCommandEvent& WXUNUSED(event))
1057 {
1058 #if wxUSE_PRINTING_ARCHITECTURE
1059 wxView *view = GetActiveView();
1060 if (!view)
1061 return;
1062
1063 wxPrintout *printout = view->OnCreatePrintout();
1064 if (printout)
1065 {
1066 // Pass two printout objects: for preview, and possible printing.
1067 wxPrintPreviewBase *
1068 preview = new wxPrintPreview(printout, view->OnCreatePrintout());
1069 if ( !preview->Ok() )
1070 {
1071 delete preview;
1072 wxLogError(_("Print preview creation failed."));
1073 return;
1074 }
1075
1076 wxPreviewFrame *
1077 frame = new wxPreviewFrame(preview, wxTheApp->GetTopWindow(),
1078 _("Print Preview"));
1079 frame->Centre(wxBOTH);
1080 frame->Initialize();
1081 frame->Show(true);
1082 }
1083 #endif // wxUSE_PRINTING_ARCHITECTURE
1084 }
1085
1086 void wxDocManager::OnUndo(wxCommandEvent& event)
1087 {
1088 wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor();
1089 if ( !cmdproc )
1090 {
1091 event.Skip();
1092 return;
1093 }
1094
1095 cmdproc->Undo();
1096 }
1097
1098 void wxDocManager::OnRedo(wxCommandEvent& event)
1099 {
1100 wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor();
1101 if ( !cmdproc )
1102 {
1103 event.Skip();
1104 return;
1105 }
1106
1107 cmdproc->Redo();
1108 }
1109
1110 // Handlers for UI update commands
1111
1112 void wxDocManager::OnUpdateFileOpen(wxUpdateUIEvent& event)
1113 {
1114 // CreateDocument() (which is called from OnFileOpen) may succeed
1115 // only when there is at least a template:
1116 event.Enable( GetTemplates().GetCount()>0 );
1117 }
1118
1119 void wxDocManager::OnUpdateDisableIfNoDoc(wxUpdateUIEvent& event)
1120 {
1121 event.Enable( GetCurrentDocument() != NULL );
1122 }
1123
1124 void wxDocManager::OnUpdateFileNew(wxUpdateUIEvent& event)
1125 {
1126 // CreateDocument() (which is called from OnFileNew) may succeed
1127 // only when there is at least a template:
1128 event.Enable( GetTemplates().GetCount()>0 );
1129 }
1130
1131 void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event)
1132 {
1133 wxDocument * const doc = GetCurrentDocument();
1134 event.Enable( doc && !doc->AlreadySaved() );
1135 }
1136
1137 void wxDocManager::OnUpdateUndo(wxUpdateUIEvent& event)
1138 {
1139 wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor();
1140 if ( !cmdproc )
1141 {
1142 event.Enable(false);
1143 return;
1144 }
1145
1146 event.Enable(cmdproc->CanUndo());
1147 cmdproc->SetMenuStrings();
1148 }
1149
1150 void wxDocManager::OnUpdateRedo(wxUpdateUIEvent& event)
1151 {
1152 wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor();
1153 if ( !cmdproc )
1154 {
1155 event.Enable(false);
1156 return;
1157 }
1158
1159 event.Enable(cmdproc->CanRedo());
1160 cmdproc->SetMenuStrings();
1161 }
1162
1163 wxView *wxDocManager::GetActiveView() const
1164 {
1165 wxView *view = GetCurrentView();
1166
1167 if ( !view && !m_docs.empty() )
1168 {
1169 // if we have exactly one document, consider its view to be the current
1170 // one
1171 //
1172 // VZ: I'm not exactly sure why is this needed but this is how this
1173 // code used to behave before the bug #9518 was fixed and it seems
1174 // safer to preserve the old logic
1175 wxList::compatibility_iterator node = m_docs.GetFirst();
1176 if ( !node->GetNext() )
1177 {
1178 wxDocument *doc = static_cast<wxDocument *>(node->GetData());
1179 view = doc->GetFirstView();
1180 }
1181 //else: we have more than one document
1182 }
1183
1184 return view;
1185 }
1186
1187 bool wxDocManager::TryBefore(wxEvent& event)
1188 {
1189 wxView * const view = GetActiveView();
1190 return view && view->ProcessEventHere(event);
1191 }
1192
1193 namespace
1194 {
1195
1196 // helper function: return only the visible templates
1197 wxDocTemplates GetVisibleTemplates(const wxList& allTemplates)
1198 {
1199 // select only the visible templates
1200 const size_t totalNumTemplates = allTemplates.GetCount();
1201 wxDocTemplates templates;
1202 if ( totalNumTemplates )
1203 {
1204 templates.reserve(totalNumTemplates);
1205
1206 for ( wxList::const_iterator i = allTemplates.begin(),
1207 end = allTemplates.end();
1208 i != end;
1209 ++i )
1210 {
1211 wxDocTemplate * const temp = (wxDocTemplate *)*i;
1212 if ( temp->IsVisible() )
1213 templates.push_back(temp);
1214 }
1215 }
1216
1217 return templates;
1218 }
1219
1220 } // anonymous namespace
1221
1222 wxDocument *wxDocManager::CreateDocument(const wxString& pathOrig, long flags)
1223 {
1224 // this ought to be const but SelectDocumentType/Path() are not
1225 // const-correct and can't be changed as, being virtual, this risks
1226 // breaking user code overriding them
1227 wxDocTemplates templates(GetVisibleTemplates(m_templates));
1228 const size_t numTemplates = templates.size();
1229 if ( !numTemplates )
1230 {
1231 // no templates can be used, can't create document
1232 return NULL;
1233 }
1234
1235
1236 // normally user should select the template to use but wxDOC_SILENT flag we
1237 // choose one ourselves
1238 wxString path = pathOrig; // may be modified below
1239 wxDocTemplate *temp;
1240 if ( flags & wxDOC_SILENT )
1241 {
1242 wxASSERT_MSG( !path.empty(),
1243 "using empty path with wxDOC_SILENT doesn't make sense" );
1244
1245 temp = FindTemplateForPath(path);
1246 if ( !temp )
1247 {
1248 wxLogWarning(_("The format of file '%s' couldn't be determined."),
1249 path);
1250 }
1251 }
1252 else // not silent, ask the user
1253 {
1254 // for the new file we need just the template, for an existing one we
1255 // need the template and the path, unless it's already specified
1256 if ( (flags & wxDOC_NEW) || !path.empty() )
1257 temp = SelectDocumentType(&templates[0], numTemplates);
1258 else
1259 temp = SelectDocumentPath(&templates[0], numTemplates, path, flags);
1260 }
1261
1262 if ( !temp )
1263 return NULL;
1264
1265 // check whether the document with this path is already opened
1266 if ( !path.empty() )
1267 {
1268 const wxFileName fn(path);
1269 for ( wxList::const_iterator i = m_docs.begin(); i != m_docs.end(); ++i )
1270 {
1271 wxDocument * const doc = (wxDocument*)*i;
1272
1273 if ( fn == doc->GetFilename() )
1274 {
1275 // file already open, just activate it and return
1276 if ( doc->GetFirstView() )
1277 {
1278 ActivateView(doc->GetFirstView());
1279 if ( doc->GetDocumentWindow() )
1280 doc->GetDocumentWindow()->SetFocus();
1281 return doc;
1282 }
1283 }
1284 }
1285 }
1286
1287
1288 // no, we need to create a new document
1289
1290
1291 // if we've reached the max number of docs, close the first one.
1292 if ( (int)GetDocuments().GetCount() >= m_maxDocsOpen )
1293 {
1294 if ( !CloseDocument((wxDocument *)GetDocuments().GetFirst()->GetData()) )
1295 {
1296 // can't open the new document if closing the old one failed
1297 return NULL;
1298 }
1299 }
1300
1301
1302 // do create and initialize the new document finally
1303 wxDocument * const docNew = temp->CreateDocument(path, flags);
1304 if ( !docNew )
1305 return NULL;
1306
1307 docNew->SetDocumentName(temp->GetDocumentName());
1308 docNew->SetDocumentTemplate(temp);
1309
1310 // call the appropriate function depending on whether we're creating a new
1311 // file or opening an existing one
1312 if ( !(flags & wxDOC_NEW ? docNew->OnNewDocument()
1313 : docNew->OnOpenDocument(path)) )
1314 {
1315 // Document is implicitly deleted by DeleteAllViews
1316 docNew->DeleteAllViews();
1317 return NULL;
1318 }
1319
1320 // add the successfully opened file to MRU, but only if we're going to be
1321 // able to reopen it successfully later which requires the template for
1322 // this document to be retrievable from the file extension
1323 if ( !(flags & wxDOC_NEW) && temp->FileMatchesTemplate(path) )
1324 AddFileToHistory(path);
1325
1326 return docNew;
1327 }
1328
1329 wxView *wxDocManager::CreateView(wxDocument *doc, long flags)
1330 {
1331 wxDocTemplates templates(GetVisibleTemplates(m_templates));
1332 const size_t numTemplates = templates.size();
1333
1334 if ( numTemplates == 0 )
1335 return NULL;
1336
1337 wxDocTemplate * const
1338 temp = numTemplates == 1 ? templates[0]
1339 : SelectViewType(&templates[0], numTemplates);
1340
1341 if ( !temp )
1342 return NULL;
1343
1344 wxView *view = temp->CreateView(doc, flags);
1345 if ( view )
1346 view->SetViewName(temp->GetViewName());
1347 return view;
1348 }
1349
1350 // Not yet implemented
1351 void
1352 wxDocManager::DeleteTemplate(wxDocTemplate *WXUNUSED(temp), long WXUNUSED(flags))
1353 {
1354 }
1355
1356 // Not yet implemented
1357 bool wxDocManager::FlushDoc(wxDocument *WXUNUSED(doc))
1358 {
1359 return false;
1360 }
1361
1362 wxDocument *wxDocManager::GetCurrentDocument() const
1363 {
1364 wxView * const view = GetActiveView();
1365 return view ? view->GetDocument() : NULL;
1366 }
1367
1368 wxCommandProcessor *wxDocManager::GetCurrentCommandProcessor() const
1369 {
1370 wxDocument * const doc = GetCurrentDocument();
1371 return doc ? doc->GetCommandProcessor() : NULL;
1372 }
1373
1374 // Make a default name for a new document
1375 #if WXWIN_COMPATIBILITY_2_8
1376 bool wxDocManager::MakeDefaultName(wxString& WXUNUSED(name))
1377 {
1378 // we consider that this function can only be overridden by the user code,
1379 // not called by it as it only makes sense to call it internally, so we
1380 // don't bother to return anything from here
1381 return false;
1382 }
1383 #endif // WXWIN_COMPATIBILITY_2_8
1384
1385 wxString wxDocManager::MakeNewDocumentName()
1386 {
1387 wxString name;
1388
1389 #if WXWIN_COMPATIBILITY_2_8
1390 if ( !MakeDefaultName(name) )
1391 #endif // WXWIN_COMPATIBILITY_2_8
1392 {
1393 name.Printf(_("unnamed%d"), m_defaultDocumentNameCounter);
1394 m_defaultDocumentNameCounter++;
1395 }
1396
1397 return name;
1398 }
1399
1400 // Make a frame title (override this to do something different)
1401 // If docName is empty, a document is not currently active.
1402 wxString wxDocManager::MakeFrameTitle(wxDocument* doc)
1403 {
1404 wxString appName = wxTheApp->GetAppDisplayName();
1405 wxString title;
1406 if (!doc)
1407 title = appName;
1408 else
1409 {
1410 wxString docName = doc->GetUserReadableName();
1411 title = docName + wxString(_(" - ")) + appName;
1412 }
1413 return title;
1414 }
1415
1416
1417 // Not yet implemented
1418 wxDocTemplate *wxDocManager::MatchTemplate(const wxString& WXUNUSED(path))
1419 {
1420 return NULL;
1421 }
1422
1423 // File history management
1424 void wxDocManager::AddFileToHistory(const wxString& file)
1425 {
1426 if (m_fileHistory)
1427 m_fileHistory->AddFileToHistory(file);
1428 }
1429
1430 void wxDocManager::RemoveFileFromHistory(size_t i)
1431 {
1432 if (m_fileHistory)
1433 m_fileHistory->RemoveFileFromHistory(i);
1434 }
1435
1436 wxString wxDocManager::GetHistoryFile(size_t i) const
1437 {
1438 wxString histFile;
1439
1440 if (m_fileHistory)
1441 histFile = m_fileHistory->GetHistoryFile(i);
1442
1443 return histFile;
1444 }
1445
1446 void wxDocManager::FileHistoryUseMenu(wxMenu *menu)
1447 {
1448 if (m_fileHistory)
1449 m_fileHistory->UseMenu(menu);
1450 }
1451
1452 void wxDocManager::FileHistoryRemoveMenu(wxMenu *menu)
1453 {
1454 if (m_fileHistory)
1455 m_fileHistory->RemoveMenu(menu);
1456 }
1457
1458 #if wxUSE_CONFIG
1459 void wxDocManager::FileHistoryLoad(const wxConfigBase& config)
1460 {
1461 if (m_fileHistory)
1462 m_fileHistory->Load(config);
1463 }
1464
1465 void wxDocManager::FileHistorySave(wxConfigBase& config)
1466 {
1467 if (m_fileHistory)
1468 m_fileHistory->Save(config);
1469 }
1470 #endif
1471
1472 void wxDocManager::FileHistoryAddFilesToMenu(wxMenu* menu)
1473 {
1474 if (m_fileHistory)
1475 m_fileHistory->AddFilesToMenu(menu);
1476 }
1477
1478 void wxDocManager::FileHistoryAddFilesToMenu()
1479 {
1480 if (m_fileHistory)
1481 m_fileHistory->AddFilesToMenu();
1482 }
1483
1484 size_t wxDocManager::GetHistoryFilesCount() const
1485 {
1486 return m_fileHistory ? m_fileHistory->GetCount() : 0;
1487 }
1488
1489
1490 // Find out the document template via matching in the document file format
1491 // against that of the template
1492 wxDocTemplate *wxDocManager::FindTemplateForPath(const wxString& path)
1493 {
1494 wxDocTemplate *theTemplate = NULL;
1495
1496 // Find the template which this extension corresponds to
1497 for (size_t i = 0; i < m_templates.GetCount(); i++)
1498 {
1499 wxDocTemplate *temp = (wxDocTemplate *)m_templates.Item(i)->GetData();
1500 if ( temp->FileMatchesTemplate(path) )
1501 {
1502 theTemplate = temp;
1503 break;
1504 }
1505 }
1506 return theTemplate;
1507 }
1508
1509 // Prompts user to open a file, using file specs in templates.
1510 // Must extend the file selector dialog or implement own; OR
1511 // match the extension to the template extension.
1512
1513 wxDocTemplate *wxDocManager::SelectDocumentPath(wxDocTemplate **templates,
1514 int noTemplates,
1515 wxString& path,
1516 long WXUNUSED(flags),
1517 bool WXUNUSED(save))
1518 {
1519 #ifdef wxHAS_MULTIPLE_FILEDLG_FILTERS
1520 wxString descrBuf;
1521
1522 for (int i = 0; i < noTemplates; i++)
1523 {
1524 if (templates[i]->IsVisible())
1525 {
1526 // add a '|' to separate this filter from the previous one
1527 if ( !descrBuf.empty() )
1528 descrBuf << wxT('|');
1529
1530 descrBuf << templates[i]->GetDescription()
1531 << wxT(" (") << templates[i]->GetFileFilter() << wxT(") |")
1532 << templates[i]->GetFileFilter();
1533 }
1534 }
1535 #else
1536 wxString descrBuf = wxT("*.*");
1537 wxUnusedVar(noTemplates);
1538 #endif
1539
1540 int FilterIndex = -1;
1541
1542 wxWindow* parent = wxFindSuitableParent();
1543
1544 wxString pathTmp = wxFileSelectorEx(_("Open File"),
1545 GetLastDirectory(),
1546 wxEmptyString,
1547 &FilterIndex,
1548 descrBuf,
1549 0,
1550 parent);
1551
1552 wxDocTemplate *theTemplate = NULL;
1553 if (!pathTmp.empty())
1554 {
1555 if (!wxFileExists(pathTmp))
1556 {
1557 wxString msgTitle;
1558 if (!wxTheApp->GetAppDisplayName().empty())
1559 msgTitle = wxTheApp->GetAppDisplayName();
1560 else
1561 msgTitle = wxString(_("File error"));
1562
1563 wxMessageBox(_("Sorry, could not open this file."),
1564 msgTitle,
1565 wxOK | wxICON_EXCLAMATION | wxCENTRE,
1566 parent);
1567
1568 path = wxEmptyString;
1569 return NULL;
1570 }
1571
1572 SetLastDirectory(wxPathOnly(pathTmp));
1573
1574 path = pathTmp;
1575
1576 // first choose the template using the extension, if this fails (i.e.
1577 // wxFileSelectorEx() didn't fill it), then use the path
1578 if ( FilterIndex != -1 )
1579 theTemplate = templates[FilterIndex];
1580 if ( !theTemplate )
1581 theTemplate = FindTemplateForPath(path);
1582 if ( !theTemplate )
1583 {
1584 // Since we do not add files with non-default extensions to the
1585 // file history this can only happen if the application changes the
1586 // allowed templates in runtime.
1587 wxMessageBox(_("Sorry, the format for this file is unknown."),
1588 _("Open File"),
1589 wxOK | wxICON_EXCLAMATION | wxCENTRE,
1590 parent);
1591 }
1592 }
1593 else
1594 {
1595 path.clear();
1596 }
1597
1598 return theTemplate;
1599 }
1600
1601 wxDocTemplate *wxDocManager::SelectDocumentType(wxDocTemplate **templates,
1602 int noTemplates, bool sort)
1603 {
1604 wxArrayString strings;
1605 wxScopedArray<wxDocTemplate *> data(new wxDocTemplate *[noTemplates]);
1606 int i;
1607 int n = 0;
1608
1609 for (i = 0; i < noTemplates; i++)
1610 {
1611 if (templates[i]->IsVisible())
1612 {
1613 int j;
1614 bool want = true;
1615 for (j = 0; j < n; j++)
1616 {
1617 //filter out NOT unique documents + view combinations
1618 if ( templates[i]->m_docTypeName == data[j]->m_docTypeName &&
1619 templates[i]->m_viewTypeName == data[j]->m_viewTypeName
1620 )
1621 want = false;
1622 }
1623
1624 if ( want )
1625 {
1626 strings.Add(templates[i]->m_description);
1627
1628 data[n] = templates[i];
1629 n ++;
1630 }
1631 }
1632 } // for
1633
1634 if (sort)
1635 {
1636 strings.Sort(); // ascending sort
1637 // Yes, this will be slow, but template lists
1638 // are typically short.
1639 int j;
1640 n = strings.Count();
1641 for (i = 0; i < n; i++)
1642 {
1643 for (j = 0; j < noTemplates; j++)
1644 {
1645 if (strings[i] == templates[j]->m_description)
1646 data[i] = templates[j];
1647 }
1648 }
1649 }
1650
1651 wxDocTemplate *theTemplate;
1652
1653 switch ( n )
1654 {
1655 case 0:
1656 // no visible templates, hence nothing to choose from
1657 theTemplate = NULL;
1658 break;
1659
1660 case 1:
1661 // don't propose the user to choose if he has no choice
1662 theTemplate = data[0];
1663 break;
1664
1665 default:
1666 // propose the user to choose one of several
1667 theTemplate = (wxDocTemplate *)wxGetSingleChoiceData
1668 (
1669 _("Select a document template"),
1670 _("Templates"),
1671 strings,
1672 (void **)data.get(),
1673 wxFindSuitableParent()
1674 );
1675 }
1676
1677 return theTemplate;
1678 }
1679
1680 wxDocTemplate *wxDocManager::SelectViewType(wxDocTemplate **templates,
1681 int noTemplates, bool sort)
1682 {
1683 wxArrayString strings;
1684 wxScopedArray<wxDocTemplate *> data(new wxDocTemplate *[noTemplates]);
1685 int i;
1686 int n = 0;
1687
1688 for (i = 0; i < noTemplates; i++)
1689 {
1690 wxDocTemplate *templ = templates[i];
1691 if ( templ->IsVisible() && !templ->GetViewName().empty() )
1692 {
1693 int j;
1694 bool want = true;
1695 for (j = 0; j < n; j++)
1696 {
1697 //filter out NOT unique views
1698 if ( templates[i]->m_viewTypeName == data[j]->m_viewTypeName )
1699 want = false;
1700 }
1701
1702 if ( want )
1703 {
1704 strings.Add(templ->m_viewTypeName);
1705 data[n] = templ;
1706 n ++;
1707 }
1708 }
1709 }
1710
1711 if (sort)
1712 {
1713 strings.Sort(); // ascending sort
1714 // Yes, this will be slow, but template lists
1715 // are typically short.
1716 int j;
1717 n = strings.Count();
1718 for (i = 0; i < n; i++)
1719 {
1720 for (j = 0; j < noTemplates; j++)
1721 {
1722 if (strings[i] == templates[j]->m_viewTypeName)
1723 data[i] = templates[j];
1724 }
1725 }
1726 }
1727
1728 wxDocTemplate *theTemplate;
1729
1730 // the same logic as above
1731 switch ( n )
1732 {
1733 case 0:
1734 theTemplate = NULL;
1735 break;
1736
1737 case 1:
1738 theTemplate = data[0];
1739 break;
1740
1741 default:
1742 theTemplate = (wxDocTemplate *)wxGetSingleChoiceData
1743 (
1744 _("Select a document view"),
1745 _("Views"),
1746 strings,
1747 (void **)data.get(),
1748 wxFindSuitableParent()
1749 );
1750
1751 }
1752
1753 return theTemplate;
1754 }
1755
1756 void wxDocManager::AssociateTemplate(wxDocTemplate *temp)
1757 {
1758 if (!m_templates.Member(temp))
1759 m_templates.Append(temp);
1760 }
1761
1762 void wxDocManager::DisassociateTemplate(wxDocTemplate *temp)
1763 {
1764 m_templates.DeleteObject(temp);
1765 }
1766
1767 // Add and remove a document from the manager's list
1768 void wxDocManager::AddDocument(wxDocument *doc)
1769 {
1770 if (!m_docs.Member(doc))
1771 m_docs.Append(doc);
1772 }
1773
1774 void wxDocManager::RemoveDocument(wxDocument *doc)
1775 {
1776 m_docs.DeleteObject(doc);
1777 }
1778
1779 // Views or windows should inform the document manager
1780 // when a view is going in or out of focus
1781 void wxDocManager::ActivateView(wxView *view, bool activate)
1782 {
1783 if ( activate )
1784 {
1785 m_currentView = view;
1786 }
1787 else // deactivate
1788 {
1789 if ( m_currentView == view )
1790 {
1791 // don't keep stale pointer
1792 m_currentView = NULL;
1793 }
1794 }
1795 }
1796
1797 // ----------------------------------------------------------------------------
1798 // wxDocChildFrameAnyBase
1799 // ----------------------------------------------------------------------------
1800
1801 bool wxDocChildFrameAnyBase::CloseView(wxCloseEvent& event)
1802 {
1803 if ( m_childView )
1804 {
1805 if ( event.CanVeto() && !m_childView->Close(false) )
1806 {
1807 event.Veto();
1808 return false;
1809 }
1810
1811 m_childView->Activate(false);
1812 delete m_childView;
1813 m_childView = NULL;
1814 }
1815
1816 m_childDocument = NULL;
1817
1818 return true;
1819 }
1820
1821 // ----------------------------------------------------------------------------
1822 // Default parent frame
1823 // ----------------------------------------------------------------------------
1824
1825 BEGIN_EVENT_TABLE(wxDocParentFrame, wxFrame)
1826 EVT_MENU(wxID_EXIT, wxDocParentFrame::OnExit)
1827 EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, wxDocParentFrame::OnMRUFile)
1828 EVT_CLOSE(wxDocParentFrame::OnCloseWindow)
1829 END_EVENT_TABLE()
1830
1831 wxDocParentFrame::wxDocParentFrame()
1832 {
1833 m_docManager = NULL;
1834 }
1835
1836 wxDocParentFrame::wxDocParentFrame(wxDocManager *manager,
1837 wxFrame *frame,
1838 wxWindowID id,
1839 const wxString& title,
1840 const wxPoint& pos,
1841 const wxSize& size,
1842 long style,
1843 const wxString& name)
1844 : wxFrame(frame, id, title, pos, size, style, name)
1845 {
1846 m_docManager = manager;
1847 }
1848
1849 bool wxDocParentFrame::Create(wxDocManager *manager,
1850 wxFrame *frame,
1851 wxWindowID id,
1852 const wxString& title,
1853 const wxPoint& pos,
1854 const wxSize& size,
1855 long style,
1856 const wxString& name)
1857 {
1858 m_docManager = manager;
1859 return base_type::Create(frame, id, title, pos, size, style, name);
1860 }
1861
1862 void wxDocParentFrame::OnExit(wxCommandEvent& WXUNUSED(event))
1863 {
1864 Close();
1865 }
1866
1867 void wxDocParentFrame::OnMRUFile(wxCommandEvent& event)
1868 {
1869 int n = event.GetId() - wxID_FILE1; // the index in MRU list
1870 wxString filename(m_docManager->GetHistoryFile(n));
1871 if ( filename.empty() )
1872 return;
1873
1874 wxString errMsg; // must contain exactly one "%s" if non-empty
1875 if ( wxFile::Exists(filename) )
1876 {
1877 // try to open it
1878 if ( m_docManager->CreateDocument(filename, wxDOC_SILENT) )
1879 return;
1880
1881 errMsg = _("The file '%s' couldn't be opened.");
1882 }
1883 else // file doesn't exist
1884 {
1885 errMsg = _("The file '%s' doesn't exist and couldn't be opened.");
1886 }
1887
1888
1889 wxASSERT_MSG( !errMsg.empty(), "should have an error message" );
1890
1891 // remove the file which we can't open from the MRU list
1892 m_docManager->RemoveFileFromHistory(n);
1893
1894 // and tell the user about it
1895 wxLogError(errMsg + '\n' +
1896 _("It has been removed from the most recently used files list."),
1897 filename);
1898 }
1899
1900 // Extend event processing to search the view's event table
1901 bool wxDocParentFrame::TryBefore(wxEvent& event)
1902 {
1903 if ( m_docManager && m_docManager->ProcessEventHere(event) )
1904 return true;
1905
1906 return wxFrame::TryBefore(event);
1907 }
1908
1909 // Define the behaviour for the frame closing
1910 // - must delete all frames except for the main one.
1911 void wxDocParentFrame::OnCloseWindow(wxCloseEvent& event)
1912 {
1913 if (m_docManager->Clear(!event.CanVeto()))
1914 {
1915 Destroy();
1916 }
1917 else
1918 event.Veto();
1919 }
1920
1921 #if wxUSE_PRINTING_ARCHITECTURE
1922
1923 wxDocPrintout::wxDocPrintout(wxView *view, const wxString& title)
1924 : wxPrintout(title)
1925 {
1926 m_printoutView = view;
1927 }
1928
1929 bool wxDocPrintout::OnPrintPage(int WXUNUSED(page))
1930 {
1931 wxDC *dc = GetDC();
1932
1933 // Get the logical pixels per inch of screen and printer
1934 int ppiScreenX, ppiScreenY;
1935 GetPPIScreen(&ppiScreenX, &ppiScreenY);
1936 wxUnusedVar(ppiScreenY);
1937 int ppiPrinterX, ppiPrinterY;
1938 GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);
1939 wxUnusedVar(ppiPrinterY);
1940
1941 // This scales the DC so that the printout roughly represents the
1942 // the screen scaling. The text point size _should_ be the right size
1943 // but in fact is too small for some reason. This is a detail that will
1944 // need to be addressed at some point but can be fudged for the
1945 // moment.
1946 float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);
1947
1948 // Now we have to check in case our real page size is reduced
1949 // (e.g. because we're drawing to a print preview memory DC)
1950 int pageWidth, pageHeight;
1951 int w, h;
1952 dc->GetSize(&w, &h);
1953 GetPageSizePixels(&pageWidth, &pageHeight);
1954 wxUnusedVar(pageHeight);
1955
1956 // If printer pageWidth == current DC width, then this doesn't
1957 // change. But w might be the preview bitmap width, so scale down.
1958 float overallScale = scale * (float)(w/(float)pageWidth);
1959 dc->SetUserScale(overallScale, overallScale);
1960
1961 if (m_printoutView)
1962 {
1963 m_printoutView->OnDraw(dc);
1964 }
1965 return true;
1966 }
1967
1968 bool wxDocPrintout::HasPage(int pageNum)
1969 {
1970 return (pageNum == 1);
1971 }
1972
1973 bool wxDocPrintout::OnBeginDocument(int startPage, int endPage)
1974 {
1975 if (!wxPrintout::OnBeginDocument(startPage, endPage))
1976 return false;
1977
1978 return true;
1979 }
1980
1981 void wxDocPrintout::GetPageInfo(int *minPage, int *maxPage,
1982 int *selPageFrom, int *selPageTo)
1983 {
1984 *minPage = 1;
1985 *maxPage = 1;
1986 *selPageFrom = 1;
1987 *selPageTo = 1;
1988 }
1989
1990 #endif // wxUSE_PRINTING_ARCHITECTURE
1991
1992 // ----------------------------------------------------------------------------
1993 // File history (a.k.a. MRU, most recently used, files list)
1994 // ----------------------------------------------------------------------------
1995
1996 wxFileHistory::wxFileHistory(size_t maxFiles, wxWindowID idBase)
1997 {
1998 m_fileMaxFiles = maxFiles;
1999 m_idBase = idBase;
2000 }
2001
2002 void wxFileHistory::AddFileToHistory(const wxString& file)
2003 {
2004 // check if we don't already have this file
2005 const wxFileName fnNew(file);
2006 size_t i,
2007 numFiles = m_fileHistory.size();
2008 for ( i = 0; i < numFiles; i++ )
2009 {
2010 if ( fnNew == m_fileHistory[i] )
2011 {
2012 // we do have it, move it to the top of the history
2013 RemoveFileFromHistory(i);
2014 numFiles--;
2015 break;
2016 }
2017 }
2018
2019 // if we already have a full history, delete the one at the end
2020 if ( numFiles == m_fileMaxFiles )
2021 {
2022 RemoveFileFromHistory(--numFiles);
2023 }
2024
2025 // add a new menu item to all file menus (they will be updated below)
2026 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
2027 node;
2028 node = node->GetNext() )
2029 {
2030 wxMenu * const menu = (wxMenu *)node->GetData();
2031
2032 if ( !numFiles && menu->GetMenuItemCount() )
2033 menu->AppendSeparator();
2034
2035 // label doesn't matter, it will be set below anyhow, but it can't
2036 // be empty (this is supposed to indicate a stock item)
2037 menu->Append(m_idBase + numFiles, " ");
2038 }
2039
2040 // insert the new file in the beginning of the file history
2041 m_fileHistory.insert(m_fileHistory.begin(), file);
2042 numFiles++;
2043
2044 // update the labels in all menus
2045 for ( i = 0; i < numFiles; i++ )
2046 {
2047 // if in same directory just show the filename; otherwise the full path
2048 const wxFileName fnOld(m_fileHistory[i]);
2049
2050 wxString pathInMenu;
2051 if ( fnOld.GetPath() == fnNew.GetPath() )
2052 {
2053 pathInMenu = fnOld.GetFullName();
2054 }
2055 else // file in different directory
2056 {
2057 // absolute path; could also set relative path
2058 pathInMenu = m_fileHistory[i];
2059 }
2060
2061 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
2062 node;
2063 node = node->GetNext() )
2064 {
2065 wxMenu * const menu = (wxMenu *)node->GetData();
2066
2067 menu->SetLabel(m_idBase + i, GetMRUEntryLabel(i, pathInMenu));
2068 }
2069 }
2070 }
2071
2072 void wxFileHistory::RemoveFileFromHistory(size_t i)
2073 {
2074 size_t numFiles = m_fileHistory.size();
2075 wxCHECK_RET( i < numFiles,
2076 wxT("invalid index in wxFileHistory::RemoveFileFromHistory") );
2077
2078 // delete the element from the array
2079 m_fileHistory.RemoveAt(i);
2080 numFiles--;
2081
2082 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
2083 node;
2084 node = node->GetNext() )
2085 {
2086 wxMenu * const menu = (wxMenu *) node->GetData();
2087
2088 // shift filenames up
2089 for ( size_t j = i; j < numFiles; j++ )
2090 {
2091 menu->SetLabel(m_idBase + j, GetMRUEntryLabel(j, m_fileHistory[j]));
2092 }
2093
2094 // delete the last menu item which is unused now
2095 const wxWindowID lastItemId = m_idBase + numFiles;
2096 if ( menu->FindItem(lastItemId) )
2097 menu->Delete(lastItemId);
2098
2099 // delete the last separator too if no more files are left
2100 if ( m_fileHistory.empty() )
2101 {
2102 const wxMenuItemList::compatibility_iterator
2103 nodeLast = menu->GetMenuItems().GetLast();
2104 if ( nodeLast )
2105 {
2106 wxMenuItem * const lastMenuItem = nodeLast->GetData();
2107 if ( lastMenuItem->IsSeparator() )
2108 menu->Delete(lastMenuItem);
2109 }
2110 //else: menu is empty somehow
2111 }
2112 }
2113 }
2114
2115 void wxFileHistory::UseMenu(wxMenu *menu)
2116 {
2117 if ( !m_fileMenus.Member(menu) )
2118 m_fileMenus.Append(menu);
2119 }
2120
2121 void wxFileHistory::RemoveMenu(wxMenu *menu)
2122 {
2123 m_fileMenus.DeleteObject(menu);
2124 }
2125
2126 #if wxUSE_CONFIG
2127 void wxFileHistory::Load(const wxConfigBase& config)
2128 {
2129 m_fileHistory.Clear();
2130
2131 wxString buf;
2132 buf.Printf(wxT("file%d"), 1);
2133
2134 wxString historyFile;
2135 while ((m_fileHistory.GetCount() < m_fileMaxFiles) &&
2136 config.Read(buf, &historyFile) && !historyFile.empty())
2137 {
2138 m_fileHistory.Add(historyFile);
2139
2140 buf.Printf(wxT("file%d"), (int)m_fileHistory.GetCount()+1);
2141 historyFile = wxEmptyString;
2142 }
2143
2144 AddFilesToMenu();
2145 }
2146
2147 void wxFileHistory::Save(wxConfigBase& config)
2148 {
2149 size_t i;
2150 for (i = 0; i < m_fileMaxFiles; i++)
2151 {
2152 wxString buf;
2153 buf.Printf(wxT("file%d"), (int)i+1);
2154 if (i < m_fileHistory.GetCount())
2155 config.Write(buf, wxString(m_fileHistory[i]));
2156 else
2157 config.Write(buf, wxEmptyString);
2158 }
2159 }
2160 #endif // wxUSE_CONFIG
2161
2162 void wxFileHistory::AddFilesToMenu()
2163 {
2164 if ( m_fileHistory.empty() )
2165 return;
2166
2167 for ( wxList::compatibility_iterator node = m_fileMenus.GetFirst();
2168 node;
2169 node = node->GetNext() )
2170 {
2171 AddFilesToMenu((wxMenu *) node->GetData());
2172 }
2173 }
2174
2175 void wxFileHistory::AddFilesToMenu(wxMenu* menu)
2176 {
2177 if ( m_fileHistory.empty() )
2178 return;
2179
2180 if ( menu->GetMenuItemCount() )
2181 menu->AppendSeparator();
2182
2183 for ( size_t i = 0; i < m_fileHistory.GetCount(); i++ )
2184 {
2185 menu->Append(m_idBase + i, GetMRUEntryLabel(i, m_fileHistory[i]));
2186 }
2187 }
2188
2189 // ----------------------------------------------------------------------------
2190 // Permits compatibility with existing file formats and functions that
2191 // manipulate files directly
2192 // ----------------------------------------------------------------------------
2193
2194 #if wxUSE_STD_IOSTREAM
2195
2196 bool wxTransferFileToStream(const wxString& filename, wxSTD ostream& stream)
2197 {
2198 wxFFile file(filename, _T("rb"));
2199 if ( !file.IsOpened() )
2200 return false;
2201
2202 char buf[4096];
2203
2204 size_t nRead;
2205 do
2206 {
2207 nRead = file.Read(buf, WXSIZEOF(buf));
2208 if ( file.Error() )
2209 return false;
2210
2211 stream.write(buf, nRead);
2212 if ( !stream )
2213 return false;
2214 }
2215 while ( !file.Eof() );
2216
2217 return true;
2218 }
2219
2220 bool wxTransferStreamToFile(wxSTD istream& stream, const wxString& filename)
2221 {
2222 wxFFile file(filename, _T("wb"));
2223 if ( !file.IsOpened() )
2224 return false;
2225
2226 char buf[4096];
2227 do
2228 {
2229 stream.read(buf, WXSIZEOF(buf));
2230 if ( !stream.bad() ) // fail may be set on EOF, don't use operator!()
2231 {
2232 if ( !file.Write(buf, stream.gcount()) )
2233 return false;
2234 }
2235 }
2236 while ( !stream.eof() );
2237
2238 return true;
2239 }
2240
2241 #else // !wxUSE_STD_IOSTREAM
2242
2243 bool wxTransferFileToStream(const wxString& filename, wxOutputStream& stream)
2244 {
2245 wxFFile file(filename, _T("rb"));
2246 if ( !file.IsOpened() )
2247 return false;
2248
2249 char buf[4096];
2250
2251 size_t nRead;
2252 do
2253 {
2254 nRead = file.Read(buf, WXSIZEOF(buf));
2255 if ( file.Error() )
2256 return false;
2257
2258 stream.Write(buf, nRead);
2259 if ( !stream )
2260 return false;
2261 }
2262 while ( !file.Eof() );
2263
2264 return true;
2265 }
2266
2267 bool wxTransferStreamToFile(wxInputStream& stream, const wxString& filename)
2268 {
2269 wxFFile file(filename, _T("wb"));
2270 if ( !file.IsOpened() )
2271 return false;
2272
2273 char buf[4096];
2274 for ( ;; )
2275 {
2276 stream.Read(buf, WXSIZEOF(buf));
2277
2278 const size_t nRead = stream.LastRead();
2279 if ( !nRead )
2280 {
2281 if ( stream.Eof() )
2282 break;
2283
2284 return false;
2285 }
2286
2287 if ( !file.Write(buf, nRead) )
2288 return false;
2289 }
2290
2291 return true;
2292 }
2293
2294 #endif // wxUSE_STD_IOSTREAM/!wxUSE_STD_IOSTREAM
2295
2296 #endif // wxUSE_DOC_VIEW_ARCHITECTURE