///////////////////////////////////////////////////////////////////////////// // Name: src/common/docview.cpp // Purpose: Document/view classes // Author: Julian Smart // Modified by: Vadim Zeitlin // Created: 01/02/97 // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ // declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #if wxUSE_DOC_VIEW_ARCHITECTURE #include "wx/docview.h" #ifndef WX_PRECOMP #include "wx/list.h" #include "wx/string.h" #include "wx/utils.h" #include "wx/app.h" #include "wx/dc.h" #include "wx/dialog.h" #include "wx/menu.h" #include "wx/filedlg.h" #include "wx/intl.h" #include "wx/log.h" #include "wx/msgdlg.h" #include "wx/mdi.h" #include "wx/choicdlg.h" #endif #if wxUSE_PRINTING_ARCHITECTURE #include "wx/prntbase.h" #include "wx/printdlg.h" #endif #include "wx/confbase.h" #include "wx/filename.h" #include "wx/file.h" #include "wx/ffile.h" #include "wx/cmdproc.h" #include "wx/tokenzr.h" #include "wx/filename.h" #include "wx/stdpaths.h" #include "wx/vector.h" #include "wx/scopedarray.h" #include "wx/scopedptr.h" #include "wx/scopeguard.h" #include "wx/except.h" #if wxUSE_STD_IOSTREAM #include "wx/ioswrap.h" #include "wx/beforestd.h" #if wxUSE_IOSTREAMH #include #else #include #endif #include "wx/afterstd.h" #else #include "wx/wfstream.h" #endif // ---------------------------------------------------------------------------- // wxWidgets macros // ---------------------------------------------------------------------------- IMPLEMENT_ABSTRACT_CLASS(wxDocument, wxEvtHandler) IMPLEMENT_ABSTRACT_CLASS(wxView, wxEvtHandler) IMPLEMENT_ABSTRACT_CLASS(wxDocTemplate, wxObject) IMPLEMENT_DYNAMIC_CLASS(wxDocManager, wxEvtHandler) IMPLEMENT_CLASS(wxDocChildFrame, wxFrame) IMPLEMENT_CLASS(wxDocParentFrame, wxFrame) #if wxUSE_PRINTING_ARCHITECTURE IMPLEMENT_DYNAMIC_CLASS(wxDocPrintout, wxPrintout) #endif // ============================================================================ // implementation // ============================================================================ // ---------------------------------------------------------------------------- // private helpers // ---------------------------------------------------------------------------- namespace { wxString FindExtension(const wxString& path) { wxString ext; wxFileName::SplitPath(path, NULL, NULL, &ext); // VZ: extensions are considered not case sensitive - is this really a good // idea? return ext.MakeLower(); } } // anonymous namespace // ---------------------------------------------------------------------------- // Definition of wxDocument // ---------------------------------------------------------------------------- wxDocument::wxDocument(wxDocument *parent) { m_documentModified = false; m_documentTemplate = NULL; m_documentParent = parent; if ( parent ) parent->m_childDocuments.push_back(this); m_commandProcessor = NULL; m_savedYet = false; } bool wxDocument::DeleteContents() { return true; } wxDocument::~wxDocument() { delete m_commandProcessor; if (GetDocumentManager()) GetDocumentManager()->RemoveDocument(this); if ( m_documentParent ) m_documentParent->m_childDocuments.remove(this); // Not safe to do here, since it'll invoke virtual view functions // expecting to see valid derived objects: and by the time we get here, // we've called destructors higher up. //DeleteAllViews(); } bool wxDocument::Close() { if ( !OnSaveModified() ) return false; // When the parent document closes, its children must be closed as well as // they can't exist without the parent. // As usual, first check if all children can be closed. DocsList::const_iterator it = m_childDocuments.begin(); for ( DocsList::const_iterator end = m_childDocuments.end(); it != end; ++it ) { if ( !(*it)->OnSaveModified() ) { // Leave the parent document opened if a child can't close. return false; } } // Now that they all did, do close them: as m_childDocuments is modified as // we iterate over it, don't use the usual for-style iteration here. while ( !m_childDocuments.empty() ) { wxDocument * const childDoc = m_childDocuments.front(); // This will call OnSaveModified() once again but it shouldn't do // anything as the document was just saved or marked as not needing to // be saved by the call to OnSaveModified() that returned true above. if ( !childDoc->Close() ) { wxFAIL_MSG( "Closing the child document unexpectedly failed " "after its OnSaveModified() returned true" ); } // Delete the child document by deleting all its views. childDoc->DeleteAllViews(); } return OnCloseDocument(); } bool wxDocument::OnCloseDocument() { // Tell all views that we're about to close NotifyClosing(); DeleteContents(); Modify(false); return true; } // Note that this implicitly deletes the document when the last view is // deleted. bool wxDocument::DeleteAllViews() { wxDocManager* manager = GetDocumentManager(); // first check if all views agree to be closed const wxList::iterator end = m_documentViews.end(); for ( wxList::iterator i = m_documentViews.begin(); i != end; ++i ) { wxView *view = (wxView *)*i; if ( !view->Close() ) return false; } // all views agreed to close, now do close them if ( m_documentViews.empty() ) { // normally the document would be implicitly deleted when the last view // is, but if don't have any views, do it here instead if ( manager && manager->GetDocuments().Member(this) ) delete this; } else // have views { // as we delete elements we iterate over, don't use the usual "from // begin to end" loop for ( ;; ) { wxView *view = (wxView *)*m_documentViews.begin(); bool isLastOne = m_documentViews.size() == 1; // this always deletes the node implicitly and if this is the last // view also deletes this object itself (also implicitly, great), // so we can't test for m_documentViews.empty() after calling this! delete view; if ( isLastOne ) break; } } return true; } wxView *wxDocument::GetFirstView() const { if ( m_documentViews.empty() ) return NULL; return static_cast(m_documentViews.GetFirst()->GetData()); } void wxDocument::Modify(bool mod) { if (mod != m_documentModified) { m_documentModified = mod; // Allow views to append asterix to the title wxView* view = GetFirstView(); if (view) view->OnChangeFilename(); } } wxDocManager *wxDocument::GetDocumentManager() const { // For child documents we use the same document manager as the parent, even // though we don't have our own template (as children are not opened/saved // directly). if ( m_documentParent ) return m_documentParent->GetDocumentManager(); return m_documentTemplate ? m_documentTemplate->GetDocumentManager() : NULL; } bool wxDocument::OnNewDocument() { // notice that there is no need to neither reset nor even check the // modified flag here as the document itself is a new object (this is only // called from CreateDocument()) and so it shouldn't be saved anyhow even // if it is modified -- this could happen if the user code creates // documents pre-filled with some user-entered (and which hence must not be // lost) information SetDocumentSaved(false); const wxString name = GetDocumentManager()->MakeNewDocumentName(); SetTitle(name); SetFilename(name, true); return true; } bool wxDocument::Save() { if ( AlreadySaved() ) return true; if ( m_documentFile.empty() || !m_savedYet ) return SaveAs(); return OnSaveDocument(m_documentFile); } bool wxDocument::SaveAs() { wxDocTemplate *docTemplate = GetDocumentTemplate(); if (!docTemplate) return false; #ifdef wxHAS_MULTIPLE_FILEDLG_FILTERS wxString filter = docTemplate->GetDescription() + wxT(" (") + docTemplate->GetFileFilter() + wxT(")|") + docTemplate->GetFileFilter(); // Now see if there are some other template with identical view and document // classes, whose filters may also be used. if (docTemplate->GetViewClassInfo() && docTemplate->GetDocClassInfo()) { wxList::compatibility_iterator node = docTemplate->GetDocumentManager()->GetTemplates().GetFirst(); while (node) { wxDocTemplate *t = (wxDocTemplate*) node->GetData(); if (t->IsVisible() && t != docTemplate && t->GetViewClassInfo() == docTemplate->GetViewClassInfo() && t->GetDocClassInfo() == docTemplate->GetDocClassInfo()) { // add a '|' to separate this filter from the previous one if ( !filter.empty() ) filter << wxT('|'); filter << t->GetDescription() << wxT(" (") << t->GetFileFilter() << wxT(") |") << t->GetFileFilter(); } node = node->GetNext(); } } #else wxString filter = docTemplate->GetFileFilter() ; #endif wxString defaultDir = docTemplate->GetDirectory(); if ( defaultDir.empty() ) { defaultDir = wxPathOnly(GetFilename()); if ( defaultDir.empty() ) defaultDir = GetDocumentManager()->GetLastDirectory(); } wxString fileName = wxFileSelector(_("Save As"), defaultDir, wxFileNameFromPath(GetFilename()), docTemplate->GetDefaultExtension(), filter, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, GetDocumentWindow()); if (fileName.empty()) return false; // cancelled by user // Files that were not saved correctly are not added to the FileHistory. if (!OnSaveDocument(fileName)) return false; SetTitle(wxFileNameFromPath(fileName)); SetFilename(fileName, true); // will call OnChangeFileName automatically // A file that doesn't use the default extension of its document template // cannot be opened via the FileHistory, so we do not add it. if (docTemplate->FileMatchesTemplate(fileName)) { GetDocumentManager()->AddFileToHistory(fileName); } //else: the user will probably not be able to open the file again, so we // could warn about the wrong file-extension here return true; } bool wxDocument::OnSaveDocument(const wxString& file) { if ( !file ) return false; if ( !DoSaveDocument(file) ) return false; if ( m_commandProcessor ) m_commandProcessor->MarkAsSaved(); Modify(false); SetFilename(file); SetDocumentSaved(true); #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON wxFileName fn(file) ; fn.MacSetDefaultTypeAndCreator() ; #endif return true; } bool wxDocument::OnOpenDocument(const wxString& file) { // notice that there is no need to check the modified flag here for the // reasons explained in OnNewDocument() if ( !DoOpenDocument(file) ) return false; SetFilename(file, true); // stretching the logic a little this does make sense because the document // had been saved into the file we just loaded it from, it just could have // happened during a previous program execution, it's just that the name of // this method is a bit unfortunate, it should probably have been called // HasAssociatedFileName() SetDocumentSaved(true); UpdateAllViews(); return true; } #if wxUSE_STD_IOSTREAM wxSTD istream& wxDocument::LoadObject(wxSTD istream& stream) #else wxInputStream& wxDocument::LoadObject(wxInputStream& stream) #endif { return stream; } #if wxUSE_STD_IOSTREAM wxSTD ostream& wxDocument::SaveObject(wxSTD ostream& stream) #else wxOutputStream& wxDocument::SaveObject(wxOutputStream& stream) #endif { return stream; } bool wxDocument::Revert() { if ( wxMessageBox ( _("Discard changes and reload the last saved version?"), wxTheApp->GetAppDisplayName(), wxYES_NO | wxCANCEL | wxICON_QUESTION, GetDocumentWindow() ) != wxYES ) return false; if ( !DoOpenDocument(GetFilename()) ) return false; Modify(false); UpdateAllViews(); return true; } // Get title, or filename if no title, else unnamed #if WXWIN_COMPATIBILITY_2_8 bool wxDocument::GetPrintableName(wxString& buf) const { // this function cannot only be overridden by the user code but also // called by it so we need to ensure that we return the same thing as // GetUserReadableName() but we can't call it because this would result in // an infinite recursion, hence we use the helper DoGetUserReadableName() buf = DoGetUserReadableName(); return true; } #endif // WXWIN_COMPATIBILITY_2_8 wxString wxDocument::GetUserReadableName() const { #if WXWIN_COMPATIBILITY_2_8 // we need to call the old virtual function to ensure that the overridden // version of it is still called wxString name; if ( GetPrintableName(name) ) return name; #endif // WXWIN_COMPATIBILITY_2_8 return DoGetUserReadableName(); } wxString wxDocument::DoGetUserReadableName() const { if ( !m_documentTitle.empty() ) return m_documentTitle; if ( !m_documentFile.empty() ) return wxFileNameFromPath(m_documentFile); return _("unnamed"); } wxWindow *wxDocument::GetDocumentWindow() const { wxView * const view = GetFirstView(); return view ? view->GetFrame() : wxTheApp->GetTopWindow(); } wxCommandProcessor *wxDocument::OnCreateCommandProcessor() { return new wxCommandProcessor; } // true if safe to close bool wxDocument::OnSaveModified() { if ( IsModified() ) { switch ( wxMessageBox ( wxString::Format ( _("Do you want to save changes to %s?"), GetUserReadableName() ), wxTheApp->GetAppDisplayName(), wxYES_NO | wxCANCEL | wxICON_QUESTION | wxCENTRE ) ) { case wxNO: Modify(false); break; case wxYES: return Save(); case wxCANCEL: return false; } } return true; } bool wxDocument::Draw(wxDC& WXUNUSED(context)) { return true; } bool wxDocument::AddView(wxView *view) { if ( !m_documentViews.Member(view) ) { m_documentViews.Append(view); OnChangedViewList(); } return true; } bool wxDocument::RemoveView(wxView *view) { (void)m_documentViews.DeleteObject(view); OnChangedViewList(); return true; } bool wxDocument::OnCreate(const wxString& WXUNUSED(path), long flags) { return GetDocumentTemplate()->CreateView(this, flags) != NULL; } // Called after a view is added or removed. // The default implementation deletes the document if // there are no more views. void wxDocument::OnChangedViewList() { if ( m_documentViews.empty() && OnSaveModified() ) delete this; } void wxDocument::UpdateAllViews(wxView *sender, wxObject *hint) { wxList::compatibility_iterator node = m_documentViews.GetFirst(); while (node) { wxView *view = (wxView *)node->GetData(); if (view != sender) view->OnUpdate(sender, hint); node = node->GetNext(); } } void wxDocument::NotifyClosing() { wxList::compatibility_iterator node = m_documentViews.GetFirst(); while (node) { wxView *view = (wxView *)node->GetData(); view->OnClosingDocument(); node = node->GetNext(); } } void wxDocument::SetFilename(const wxString& filename, bool notifyViews) { m_documentFile = filename; OnChangeFilename(notifyViews); } void wxDocument::OnChangeFilename(bool notifyViews) { if ( notifyViews ) { // Notify the views that the filename has changed wxList::compatibility_iterator node = m_documentViews.GetFirst(); while (node) { wxView *view = (wxView *)node->GetData(); view->OnChangeFilename(); node = node->GetNext(); } } } bool wxDocument::DoSaveDocument(const wxString& file) { #if wxUSE_STD_IOSTREAM wxSTD ofstream store(file.mb_str(), wxSTD ios::binary); if ( !store ) #else wxFileOutputStream store(file); if ( store.GetLastError() != wxSTREAM_NO_ERROR ) #endif { wxLogError(_("File \"%s\" could not be opened for writing."), file); return false; } if (!SaveObject(store)) { wxLogError(_("Failed to save document to the file \"%s\"."), file); return false; } return true; } bool wxDocument::DoOpenDocument(const wxString& file) { #if wxUSE_STD_IOSTREAM wxSTD ifstream store(file.mb_str(), wxSTD ios::binary); if ( !store ) #else wxFileInputStream store(file); if (store.GetLastError() != wxSTREAM_NO_ERROR || !store.IsOk()) #endif { wxLogError(_("File \"%s\" could not be opened for reading."), file); return false; } #if wxUSE_STD_IOSTREAM LoadObject(store); if ( !store ) #else int res = LoadObject(store).GetLastError(); if ( res != wxSTREAM_NO_ERROR && res != wxSTREAM_EOF ) #endif { wxLogError(_("Failed to read document from the file \"%s\"."), file); return false; } return true; } // ---------------------------------------------------------------------------- // Document view // ---------------------------------------------------------------------------- wxView::wxView() { m_viewDocument = NULL; m_viewFrame = NULL; m_docChildFrame = NULL; } wxView::~wxView() { if (m_viewDocument && GetDocumentManager()) GetDocumentManager()->ActivateView(this, false); // reset our frame view first, before removing it from the document as // SetView(NULL) is a simple call while RemoveView() may result in user // code being executed and this user code can, for example, show a message // box which would result in an activation event for m_docChildFrame and so // could reactivate the view being destroyed -- unless we reset it first if ( m_docChildFrame && m_docChildFrame->GetView() == this ) { // prevent it from doing anything with us m_docChildFrame->SetView(NULL); // it doesn't make sense to leave the frame alive if its associated // view doesn't exist any more so unconditionally close it as well // // notice that we only get here if m_docChildFrame is non-NULL in the // first place and it will be always NULL if we're deleted because our // frame was closed, so this only catches the case of directly deleting // the view, as it happens if its creation fails in wxDocTemplate:: // CreateView() for example m_docChildFrame->GetWindow()->Destroy(); } if ( m_viewDocument ) m_viewDocument->RemoveView(this); } void wxView::SetDocChildFrame(wxDocChildFrameAnyBase *docChildFrame) { SetFrame(docChildFrame ? docChildFrame->GetWindow() : NULL); m_docChildFrame = docChildFrame; } bool wxView::TryBefore(wxEvent& event) { wxDocument * const doc = GetDocument(); return doc && doc->ProcessEventLocally(event); } void wxView::OnActivateView(bool WXUNUSED(activate), wxView *WXUNUSED(activeView), wxView *WXUNUSED(deactiveView)) { } void wxView::OnPrint(wxDC *dc, wxObject *WXUNUSED(info)) { OnDraw(dc); } void wxView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint)) { } void wxView::OnChangeFilename() { // GetFrame can return wxWindow rather than wxTopLevelWindow due to // generic MDI implementation so use SetLabel rather than SetTitle. // It should cause SetTitle() for top level windows. wxWindow *win = GetFrame(); if (!win) return; wxDocument *doc = GetDocument(); if (!doc) return; wxString label = doc->GetUserReadableName(); if (doc->IsModified()) { label += "*"; } win->SetLabel(label); } void wxView::SetDocument(wxDocument *doc) { m_viewDocument = doc; if (doc) doc->AddView(this); } bool wxView::Close(bool deleteWindow) { return OnClose(deleteWindow); } void wxView::Activate(bool activate) { if (GetDocument() && GetDocumentManager()) { OnActivateView(activate, this, GetDocumentManager()->GetCurrentView()); GetDocumentManager()->ActivateView(this, activate); } } bool wxView::OnClose(bool WXUNUSED(deleteWindow)) { return GetDocument() ? GetDocument()->Close() : true; } #if wxUSE_PRINTING_ARCHITECTURE wxPrintout *wxView::OnCreatePrintout() { return new wxDocPrintout(this); } #endif // wxUSE_PRINTING_ARCHITECTURE // ---------------------------------------------------------------------------- // wxDocTemplate // ---------------------------------------------------------------------------- wxDocTemplate::wxDocTemplate(wxDocManager *manager, const wxString& descr, const wxString& filter, const wxString& dir, const wxString& ext, const wxString& docTypeName, const wxString& viewTypeName, wxClassInfo *docClassInfo, wxClassInfo *viewClassInfo, long flags) { m_documentManager = manager; m_description = descr; m_directory = dir; m_defaultExt = ext; m_fileFilter = filter; m_flags = flags; m_docTypeName = docTypeName; m_viewTypeName = viewTypeName; m_documentManager->AssociateTemplate(this); m_docClassInfo = docClassInfo; m_viewClassInfo = viewClassInfo; } wxDocTemplate::~wxDocTemplate() { m_documentManager->DisassociateTemplate(this); } // Tries to dynamically construct an object of the right class. wxDocument *wxDocTemplate::CreateDocument(const wxString& path, long flags) { // InitDocument() is supposed to delete the document object if its // initialization fails so don't use wxScopedPtr<> here: this is fragile // but unavoidable because the default implementation uses CreateView() // which may -- or not -- create a wxView and if it does create it and its // initialization fails then the view destructor will delete the document // (via RemoveView()) and as we can't distinguish between the two cases we // just have to assume that it always deletes it in case of failure wxDocument * const doc = DoCreateDocument(); return doc && InitDocument(doc, path, flags) ? doc : NULL; } bool wxDocTemplate::InitDocument(wxDocument* doc, const wxString& path, long flags) { // Normally, if wxDocument::OnCreate() fails, it happens because the view // initialization fails and then the document is destroyed due to the // destruction of its last view. But take into account the (currently // unrealized, AFAICS) possibility of other failures as well and ensure // that the document is always destroyed if it can't be initialized. wxTRY { doc->SetFilename(path); doc->SetDocumentTemplate(this); GetDocumentManager()->AddDocument(doc); doc->SetCommandProcessor(doc->OnCreateCommandProcessor()); return doc->OnCreate(path, flags); } wxCATCH_ALL( if ( GetDocumentManager()->GetDocuments().Member(doc) ) doc->DeleteAllViews(); throw; ) } wxView *wxDocTemplate::CreateView(wxDocument *doc, long flags) { wxScopedPtr view(DoCreateView()); if ( !view ) return NULL; view->SetDocument(doc); if ( !view->OnCreate(doc, flags) ) return NULL; return view.release(); } // The default (very primitive) format detection: check is the extension is // that of the template bool wxDocTemplate::FileMatchesTemplate(const wxString& path) { wxStringTokenizer parser (GetFileFilter(), wxT(";")); wxString anything = wxT ("*"); while (parser.HasMoreTokens()) { wxString filter = parser.GetNextToken(); wxString filterExt = FindExtension (filter); if ( filter.IsSameAs (anything) || filterExt.IsSameAs (anything) || filterExt.IsSameAs (FindExtension (path)) ) return true; } return GetDefaultExtension().IsSameAs(FindExtension(path)); } wxDocument *wxDocTemplate::DoCreateDocument() { if (!m_docClassInfo) return NULL; return static_cast(m_docClassInfo->CreateObject()); } wxView *wxDocTemplate::DoCreateView() { if (!m_viewClassInfo) return NULL; return static_cast(m_viewClassInfo->CreateObject()); } // ---------------------------------------------------------------------------- // wxDocManager // ---------------------------------------------------------------------------- BEGIN_EVENT_TABLE(wxDocManager, wxEvtHandler) EVT_MENU(wxID_OPEN, wxDocManager::OnFileOpen) EVT_MENU(wxID_CLOSE, wxDocManager::OnFileClose) EVT_MENU(wxID_CLOSE_ALL, wxDocManager::OnFileCloseAll) EVT_MENU(wxID_REVERT, wxDocManager::OnFileRevert) EVT_MENU(wxID_NEW, wxDocManager::OnFileNew) EVT_MENU(wxID_SAVE, wxDocManager::OnFileSave) EVT_MENU(wxID_SAVEAS, wxDocManager::OnFileSaveAs) EVT_MENU(wxID_UNDO, wxDocManager::OnUndo) EVT_MENU(wxID_REDO, wxDocManager::OnRedo) // We don't know in advance how many items can there be in the MRU files // list so set up OnMRUFile() as a handler for all menu events and do the // check for the id of the menu item clicked inside it. EVT_MENU(wxID_ANY, wxDocManager::OnMRUFile) EVT_UPDATE_UI(wxID_OPEN, wxDocManager::OnUpdateFileOpen) EVT_UPDATE_UI(wxID_CLOSE, wxDocManager::OnUpdateDisableIfNoDoc) EVT_UPDATE_UI(wxID_CLOSE_ALL, wxDocManager::OnUpdateDisableIfNoDoc) EVT_UPDATE_UI(wxID_REVERT, wxDocManager::OnUpdateFileRevert) EVT_UPDATE_UI(wxID_NEW, wxDocManager::OnUpdateFileNew) EVT_UPDATE_UI(wxID_SAVE, wxDocManager::OnUpdateFileSave) EVT_UPDATE_UI(wxID_SAVEAS, wxDocManager::OnUpdateFileSaveAs) EVT_UPDATE_UI(wxID_UNDO, wxDocManager::OnUpdateUndo) EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo) #if wxUSE_PRINTING_ARCHITECTURE EVT_MENU(wxID_PRINT, wxDocManager::OnPrint) EVT_MENU(wxID_PREVIEW, wxDocManager::OnPreview) EVT_MENU(wxID_PRINT_SETUP, wxDocManager::OnPageSetup) EVT_UPDATE_UI(wxID_PRINT, wxDocManager::OnUpdateDisableIfNoDoc) EVT_UPDATE_UI(wxID_PREVIEW, wxDocManager::OnUpdateDisableIfNoDoc) // NB: we keep "Print setup" menu item always enabled as it can be used // even without an active document #endif // wxUSE_PRINTING_ARCHITECTURE END_EVENT_TABLE() wxDocManager* wxDocManager::sm_docManager = NULL; wxDocManager::wxDocManager(long WXUNUSED(flags), bool initialize) { sm_docManager = this; m_defaultDocumentNameCounter = 1; m_currentView = NULL; m_maxDocsOpen = INT_MAX; m_fileHistory = NULL; if ( initialize ) Initialize(); } wxDocManager::~wxDocManager() { Clear(); delete m_fileHistory; sm_docManager = NULL; } // closes the specified document bool wxDocManager::CloseDocument(wxDocument* doc, bool force) { if ( !doc->Close() && !force ) return false; // Implicitly deletes the document when // the last view is deleted doc->DeleteAllViews(); // Check we're really deleted if (m_docs.Member(doc)) delete doc; return true; } bool wxDocManager::CloseDocuments(bool force) { wxList::compatibility_iterator node = m_docs.GetFirst(); while (node) { wxDocument *doc = (wxDocument *)node->GetData(); wxList::compatibility_iterator next = node->GetNext(); if (!CloseDocument(doc, force)) return false; // This assumes that documents are not connected in // any way, i.e. deleting one document does NOT // delete another. node = next; } return true; } bool wxDocManager::Clear(bool force) { if (!CloseDocuments(force)) return false; m_currentView = NULL; wxList::compatibility_iterator node = m_templates.GetFirst(); while (node) { wxDocTemplate *templ = (wxDocTemplate*) node->GetData(); wxList::compatibility_iterator next = node->GetNext(); delete templ; node = next; } return true; } bool wxDocManager::Initialize() { m_fileHistory = OnCreateFileHistory(); return true; } wxString wxDocManager::GetLastDirectory() const { // if we haven't determined the last used directory yet, do it now if ( m_lastDirectory.empty() ) { // we're going to modify m_lastDirectory in this const method, so do it // via non-const self pointer instead of const this one wxDocManager * const self = const_cast(this); // first try to reuse the directory of the most recently opened file: // this ensures that if the user opens a file, closes the program and // runs it again the "Open file" dialog will open in the directory of // the last file he used if ( m_fileHistory && m_fileHistory->GetCount() ) { const wxString lastOpened = m_fileHistory->GetHistoryFile(0); const wxFileName fn(lastOpened); if ( fn.DirExists() ) { self->m_lastDirectory = fn.GetPath(); } //else: should we try the next one? } //else: no history yet // if we don't have any files in the history (yet?), use the // system-dependent default location for the document files if ( m_lastDirectory.empty() ) { self->m_lastDirectory = wxStandardPaths::Get().GetAppDocumentsDir(); } } return m_lastDirectory; } wxFileHistory *wxDocManager::OnCreateFileHistory() { return new wxFileHistory; } void wxDocManager::OnFileClose(wxCommandEvent& WXUNUSED(event)) { wxDocument *doc = GetCurrentDocument(); if (doc) CloseDocument(doc); } void wxDocManager::OnFileCloseAll(wxCommandEvent& WXUNUSED(event)) { CloseDocuments(false); } void wxDocManager::OnFileNew(wxCommandEvent& WXUNUSED(event)) { CreateNewDocument(); } void wxDocManager::OnFileOpen(wxCommandEvent& WXUNUSED(event)) { if ( !CreateDocument("") ) { OnOpenFileFailure(); } } void wxDocManager::OnFileRevert(wxCommandEvent& WXUNUSED(event)) { wxDocument *doc = GetCurrentDocument(); if (!doc) return; doc->Revert(); } void wxDocManager::OnFileSave(wxCommandEvent& WXUNUSED(event)) { wxDocument *doc = GetCurrentDocument(); if (!doc) return; doc->Save(); } void wxDocManager::OnFileSaveAs(wxCommandEvent& WXUNUSED(event)) { wxDocument *doc = GetCurrentDocument(); if (!doc) return; doc->SaveAs(); } void wxDocManager::OnMRUFile(wxCommandEvent& event) { // Check if the id is in the range assigned to MRU list entries. const int id = event.GetId(); if ( id >= wxID_FILE1 && id < wxID_FILE1 + static_cast(m_fileHistory->GetCount()) ) { DoOpenMRUFile(id - wxID_FILE1); } else { event.Skip(); } } void wxDocManager::DoOpenMRUFile(unsigned n) { wxString filename(GetHistoryFile(n)); if ( filename.empty() ) return; wxString errMsg; // must contain exactly one "%s" if non-empty if ( wxFile::Exists(filename) ) { // Try to open it but don't give an error if it failed: this could be // normal, e.g. because the user cancelled opening it, and we don't // have any useful information to put in the error message anyhow, so // we assume that in case of an error the appropriate message had been // already logged. (void)CreateDocument(filename, wxDOC_SILENT); } else // file doesn't exist { OnMRUFileNotExist(n, filename); } } void wxDocManager::OnMRUFileNotExist(unsigned n, const wxString& filename) { // remove the file which we can't open from the MRU list RemoveFileFromHistory(n); // and tell the user about it wxLogError(_("The file '%s' doesn't exist and couldn't be opened.\n" "It has been removed from the most recently used files list."), filename); } #if wxUSE_PRINTING_ARCHITECTURE void wxDocManager::OnPrint(wxCommandEvent& WXUNUSED(event)) { wxView *view = GetAnyUsableView(); if (!view) return; wxPrintout *printout = view->OnCreatePrintout(); if (printout) { wxPrintDialogData printDialogData(m_pageSetupDialogData.GetPrintData()); wxPrinter printer(&printDialogData); printer.Print(view->GetFrame(), printout, true); delete printout; } } void wxDocManager::OnPageSetup(wxCommandEvent& WXUNUSED(event)) { wxPageSetupDialog dlg(wxTheApp->GetTopWindow(), &m_pageSetupDialogData); if ( dlg.ShowModal() == wxID_OK ) { m_pageSetupDialogData = dlg.GetPageSetupData(); } } wxPreviewFrame* wxDocManager::CreatePreviewFrame(wxPrintPreviewBase* preview, wxWindow *parent, const wxString& title) { return new wxPreviewFrame(preview, parent, title); } void wxDocManager::OnPreview(wxCommandEvent& WXUNUSED(event)) { wxBusyCursor busy; wxView *view = GetAnyUsableView(); if (!view) return; wxPrintout *printout = view->OnCreatePrintout(); if (printout) { wxPrintDialogData printDialogData(m_pageSetupDialogData.GetPrintData()); // Pass two printout objects: for preview, and possible printing. wxPrintPreviewBase * preview = new wxPrintPreview(printout, view->OnCreatePrintout(), &printDialogData); if ( !preview->IsOk() ) { delete preview; wxLogError(_("Print preview creation failed.")); return; } wxPreviewFrame* frame = CreatePreviewFrame(preview, wxTheApp->GetTopWindow(), _("Print Preview")); wxCHECK_RET( frame, "should create a print preview frame" ); frame->Centre(wxBOTH); frame->Initialize(); frame->Show(true); } } #endif // wxUSE_PRINTING_ARCHITECTURE void wxDocManager::OnUndo(wxCommandEvent& event) { wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor(); if ( !cmdproc ) { event.Skip(); return; } cmdproc->Undo(); } void wxDocManager::OnRedo(wxCommandEvent& event) { wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor(); if ( !cmdproc ) { event.Skip(); return; } cmdproc->Redo(); } // Handlers for UI update commands void wxDocManager::OnUpdateFileOpen(wxUpdateUIEvent& event) { // CreateDocument() (which is called from OnFileOpen) may succeed // only when there is at least a template: event.Enable( GetTemplates().GetCount()>0 ); } void wxDocManager::OnUpdateDisableIfNoDoc(wxUpdateUIEvent& event) { event.Enable( GetCurrentDocument() != NULL ); } void wxDocManager::OnUpdateFileRevert(wxUpdateUIEvent& event) { wxDocument* doc = GetCurrentDocument(); event.Enable(doc && doc->IsModified() && doc->GetDocumentSaved()); } void wxDocManager::OnUpdateFileNew(wxUpdateUIEvent& event) { // CreateDocument() (which is called from OnFileNew) may succeed // only when there is at least a template: event.Enable( GetTemplates().GetCount()>0 ); } void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event) { wxDocument * const doc = GetCurrentDocument(); event.Enable( doc && !doc->IsChildDocument() && !doc->AlreadySaved() ); } void wxDocManager::OnUpdateFileSaveAs(wxUpdateUIEvent& event) { wxDocument * const doc = GetCurrentDocument(); event.Enable( doc && !doc->IsChildDocument() ); } void wxDocManager::OnUpdateUndo(wxUpdateUIEvent& event) { wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor(); if ( !cmdproc ) { // If we don't have any document at all, the menu item should really be // disabled. if ( !GetCurrentDocument() ) event.Enable(false); else // But if we do have it, it might handle wxID_UNDO on its own event.Skip(); return; } event.Enable(cmdproc->CanUndo()); cmdproc->SetMenuStrings(); } void wxDocManager::OnUpdateRedo(wxUpdateUIEvent& event) { wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor(); if ( !cmdproc ) { // Use same logic as in OnUpdateUndo() above. if ( !GetCurrentDocument() ) event.Enable(false); else event.Skip(); return; } event.Enable(cmdproc->CanRedo()); cmdproc->SetMenuStrings(); } wxView *wxDocManager::GetAnyUsableView() const { wxView *view = GetCurrentView(); if ( !view && !m_docs.empty() ) { // if we have exactly one document, consider its view to be the current // one // // VZ: I'm not exactly sure why is this needed but this is how this // code used to behave before the bug #9518 was fixed and it seems // safer to preserve the old logic wxList::compatibility_iterator node = m_docs.GetFirst(); if ( !node->GetNext() ) { wxDocument *doc = static_cast(node->GetData()); view = doc->GetFirstView(); } //else: we have more than one document } return view; } bool wxDocManager::TryBefore(wxEvent& event) { wxView * const view = GetAnyUsableView(); return view && view->ProcessEventLocally(event); } namespace { // helper function: return only the visible templates wxDocTemplateVector GetVisibleTemplates(const wxList& allTemplates) { // select only the visible templates const size_t totalNumTemplates = allTemplates.GetCount(); wxDocTemplateVector templates; if ( totalNumTemplates ) { templates.reserve(totalNumTemplates); for ( wxList::const_iterator i = allTemplates.begin(), end = allTemplates.end(); i != end; ++i ) { wxDocTemplate * const temp = (wxDocTemplate *)*i; if ( temp->IsVisible() ) templates.push_back(temp); } } return templates; } } // anonymous namespace void wxDocument::Activate() { wxView * const view = GetFirstView(); if ( !view ) return; view->Activate(true); if ( wxWindow *win = view->GetFrame() ) win->Raise(); } wxDocument* wxDocManager::FindDocumentByPath(const wxString& path) const { const wxFileName fileName(path); for ( wxList::const_iterator i = m_docs.begin(); i != m_docs.end(); ++i ) { wxDocument * const doc = wxStaticCast(*i, wxDocument); if ( fileName == wxFileName(doc->GetFilename()) ) return doc; } return NULL; } wxDocument *wxDocManager::CreateDocument(const wxString& pathOrig, long flags) { // this ought to be const but SelectDocumentType/Path() are not // const-correct and can't be changed as, being virtual, this risks // breaking user code overriding them wxDocTemplateVector templates(GetVisibleTemplates(m_templates)); const size_t numTemplates = templates.size(); if ( !numTemplates ) { // no templates can be used, can't create document return NULL; } // normally user should select the template to use but wxDOC_SILENT flag we // choose one ourselves wxString path = pathOrig; // may be modified below wxDocTemplate *temp; if ( flags & wxDOC_SILENT ) { wxASSERT_MSG( !path.empty(), "using empty path with wxDOC_SILENT doesn't make sense" ); temp = FindTemplateForPath(path); if ( !temp ) { wxLogWarning(_("The format of file '%s' couldn't be determined."), path); } } else // not silent, ask the user { // for the new file we need just the template, for an existing one we // need the template and the path, unless it's already specified if ( (flags & wxDOC_NEW) || !path.empty() ) temp = SelectDocumentType(&templates[0], numTemplates); else temp = SelectDocumentPath(&templates[0], numTemplates, path, flags); } if ( !temp ) return NULL; // check whether the document with this path is already opened if ( !path.empty() ) { wxDocument * const doc = FindDocumentByPath(path); if (doc) { // file already open, just activate it and return doc->Activate(); return doc; } } // no, we need to create a new document // if we've reached the max number of docs, close the first one. if ( (int)GetDocuments().GetCount() >= m_maxDocsOpen ) { if ( !CloseDocument((wxDocument *)GetDocuments().GetFirst()->GetData()) ) { // can't open the new document if closing the old one failed return NULL; } } // do create and initialize the new document finally wxDocument * const docNew = temp->CreateDocument(path, flags); if ( !docNew ) return NULL; docNew->SetDocumentName(temp->GetDocumentName()); wxTRY { // call the appropriate function depending on whether we're creating a // new file or opening an existing one if ( !(flags & wxDOC_NEW ? docNew->OnNewDocument() : docNew->OnOpenDocument(path)) ) { docNew->DeleteAllViews(); return NULL; } } wxCATCH_ALL( docNew->DeleteAllViews(); throw; ) // add the successfully opened file to MRU, but only if we're going to be // able to reopen it successfully later which requires the template for // this document to be retrievable from the file extension if ( !(flags & wxDOC_NEW) && temp->FileMatchesTemplate(path) ) AddFileToHistory(path); // at least under Mac (where views are top level windows) it seems to be // necessary to manually activate the new document to bring it to the // forefront -- and it shouldn't hurt doing this under the other platforms docNew->Activate(); return docNew; } wxView *wxDocManager::CreateView(wxDocument *doc, long flags) { wxDocTemplateVector templates(GetVisibleTemplates(m_templates)); const size_t numTemplates = templates.size(); if ( numTemplates == 0 ) return NULL; wxDocTemplate * const temp = numTemplates == 1 ? templates[0] : SelectViewType(&templates[0], numTemplates); if ( !temp ) return NULL; wxView *view = temp->CreateView(doc, flags); if ( view ) view->SetViewName(temp->GetViewName()); return view; } // Not yet implemented void wxDocManager::DeleteTemplate(wxDocTemplate *WXUNUSED(temp), long WXUNUSED(flags)) { } // Not yet implemented bool wxDocManager::FlushDoc(wxDocument *WXUNUSED(doc)) { return false; } wxDocument *wxDocManager::GetCurrentDocument() const { wxView * const view = GetAnyUsableView(); return view ? view->GetDocument() : NULL; } wxCommandProcessor *wxDocManager::GetCurrentCommandProcessor() const { wxDocument * const doc = GetCurrentDocument(); return doc ? doc->GetCommandProcessor() : NULL; } // Make a default name for a new document #if WXWIN_COMPATIBILITY_2_8 bool wxDocManager::MakeDefaultName(wxString& WXUNUSED(name)) { // we consider that this function can only be overridden by the user code, // not called by it as it only makes sense to call it internally, so we // don't bother to return anything from here return false; } #endif // WXWIN_COMPATIBILITY_2_8 wxString wxDocManager::MakeNewDocumentName() { wxString name; #if WXWIN_COMPATIBILITY_2_8 if ( !MakeDefaultName(name) ) #endif // WXWIN_COMPATIBILITY_2_8 { name.Printf(_("unnamed%d"), m_defaultDocumentNameCounter); m_defaultDocumentNameCounter++; } return name; } // Make a frame title (override this to do something different) // If docName is empty, a document is not currently active. wxString wxDocManager::MakeFrameTitle(wxDocument* doc) { wxString appName = wxTheApp->GetAppDisplayName(); wxString title; if (!doc) title = appName; else { wxString docName = doc->GetUserReadableName(); title = docName + wxString(_(" - ")) + appName; } return title; } // Not yet implemented wxDocTemplate *wxDocManager::MatchTemplate(const wxString& WXUNUSED(path)) { return NULL; } // File history management void wxDocManager::AddFileToHistory(const wxString& file) { if (m_fileHistory) m_fileHistory->AddFileToHistory(file); } void wxDocManager::RemoveFileFromHistory(size_t i) { if (m_fileHistory) m_fileHistory->RemoveFileFromHistory(i); } wxString wxDocManager::GetHistoryFile(size_t i) const { wxString histFile; if (m_fileHistory) histFile = m_fileHistory->GetHistoryFile(i); return histFile; } void wxDocManager::FileHistoryUseMenu(wxMenu *menu) { if (m_fileHistory) m_fileHistory->UseMenu(menu); } void wxDocManager::FileHistoryRemoveMenu(wxMenu *menu) { if (m_fileHistory) m_fileHistory->RemoveMenu(menu); } #if wxUSE_CONFIG void wxDocManager::FileHistoryLoad(const wxConfigBase& config) { if (m_fileHistory) m_fileHistory->Load(config); } void wxDocManager::FileHistorySave(wxConfigBase& config) { if (m_fileHistory) m_fileHistory->Save(config); } #endif void wxDocManager::FileHistoryAddFilesToMenu(wxMenu* menu) { if (m_fileHistory) m_fileHistory->AddFilesToMenu(menu); } void wxDocManager::FileHistoryAddFilesToMenu() { if (m_fileHistory) m_fileHistory->AddFilesToMenu(); } size_t wxDocManager::GetHistoryFilesCount() const { return m_fileHistory ? m_fileHistory->GetCount() : 0; } // Find out the document template via matching in the document file format // against that of the template wxDocTemplate *wxDocManager::FindTemplateForPath(const wxString& path) { wxDocTemplate *theTemplate = NULL; // Find the template which this extension corresponds to for (size_t i = 0; i < m_templates.GetCount(); i++) { wxDocTemplate *temp = (wxDocTemplate *)m_templates.Item(i)->GetData(); if ( temp->FileMatchesTemplate(path) ) { theTemplate = temp; break; } } return theTemplate; } // Prompts user to open a file, using file specs in templates. // Must extend the file selector dialog or implement own; OR // match the extension to the template extension. wxDocTemplate *wxDocManager::SelectDocumentPath(wxDocTemplate **templates, int noTemplates, wxString& path, long WXUNUSED(flags), bool WXUNUSED(save)) { #ifdef wxHAS_MULTIPLE_FILEDLG_FILTERS wxString descrBuf; for (int i = 0; i < noTemplates; i++) { if (templates[i]->IsVisible()) { // add a '|' to separate this filter from the previous one if ( !descrBuf.empty() ) descrBuf << wxT('|'); descrBuf << templates[i]->GetDescription() << wxT(" (") << templates[i]->GetFileFilter() << wxT(") |") << templates[i]->GetFileFilter(); } } #else wxString descrBuf = wxT("*.*"); wxUnusedVar(noTemplates); #endif int FilterIndex = -1; wxString pathTmp = wxFileSelectorEx(_("Open File"), GetLastDirectory(), wxEmptyString, &FilterIndex, descrBuf, wxFD_OPEN | wxFD_FILE_MUST_EXIST); wxDocTemplate *theTemplate = NULL; if (!pathTmp.empty()) { if (!wxFileExists(pathTmp)) { wxString msgTitle; if (!wxTheApp->GetAppDisplayName().empty()) msgTitle = wxTheApp->GetAppDisplayName(); else msgTitle = wxString(_("File error")); wxMessageBox(_("Sorry, could not open this file."), msgTitle, wxOK | wxICON_EXCLAMATION | wxCENTRE); path = wxEmptyString; return NULL; } SetLastDirectory(wxPathOnly(pathTmp)); path = pathTmp; // first choose the template using the extension, if this fails (i.e. // wxFileSelectorEx() didn't fill it), then use the path if ( FilterIndex != -1 ) theTemplate = templates[FilterIndex]; if ( !theTemplate ) theTemplate = FindTemplateForPath(path); if ( !theTemplate ) { // Since we do not add files with non-default extensions to the // file history this can only happen if the application changes the // allowed templates in runtime. wxMessageBox(_("Sorry, the format for this file is unknown."), _("Open File"), wxOK | wxICON_EXCLAMATION | wxCENTRE); } } else { path.clear(); } return theTemplate; } wxDocTemplate *wxDocManager::SelectDocumentType(wxDocTemplate **templates, int noTemplates, bool sort) { wxArrayString strings; wxScopedArray data(new wxDocTemplate *[noTemplates]); int i; int n = 0; for (i = 0; i < noTemplates; i++) { if (templates[i]->IsVisible()) { int j; bool want = true; for (j = 0; j < n; j++) { //filter out NOT unique documents + view combinations if ( templates[i]->m_docTypeName == data[j]->m_docTypeName && templates[i]->m_viewTypeName == data[j]->m_viewTypeName ) want = false; } if ( want ) { strings.Add(templates[i]->m_description); data[n] = templates[i]; n ++; } } } // for if (sort) { strings.Sort(); // ascending sort // Yes, this will be slow, but template lists // are typically short. int j; n = strings.Count(); for (i = 0; i < n; i++) { for (j = 0; j < noTemplates; j++) { if (strings[i] == templates[j]->m_description) data[i] = templates[j]; } } } wxDocTemplate *theTemplate; switch ( n ) { case 0: // no visible templates, hence nothing to choose from theTemplate = NULL; break; case 1: // don't propose the user to choose if he has no choice theTemplate = data[0]; break; default: // propose the user to choose one of several theTemplate = (wxDocTemplate *)wxGetSingleChoiceData ( _("Select a document template"), _("Templates"), strings, (void **)data.get() ); } return theTemplate; } wxDocTemplate *wxDocManager::SelectViewType(wxDocTemplate **templates, int noTemplates, bool sort) { wxArrayString strings; wxScopedArray data(new wxDocTemplate *[noTemplates]); int i; int n = 0; for (i = 0; i < noTemplates; i++) { wxDocTemplate *templ = templates[i]; if ( templ->IsVisible() && !templ->GetViewName().empty() ) { int j; bool want = true; for (j = 0; j < n; j++) { //filter out NOT unique views if ( templates[i]->m_viewTypeName == data[j]->m_viewTypeName ) want = false; } if ( want ) { strings.Add(templ->m_viewTypeName); data[n] = templ; n ++; } } } if (sort) { strings.Sort(); // ascending sort // Yes, this will be slow, but template lists // are typically short. int j; n = strings.Count(); for (i = 0; i < n; i++) { for (j = 0; j < noTemplates; j++) { if (strings[i] == templates[j]->m_viewTypeName) data[i] = templates[j]; } } } wxDocTemplate *theTemplate; // the same logic as above switch ( n ) { case 0: theTemplate = NULL; break; case 1: theTemplate = data[0]; break; default: theTemplate = (wxDocTemplate *)wxGetSingleChoiceData ( _("Select a document view"), _("Views"), strings, (void **)data.get() ); } return theTemplate; } void wxDocManager::AssociateTemplate(wxDocTemplate *temp) { if (!m_templates.Member(temp)) m_templates.Append(temp); } void wxDocManager::DisassociateTemplate(wxDocTemplate *temp) { m_templates.DeleteObject(temp); } wxDocTemplate* wxDocManager::FindTemplate(const wxClassInfo* classinfo) { for ( wxList::compatibility_iterator node = m_templates.GetFirst(); node; node = node->GetNext() ) { wxDocTemplate* t = wxStaticCast(node->GetData(), wxDocTemplate); if ( t->GetDocClassInfo() == classinfo ) return t; } return NULL; } // Add and remove a document from the manager's list void wxDocManager::AddDocument(wxDocument *doc) { if (!m_docs.Member(doc)) m_docs.Append(doc); } void wxDocManager::RemoveDocument(wxDocument *doc) { m_docs.DeleteObject(doc); } // Views or windows should inform the document manager // when a view is going in or out of focus void wxDocManager::ActivateView(wxView *view, bool activate) { if ( activate ) { m_currentView = view; } else // deactivate { if ( m_currentView == view ) { // don't keep stale pointer m_currentView = NULL; } } } // ---------------------------------------------------------------------------- // wxDocChildFrameAnyBase // ---------------------------------------------------------------------------- bool wxDocChildFrameAnyBase::TryProcessEvent(wxEvent& event) { if ( !m_childView ) { // We must be being destroyed, don't forward events anywhere as // m_childDocument could be invalid by now. return false; } // Store a (non-owning) pointer to the last processed event here to be able // to recognize this event again if it bubbles up to the parent frame, see // the code in wxDocParentFrameAnyBase::TryProcessEvent(). m_lastEvent = &event; // Forward the event to the document manager which will, in turn, forward // it to its active view which must be our m_childView. // // Notice that we do things in this roundabout way to guarantee the correct // event handlers call order: first the document, then the view and then the // document manager itself. And if we forwarded the event directly to the // view, then the document manager would do it once again when we forwarded // it to it. return m_childDocument->GetDocumentManager()->ProcessEventLocally(event); } bool wxDocChildFrameAnyBase::CloseView(wxCloseEvent& event) { if ( m_childView ) { // notice that we must call wxView::Close() and OnClose() called from // it in any case, even if we know that we are going to close anyhow if ( !m_childView->Close(false) && event.CanVeto() ) { event.Veto(); return false; } m_childView->Activate(false); // it is important to reset m_childView frame pointer to NULL before // deleting it because while normally it is the frame which deletes the // view when it's closed, the view also closes the frame if it is // deleted directly not by us as indicated by its doc child frame // pointer still being set m_childView->SetDocChildFrame(NULL); wxDELETE(m_childView); } m_childDocument = NULL; return true; } // ---------------------------------------------------------------------------- // wxDocParentFrameAnyBase // ---------------------------------------------------------------------------- bool wxDocParentFrameAnyBase::TryProcessEvent(wxEvent& event) { if ( !m_docManager ) return false; // If we have an active view, its associated child frame may have // already forwarded the event to wxDocManager, check for this: if ( wxView* const view = m_docManager->GetAnyUsableView() ) { wxDocChildFrameAnyBase* const childFrame = view->GetDocChildFrame(); if ( childFrame && childFrame->HasAlreadyProcessed(event) ) return false; } // But forward the event to wxDocManager ourselves if there are no views at // all or if this event hadn't been sent to the child frame previously. return m_docManager->ProcessEventLocally(event); } // ---------------------------------------------------------------------------- // Printing support // ---------------------------------------------------------------------------- #if wxUSE_PRINTING_ARCHITECTURE namespace { wxString GetAppropriateTitle(const wxView *view, const wxString& titleGiven) { wxString title(titleGiven); if ( title.empty() ) { if ( view && view->GetDocument() ) title = view->GetDocument()->GetUserReadableName(); else title = _("Printout"); } return title; } } // anonymous namespace wxDocPrintout::wxDocPrintout(wxView *view, const wxString& title) : wxPrintout(GetAppropriateTitle(view, title)) { m_printoutView = view; } bool wxDocPrintout::OnPrintPage(int WXUNUSED(page)) { wxDC *dc = GetDC(); // Get the logical pixels per inch of screen and printer int ppiScreenX, ppiScreenY; GetPPIScreen(&ppiScreenX, &ppiScreenY); wxUnusedVar(ppiScreenY); int ppiPrinterX, ppiPrinterY; GetPPIPrinter(&ppiPrinterX, &ppiPrinterY); wxUnusedVar(ppiPrinterY); // This scales the DC so that the printout roughly represents the // the screen scaling. The text point size _should_ be the right size // but in fact is too small for some reason. This is a detail that will // need to be addressed at some point but can be fudged for the // moment. float scale = (float)((float)ppiPrinterX/(float)ppiScreenX); // Now we have to check in case our real page size is reduced // (e.g. because we're drawing to a print preview memory DC) int pageWidth, pageHeight; int w, h; dc->GetSize(&w, &h); GetPageSizePixels(&pageWidth, &pageHeight); wxUnusedVar(pageHeight); // If printer pageWidth == current DC width, then this doesn't // change. But w might be the preview bitmap width, so scale down. float overallScale = scale * (float)(w/(float)pageWidth); dc->SetUserScale(overallScale, overallScale); if (m_printoutView) { m_printoutView->OnDraw(dc); } return true; } bool wxDocPrintout::HasPage(int pageNum) { return (pageNum == 1); } bool wxDocPrintout::OnBeginDocument(int startPage, int endPage) { if (!wxPrintout::OnBeginDocument(startPage, endPage)) return false; return true; } void wxDocPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo) { *minPage = 1; *maxPage = 1; *selPageFrom = 1; *selPageTo = 1; } #endif // wxUSE_PRINTING_ARCHITECTURE // ---------------------------------------------------------------------------- // Permits compatibility with existing file formats and functions that // manipulate files directly // ---------------------------------------------------------------------------- #if wxUSE_STD_IOSTREAM bool wxTransferFileToStream(const wxString& filename, wxSTD ostream& stream) { #if wxUSE_FFILE wxFFile file(filename, wxT("rb")); #elif wxUSE_FILE wxFile file(filename, wxFile::read); #endif if ( !file.IsOpened() ) return false; char buf[4096]; size_t nRead; do { nRead = file.Read(buf, WXSIZEOF(buf)); if ( file.Error() ) return false; stream.write(buf, nRead); if ( !stream ) return false; } while ( !file.Eof() ); return true; } bool wxTransferStreamToFile(wxSTD istream& stream, const wxString& filename) { #if wxUSE_FFILE wxFFile file(filename, wxT("wb")); #elif wxUSE_FILE wxFile file(filename, wxFile::write); #endif if ( !file.IsOpened() ) return false; char buf[4096]; do { stream.read(buf, WXSIZEOF(buf)); if ( !stream.bad() ) // fail may be set on EOF, don't use operator!() { if ( !file.Write(buf, stream.gcount()) ) return false; } } while ( !stream.eof() ); return true; } #else // !wxUSE_STD_IOSTREAM bool wxTransferFileToStream(const wxString& filename, wxOutputStream& stream) { #if wxUSE_FFILE wxFFile file(filename, wxT("rb")); #elif wxUSE_FILE wxFile file(filename, wxFile::read); #endif if ( !file.IsOpened() ) return false; char buf[4096]; size_t nRead; do { nRead = file.Read(buf, WXSIZEOF(buf)); if ( file.Error() ) return false; stream.Write(buf, nRead); if ( !stream ) return false; } while ( !file.Eof() ); return true; } bool wxTransferStreamToFile(wxInputStream& stream, const wxString& filename) { #if wxUSE_FFILE wxFFile file(filename, wxT("wb")); #elif wxUSE_FILE wxFile file(filename, wxFile::write); #endif if ( !file.IsOpened() ) return false; char buf[4096]; for ( ;; ) { stream.Read(buf, WXSIZEOF(buf)); const size_t nRead = stream.LastRead(); if ( !nRead ) { if ( stream.Eof() ) break; return false; } if ( !file.Write(buf, nRead) ) return false; } return true; } #endif // wxUSE_STD_IOSTREAM/!wxUSE_STD_IOSTREAM #endif // wxUSE_DOC_VIEW_ARCHITECTURE