]> git.saurik.com Git - wxWidgets.git/commitdiff
Add support for child documents to docview framework.
authorVadim Zeitlin <vadim@wxwidgets.org>
Mon, 27 Jun 2011 00:09:37 +0000 (00:09 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Mon, 27 Jun 2011 00:09:37 +0000 (00:09 +0000)
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
include/wx/docview.h
interface/wx/docview.h
samples/docview/doc.cpp
samples/docview/doc.h
samples/docview/view.cpp
samples/docview/view.h
src/common/docview.cpp

index 61dbacf73337e2dfa598704013a9829cbaa06659..57982bbed8dc47c124931517c68b1829faa19efe 100644 (file)
@@ -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).
index 1ed240f256729127bb3cb55a8a08b2f0c8d0ab8a..a6efd5dd107df22f3a119d12005fc5b3e6bd6bca 100644 (file)
@@ -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<wxDocument> 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);
 
index 4d2cd89bfe8a2be60ad9588005d17ffda134c6d7..c33bf8279ed39a8947cc229fc82aebbe6c0d5c90 100644 (file)
@@ -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 <em>child documents</em> 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 <em>child document</em>, 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
index 713a76d569786199611053feb0e2419740d505e7..24f3984b165033c74cc83840cbe4ea1c921c47f7 100644 (file)
@@ -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();
+}
index d79f8275de31b43f0a7376e7dd6b8098b5140059..c65439822267db7742eb5ad1aacfd33a2e5b68ae 100644 (file)
@@ -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_
index f4e4462f5a4ac097d26ef1e3ac67807ccb5adf2a..555368aa7bb9d9ed4711415a0506d55e383fe2fb 100644 (file)
@@ -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;
+}
index 8bbc37c2baae52234184688a03b4dfbf56a5b82d..3ba84d88476509d50e0d2cd40a7ca42167b2690a 100644 (file)
@@ -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_
index 34df0b1c277cf538f86387eca2540c97e8000023..6d6950b008e1b287239c5166e34595275edf7b5f 100644 (file)
@@ -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)