From 4db97e24f5e2ad38970c55148ddb807399ecd191 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 27 Jun 2011 00:09:37 +0000 Subject: [PATCH] Add support for child documents to docview framework. Child documents are virtual documents corresponding to parts of their parent document which can't be saved nor loaded independently of their parent and are closed when the parent is closed. This finally makes some use of wxDocument::m_documentParent field which was always present in the docview code but never used before. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@68051 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/docview.h | 15 +++++++++ interface/wx/docview.h | 36 +++++++++++++++++++- samples/docview/doc.cpp | 28 ++++++++++++++- samples/docview/doc.h | 31 ++++++++++++++++- samples/docview/view.cpp | 73 ++++++++++++++++++++++++++++++++++++++++ samples/docview/view.h | 18 ++++++++++ src/common/docview.cpp | 59 ++++++++++++++++++++++++++++++-- 8 files changed, 255 insertions(+), 6 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 61dbacf733..57982bbed8 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -476,6 +476,7 @@ All (GUI): - wxHTML: render in RTL order inside RTL window (Richard Bullington-McGuire). - wxRibbon: added EVT_RIBBONGALLERY_CLICKED event (John Roberts). - wxRibbon: allow hiding the panels and showing tabs only (snowleopard). +- Add support for child documents to docview framework. - Add support for CP-866 encoding to wxEncodingConverter (madnut). - Consistency fixes for keyboard events across all major ports. - Added EVT_RIBBONBAR_TAB_LEFT_DCLICK event (snowleopard). diff --git a/include/wx/docview.h b/include/wx/docview.h index 1ed240f256..a6efd5dd10 100644 --- a/include/wx/docview.h +++ b/include/wx/docview.h @@ -17,6 +17,7 @@ #if wxUSE_DOC_VIEW_ARCHITECTURE #include "wx/list.h" +#include "wx/dlist.h" #include "wx/string.h" #include "wx/frame.h" #include "wx/filehistory.h" @@ -172,6 +173,10 @@ public: // dialogs. Override if necessary. virtual wxWindow *GetDocumentWindow() const; + // Returns true if this document is a child document corresponding to a + // part of the parent document and not a disk file as usual. + bool IsChildDocument() const { return m_documentParent != NULL; } + protected: wxList m_documentViews; wxString m_documentFile; @@ -179,7 +184,12 @@ protected: wxString m_documentTypeName; wxDocTemplate* m_documentTemplate; bool m_documentModified; + + // if the document parent is non-NULL, it's a pseudo-document corresponding + // to a part of the parent document which can't be saved or loaded + // independently of its parent and is always closed when its parent is wxDocument* m_documentParent; + wxCommandProcessor* m_commandProcessor; bool m_savedYet; @@ -193,6 +203,10 @@ protected: wxString DoGetUserReadableName() const; private: + // list of all documents whose m_documentParent is this one + typedef wxDList DocsList; + DocsList m_childDocuments; + DECLARE_ABSTRACT_CLASS(wxDocument) wxDECLARE_NO_COPY_CLASS(wxDocument); }; @@ -386,6 +400,7 @@ public: void OnUpdateFileRevert(wxUpdateUIEvent& event); void OnUpdateFileNew(wxUpdateUIEvent& event); void OnUpdateFileSave(wxUpdateUIEvent& event); + void OnUpdateFileSaveAs(wxUpdateUIEvent& event); void OnUpdateUndo(wxUpdateUIEvent& event); void OnUpdateRedo(wxUpdateUIEvent& event); diff --git a/interface/wx/docview.h b/interface/wx/docview.h index 4d2cd89bfe..c33bf8279e 100644 --- a/interface/wx/docview.h +++ b/interface/wx/docview.h @@ -1130,9 +1130,25 @@ public: @class wxDocument The document class can be used to model an application's file-based data. + It is part of the document/view framework supported by wxWidgets, and cooperates with the wxView, wxDocTemplate and wxDocManager classes. + A normal document is the one created without parent document and is + associated with a disk file. Since version 2.9.2 wxWidgets also supports a + special kind of documents called child documents which are virtual + in the sense that they do not correspond to a file but rather to a part of + their parent document. Because of this, the child documents can't be + created directly by user but can only be created by the parent document + (usually when it's being created itself). They also can't be independently + saved. A child document has its own view with the corresponding window. + This view can be closed by user but, importantly, is also automatically + closed when its parent document is closed. Thus, child documents may be + convenient for creating additional windows which need to be closed when the + main document is. The docview sample demonstrates this use of child + documents by creating a child document containing the information about the + parameters of the image opened in the main document. + @library{wxcore} @category{docview} @@ -1144,8 +1160,14 @@ public: /** Constructor. Define your own default constructor to initialize application-specific data. + + @param parent + Specifying a non-@c NULL parent document here makes this document a + special child document, see their description in the class + documentation. Notice that this parameter exists but is ignored in + wxWidgets versions prior to 2.9.1. */ - wxDocument(wxDocument* parent = 0); + wxDocument(wxDocument* parent = NULL); /** Destructor. Removes itself from the document manager. @@ -1278,6 +1300,18 @@ public: const wxList& GetViews() const; //@} + /** + Returns true if this document is a child document corresponding to a + part of the parent document and not a disk file as usual. + + This method can be used to check whether file-related operations make + sense for this document as they only apply to top-level documents and + not child ones. + + @since 2.9.2 + */ + bool IsChildDocument() const; + /** Returns @true if the document has been modified since the last save, @false otherwise. You may need to override this if your document view diff --git a/samples/docview/doc.cpp b/samples/docview/doc.cpp index 713a76d569..24f3984b16 100644 --- a/samples/docview/doc.cpp +++ b/samples/docview/doc.cpp @@ -250,7 +250,7 @@ wxTextCtrl* TextEditDocument::GetTextCtrl() const } // ---------------------------------------------------------------------------- -// ImageDocument and wxImageDetailsDocument implementation +// ImageDocument and ImageDetailsDocument implementation // ---------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(ImageDocument, wxDocument) @@ -260,3 +260,29 @@ bool ImageDocument::DoOpenDocument(const wxString& file) return m_image.LoadFile(file); } +bool ImageDocument::OnOpenDocument(const wxString& filename) +{ + if ( !wxDocument::OnOpenDocument(filename) ) + return false; + + // we don't have a wxDocTemplate for the image details document as it's + // never created by wxWidgets automatically, instead just do it manually + ImageDetailsDocument * const docDetails = new ImageDetailsDocument(this); + docDetails->SetFilename(filename); + + new ImageDetailsView(docDetails); + + return true; +} + +ImageDetailsDocument::ImageDetailsDocument(ImageDocument *parent) + : wxDocument(parent) +{ + const wxImage image = parent->GetImage(); + + m_size.x = image.GetWidth(); + m_size.y = image.GetHeight(); + m_numColours = image.CountColours(); + m_type = image.GetType(); + m_hasAlpha = image.HasAlpha(); +} diff --git a/samples/docview/doc.h b/samples/docview/doc.h index d79f8275de..c654398222 100644 --- a/samples/docview/doc.h +++ b/samples/docview/doc.h @@ -198,14 +198,19 @@ public: }; // ---------------------------------------------------------------------------- -// A document class representing an image +// Image and image details document classes (both are read-only for simplicity) // ---------------------------------------------------------------------------- +// This is a normal document containing an image, just like TextEditDocument +// above contains some text. It can be created from an image file on disk as +// usual. class ImageDocument : public wxDocument { public: ImageDocument() : wxDocument() { } + virtual bool OnOpenDocument(const wxString& file); + wxImage GetImage() const { return m_image; } protected: @@ -218,4 +223,28 @@ private: DECLARE_DYNAMIC_CLASS(ImageDocument) }; +// This is a child document of ImageDocument: this document doesn't +// correspond to any file on disk, it's part of ImageDocument and can't be +// instantiated independently of it. +class ImageDetailsDocument : public wxDocument +{ +public: + ImageDetailsDocument(ImageDocument *parent); + + // accessors for ImageDetailsView + wxSize GetSize() const { return m_size; } + unsigned long GetNumColours() const { return m_numColours; } + wxBitmapType GetType() const { return m_type; } + bool HasAlpha() const { return m_hasAlpha; } + +private: + // some information about the image we choose to show to the user + wxSize m_size; + unsigned long m_numColours; + wxBitmapType m_type; + bool m_hasAlpha; + + wxDECLARE_NO_COPY_CLASS(ImageDetailsDocument); +}; + #endif // _WX_SAMPLES_DOCVIEW_DOC_H_ diff --git a/samples/docview/view.cpp b/samples/docview/view.cpp index f4e4462f5a..555368aa7b 100644 --- a/samples/docview/view.cpp +++ b/samples/docview/view.cpp @@ -362,3 +362,76 @@ bool ImageView::OnClose(bool deleteWindow) return true; } +// ---------------------------------------------------------------------------- +// ImageDetailsView +// ---------------------------------------------------------------------------- + +ImageDetailsView::ImageDetailsView(ImageDetailsDocument *doc) + : wxView() +{ + SetDocument(doc); + + m_frame = wxGetApp().CreateChildFrame(this, false); + m_frame->SetTitle("Image Details"); + + wxPanel * const panel = new wxPanel(m_frame); + wxFlexGridSizer * const sizer = new wxFlexGridSizer(2, wxSize(5, 5)); + const wxSizerFlags + flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border(); + + sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &file:"), flags); + sizer->Add(new wxStaticText(panel, wxID_ANY, doc->GetFilename()), flags); + + sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &type:"), flags); + wxString typeStr; + switch ( doc->GetType() ) + { + case wxBITMAP_TYPE_PNG: + typeStr = "PNG"; + break; + + case wxBITMAP_TYPE_JPEG: + typeStr = "JPEG"; + break; + + default: + typeStr = "Unknown"; + } + sizer->Add(new wxStaticText(panel, wxID_ANY, typeStr), flags); + + sizer->Add(new wxStaticText(panel, wxID_ANY, "Image &size:"), flags); + wxSize size = doc->GetSize(); + sizer->Add(new wxStaticText(panel, wxID_ANY, + wxString::Format("%d*%d", size.x, size.y)), + flags); + + sizer->Add(new wxStaticText(panel, wxID_ANY, "Number of unique &colours:"), + flags); + sizer->Add(new wxStaticText(panel, wxID_ANY, + wxString::Format("%lu", doc->GetNumColours())), + flags); + + sizer->Add(new wxStaticText(panel, wxID_ANY, "Uses &alpha:"), flags); + sizer->Add(new wxStaticText(panel, wxID_ANY, + doc->HasAlpha() ? "Yes" : "No"), flags); + + panel->SetSizer(sizer); + m_frame->SetClientSize(panel->GetBestSize()); + m_frame->Show(true); +} + +void ImageDetailsView::OnDraw(wxDC * WXUNUSED(dc)) +{ + // nothing to do here, we use controls to show our information +} + +bool ImageDetailsView::OnClose(bool deleteWindow) +{ + if ( wxGetApp().GetMode() != MyApp::Mode_Single && deleteWindow ) + { + delete m_frame; + m_frame = NULL; + } + + return true; +} diff --git a/samples/docview/view.h b/samples/docview/view.h index 8bbc37c2ba..3ba84d8847 100644 --- a/samples/docview/view.h +++ b/samples/docview/view.h @@ -148,4 +148,22 @@ private: DECLARE_DYNAMIC_CLASS(ImageView) }; +// ---------------------------------------------------------------------------- +// ImageDetailsView +// ---------------------------------------------------------------------------- + +class ImageDetailsView : public wxView +{ +public: + ImageDetailsView(ImageDetailsDocument *doc); + + virtual void OnDraw(wxDC *dc); + virtual bool OnClose(bool deleteWindow); + +private: + wxFrame *m_frame; + + wxDECLARE_NO_COPY_CLASS(ImageDetailsView); +}; + #endif // _WX_SAMPLES_DOCVIEW_VIEW_H_ diff --git a/src/common/docview.cpp b/src/common/docview.cpp index 34df0b1c27..6d6950b008 100644 --- a/src/common/docview.cpp +++ b/src/common/docview.cpp @@ -122,8 +122,12 @@ wxString FindExtension(const wxString& path) 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; } @@ -140,6 +144,9 @@ wxDocument::~wxDocument() 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. @@ -151,6 +158,40 @@ 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(); } @@ -231,6 +272,12 @@ void wxDocument::Modify(bool mod) 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; } @@ -895,7 +942,7 @@ BEGIN_EVENT_TABLE(wxDocManager, wxEvtHandler) 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) @@ -1254,7 +1301,13 @@ void wxDocManager::OnUpdateFileNew(wxUpdateUIEvent& event) 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) -- 2.45.2