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
This commit is contained in:
Vadim Zeitlin
2011-06-27 00:09:37 +00:00
parent 9f3b6638e4
commit 4db97e24f5
8 changed files with 255 additions and 6 deletions

View File

@@ -476,6 +476,7 @@ All (GUI):
- wxHTML: render in RTL order inside RTL window (Richard Bullington-McGuire). - wxHTML: render in RTL order inside RTL window (Richard Bullington-McGuire).
- wxRibbon: added EVT_RIBBONGALLERY_CLICKED event (John Roberts). - wxRibbon: added EVT_RIBBONGALLERY_CLICKED event (John Roberts).
- wxRibbon: allow hiding the panels and showing tabs only (snowleopard). - 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). - Add support for CP-866 encoding to wxEncodingConverter (madnut).
- Consistency fixes for keyboard events across all major ports. - Consistency fixes for keyboard events across all major ports.
- Added EVT_RIBBONBAR_TAB_LEFT_DCLICK event (snowleopard). - Added EVT_RIBBONBAR_TAB_LEFT_DCLICK event (snowleopard).

View File

@@ -17,6 +17,7 @@
#if wxUSE_DOC_VIEW_ARCHITECTURE #if wxUSE_DOC_VIEW_ARCHITECTURE
#include "wx/list.h" #include "wx/list.h"
#include "wx/dlist.h"
#include "wx/string.h" #include "wx/string.h"
#include "wx/frame.h" #include "wx/frame.h"
#include "wx/filehistory.h" #include "wx/filehistory.h"
@@ -172,6 +173,10 @@ public:
// dialogs. Override if necessary. // dialogs. Override if necessary.
virtual wxWindow *GetDocumentWindow() const; 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: protected:
wxList m_documentViews; wxList m_documentViews;
wxString m_documentFile; wxString m_documentFile;
@@ -179,7 +184,12 @@ protected:
wxString m_documentTypeName; wxString m_documentTypeName;
wxDocTemplate* m_documentTemplate; wxDocTemplate* m_documentTemplate;
bool m_documentModified; 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; wxDocument* m_documentParent;
wxCommandProcessor* m_commandProcessor; wxCommandProcessor* m_commandProcessor;
bool m_savedYet; bool m_savedYet;
@@ -193,6 +203,10 @@ protected:
wxString DoGetUserReadableName() const; wxString DoGetUserReadableName() const;
private: private:
// list of all documents whose m_documentParent is this one
typedef wxDList<wxDocument> DocsList;
DocsList m_childDocuments;
DECLARE_ABSTRACT_CLASS(wxDocument) DECLARE_ABSTRACT_CLASS(wxDocument)
wxDECLARE_NO_COPY_CLASS(wxDocument); wxDECLARE_NO_COPY_CLASS(wxDocument);
}; };
@@ -386,6 +400,7 @@ public:
void OnUpdateFileRevert(wxUpdateUIEvent& event); void OnUpdateFileRevert(wxUpdateUIEvent& event);
void OnUpdateFileNew(wxUpdateUIEvent& event); void OnUpdateFileNew(wxUpdateUIEvent& event);
void OnUpdateFileSave(wxUpdateUIEvent& event); void OnUpdateFileSave(wxUpdateUIEvent& event);
void OnUpdateFileSaveAs(wxUpdateUIEvent& event);
void OnUpdateUndo(wxUpdateUIEvent& event); void OnUpdateUndo(wxUpdateUIEvent& event);
void OnUpdateRedo(wxUpdateUIEvent& event); void OnUpdateRedo(wxUpdateUIEvent& event);

View File

@@ -1130,9 +1130,25 @@ public:
@class wxDocument @class wxDocument
The document class can be used to model an application's file-based data. 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 It is part of the document/view framework supported by wxWidgets, and
cooperates with the wxView, wxDocTemplate and wxDocManager classes. 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} @library{wxcore}
@category{docview} @category{docview}
@@ -1144,8 +1160,14 @@ public:
/** /**
Constructor. Define your own default constructor to initialize Constructor. Define your own default constructor to initialize
application-specific data. 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. Destructor. Removes itself from the document manager.
@@ -1278,6 +1300,18 @@ public:
const wxList& GetViews() const; 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, Returns @true if the document has been modified since the last save,
@false otherwise. You may need to override this if your document view @false otherwise. You may need to override this if your document view

View File

@@ -250,7 +250,7 @@ wxTextCtrl* TextEditDocument::GetTextCtrl() const
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ImageDocument and wxImageDetailsDocument implementation // ImageDocument and ImageDetailsDocument implementation
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(ImageDocument, wxDocument) IMPLEMENT_DYNAMIC_CLASS(ImageDocument, wxDocument)
@@ -260,3 +260,29 @@ bool ImageDocument::DoOpenDocument(const wxString& file)
return m_image.LoadFile(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();
}

View 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 class ImageDocument : public wxDocument
{ {
public: public:
ImageDocument() : wxDocument() { } ImageDocument() : wxDocument() { }
virtual bool OnOpenDocument(const wxString& file);
wxImage GetImage() const { return m_image; } wxImage GetImage() const { return m_image; }
protected: protected:
@@ -218,4 +223,28 @@ private:
DECLARE_DYNAMIC_CLASS(ImageDocument) 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_ #endif // _WX_SAMPLES_DOCVIEW_DOC_H_

View File

@@ -362,3 +362,76 @@ bool ImageView::OnClose(bool deleteWindow)
return true; 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;
}

View File

@@ -148,4 +148,22 @@ private:
DECLARE_DYNAMIC_CLASS(ImageView) 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_ #endif // _WX_SAMPLES_DOCVIEW_VIEW_H_

View File

@@ -122,8 +122,12 @@ wxString FindExtension(const wxString& path)
wxDocument::wxDocument(wxDocument *parent) wxDocument::wxDocument(wxDocument *parent)
{ {
m_documentModified = false; m_documentModified = false;
m_documentParent = parent;
m_documentTemplate = NULL; m_documentTemplate = NULL;
m_documentParent = parent;
if ( parent )
parent->m_childDocuments.push_back(this);
m_commandProcessor = NULL; m_commandProcessor = NULL;
m_savedYet = false; m_savedYet = false;
} }
@@ -140,6 +144,9 @@ wxDocument::~wxDocument()
if (GetDocumentManager()) if (GetDocumentManager())
GetDocumentManager()->RemoveDocument(this); GetDocumentManager()->RemoveDocument(this);
if ( m_documentParent )
m_documentParent->m_childDocuments.remove(this);
// Not safe to do here, since it'll invoke virtual view functions // 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, // expecting to see valid derived objects: and by the time we get here,
// we've called destructors higher up. // we've called destructors higher up.
@@ -151,6 +158,40 @@ bool wxDocument::Close()
if ( !OnSaveModified() ) if ( !OnSaveModified() )
return false; 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(); return OnCloseDocument();
} }
@@ -231,6 +272,12 @@ void wxDocument::Modify(bool mod)
wxDocManager *wxDocument::GetDocumentManager() const 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; 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_REVERT, wxDocManager::OnUpdateFileRevert)
EVT_UPDATE_UI(wxID_NEW, wxDocManager::OnUpdateFileNew) EVT_UPDATE_UI(wxID_NEW, wxDocManager::OnUpdateFileNew)
EVT_UPDATE_UI(wxID_SAVE, wxDocManager::OnUpdateFileSave) 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_UNDO, wxDocManager::OnUpdateUndo)
EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo) EVT_UPDATE_UI(wxID_REDO, wxDocManager::OnUpdateRedo)
@@ -1254,7 +1301,13 @@ void wxDocManager::OnUpdateFileNew(wxUpdateUIEvent& event)
void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event) void wxDocManager::OnUpdateFileSave(wxUpdateUIEvent& event)
{ {
wxDocument * const doc = GetCurrentDocument(); 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) void wxDocManager::OnUpdateUndo(wxUpdateUIEvent& event)