// Author: Julian Smart
// Modified by: Vadim Zeitlin
// Created: 01/02/97
-// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#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/wfstream.h"
#endif
-typedef wxVector<wxDocTemplate *> wxDocTemplates;
-
// ----------------------------------------------------------------------------
// wxWidgets macros
// ----------------------------------------------------------------------------
wxDocument::wxDocument(wxDocument *parent)
{
m_documentModified = false;
- m_documentParent = parent;
m_documentTemplate = NULL;
+
+ m_documentParent = parent;
+ if ( parent )
+ parent->m_childDocuments.push_back(this);
+
m_commandProcessor = NULL;
m_savedYet = false;
}
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.
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();
}
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;
}
if ( !DoSaveDocument(file) )
return false;
+ if ( m_commandProcessor )
+ m_commandProcessor->MarkAsSaved();
+
Modify(false);
SetFilename(file);
SetDocumentSaved(true);
#if WXWIN_COMPATIBILITY_2_8
bool wxDocument::GetPrintableName(wxString& buf) const
{
- // this function can not only be overridden by the user code but also
+ // 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()
bool
wxDocTemplate::InitDocument(wxDocument* doc, const wxString& path, long flags)
{
- doc->SetFilename(path);
- doc->SetDocumentTemplate(this);
- GetDocumentManager()->AddDocument(doc);
- doc->SetCommandProcessor(doc->OnCreateCommandProcessor());
-
- if (doc->OnCreate(path, flags))
- return true;
+ // 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());
- if (GetDocumentManager()->GetDocuments().Member(doc))
- doc->DeleteAllViews();
- return false;
+ return doc->OnCreate(path, flags);
+ }
+ wxCATCH_ALL(
+ if ( GetDocumentManager()->GetDocuments().Member(doc) )
+ doc->DeleteAllViews();
+ throw;
+ )
}
wxView *wxDocTemplate::CreateView(wxDocument *doc, long flags)
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::OnUpdateDisableIfNoDoc)
+ EVT_UPDATE_UI(wxID_SAVEAS, wxDocManager::OnUpdateFileSaveAs)
EVT_UPDATE_UI(wxID_UNDO, wxDocManager::OnUpdateUndo)
EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo)
wxString errMsg; // must contain exactly one "%s" if non-empty
if ( wxFile::Exists(filename) )
{
- // try to open it
- if ( CreateDocument(filename, wxDOC_SILENT) )
- return;
-
- errMsg = _("The file '%s' couldn't be opened.");
+ // 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
{
- errMsg = _("The file '%s' doesn't exist and couldn't be opened.");
+ OnMRUFileNotExist(n, filename);
}
+}
-
- wxASSERT_MSG( !errMsg.empty(), "should have an error message" );
-
+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(errMsg + '\n' +
- _("It has been removed from the most recently used files list."),
+ 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);
}
void wxDocManager::OnPrint(wxCommandEvent& WXUNUSED(event))
{
- wxView *view = GetActiveView();
+ wxView *view = GetAnyUsableView();
if (!view)
return;
void wxDocManager::OnPreview(wxCommandEvent& WXUNUSED(event))
{
wxBusyCursor busy;
- wxView *view = GetActiveView();
+ wxView *view = GetAnyUsableView();
if (!view)
return;
preview = new wxPrintPreview(printout,
view->OnCreatePrintout(),
&printDialogData);
- if ( !preview->Ok() )
+ if ( !preview->IsOk() )
{
delete preview;
wxLogError(_("Print preview creation failed."));
void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event)
{
wxDocument * const doc = GetCurrentDocument();
- event.Enable( doc && !doc->AlreadySaved() );
+ 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 )
{
- event.Enable(false);
+ // 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();
}
wxCommandProcessor * const cmdproc = GetCurrentCommandProcessor();
if ( !cmdproc )
{
- event.Enable(false);
+ // Use same logic as in OnUpdateUndo() above.
+ if ( !GetCurrentDocument() )
+ event.Enable(false);
+ else
+ event.Skip();
return;
}
-
event.Enable(cmdproc->CanRedo());
cmdproc->SetMenuStrings();
}
-wxView *wxDocManager::GetActiveView() const
+wxView *wxDocManager::GetAnyUsableView() const
{
wxView *view = GetCurrentView();
bool wxDocManager::TryBefore(wxEvent& event)
{
- wxView * const view = GetActiveView();
+ wxView * const view = GetAnyUsableView();
return view && view->ProcessEventLocally(event);
}
{
// helper function: return only the visible templates
-wxDocTemplates GetVisibleTemplates(const wxList& allTemplates)
+wxDocTemplateVector GetVisibleTemplates(const wxList& allTemplates)
{
// select only the visible templates
const size_t totalNumTemplates = allTemplates.GetCount();
- wxDocTemplates templates;
+ wxDocTemplateVector templates;
if ( totalNumTemplates )
{
templates.reserve(totalNumTemplates);
} // anonymous namespace
-void wxDocManager::ActivateDocument(wxDocument *doc)
+void wxDocument::Activate()
{
- wxView * const view = doc->GetFirstView();
+ wxView * const view = GetFirstView();
if ( !view )
return;
view->Activate(true);
if ( wxWindow *win = view->GetFrame() )
- win->SetFocus();
+ 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
- wxDocTemplates templates(GetVisibleTemplates(m_templates));
+ wxDocTemplateVector templates(GetVisibleTemplates(m_templates));
const size_t numTemplates = templates.size();
if ( !numTemplates )
{
// check whether the document with this path is already opened
if ( !path.empty() )
{
- const wxFileName fn(path);
- for ( wxList::const_iterator i = m_docs.begin(); i != m_docs.end(); ++i )
+ wxDocument * const doc = FindDocumentByPath(path);
+ if (doc)
{
- wxDocument * const doc = (wxDocument*)*i;
-
- if ( fn == doc->GetFilename() )
- {
- // file already open, just activate it and return
- ActivateDocument(doc);
- return doc;
- }
+ // file already open, just activate it and return
+ doc->Activate();
+ return doc;
}
}
-
// no, we need to create a new document
return NULL;
docNew->SetDocumentName(temp->GetDocumentName());
- docNew->SetDocumentTemplate(temp);
wxTRY
{
// 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
- ActivateDocument(docNew);
+ docNew->Activate();
return docNew;
}
wxView *wxDocManager::CreateView(wxDocument *doc, long flags)
{
- wxDocTemplates templates(GetVisibleTemplates(m_templates));
+ wxDocTemplateVector templates(GetVisibleTemplates(m_templates));
const size_t numTemplates = templates.size();
if ( numTemplates == 0 )
wxDocument *wxDocManager::GetCurrentDocument() const
{
- wxView * const view = GetActiveView();
+ wxView * const view = GetAnyUsableView();
return view ? view->GetDocument() : 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;
+ }
+
+ // 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 new 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 )
// 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() )
+ {
+ wxWindow* win = view->GetFrame();
+ if ( win && win != m_frame )
+ {
+ // Notice that we intentionally don't use wxGetTopLevelParent()
+ // here because we want to check both for the case of a child
+ // "frame" (e.g. MDI child frame or notebook page) inside this TLW
+ // and a separate child TLW frame (as used in the SDI mode) here.
+ for ( win = win->GetParent(); win; win = win->GetParent() )
+ {
+ if ( win == m_frame )
+ return false;
+ }
+ }
+ //else: This view is directly associated with the parent frame (which
+ // can happen in the so called "single" mode in which only one
+ // document can be opened and so is managed by the parent frame
+ // itself), there can be no child frame in play so we must forward
+ // the event to wxDocManager ourselves.
+ }
+
+ // But forward the event to wxDocManager ourselves if there are no views at
+ // all or if we are the frame's view ourselves.
+ 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(title)
+ : wxPrintout(GetAppropriateTitle(view, title))
{
m_printoutView = view;
}