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