Getting Acquainted with Document-View architecture – Part II – Simple Text Editor

Today we’ll dig a little bit deeper into Document/View framework provided by wxWidgets and will see how to create a simple text editor using this framework.
We’ll take the source code from the previous article of this series and add some modifications. You will see below that modifications are rather simple and take almost no time.
First of all we have to make wxDocTemplate to handle desired file extensions (in our case it’s TXT).

DocViewTestApp.cpp

bool DocViewTestApp::OnInit()
{
	...
	wxDocTemplate * docTemplate = new 
		wxDocTemplate(m_DocManager, _("DocViewTest Document"), 
		wxT("*.txt"), wxEmptyString,
		wxT("txt"), wxT("DocViewTest Doc"), wxT("DocViewTest View"),
		CLASSINFO(DocViewTestDocument), CLASSINFO(DocViewTestView));
...
}

As you can see, this task is very simple. We just changed the filter and default file extension parameter of wxDocTemplate.
After that we have to add a wxTextCtrl to our main frame.

DocViewTestMainFrame.h

class DocViewTestMainFrame : public wxDocParentFrame
{
...
public:
	enum
	{
		ID_EDITOR = wxID_HIGHEST+1
	};
	wxTextCtrl * m_Editor;
	...
};

DocViewTestMainFrame.cpp

void DocViewTestMainFrame::CreateControls()
{
	SetMenuBar(CreateMenuBar());
	m_AuiManager.SetManagedWindow(this);
	
	m_Editor = new wxTextCtrl(this, ID_EDITOR, wxEmptyString, wxDefaultPosition, 
		wxDefaultSize, wxTE_MULTILINE);

	m_AuiManager.AddPane(m_Editor, wxAuiPaneInfo().CenterPane().Name(_("Editor")));

	m_AuiManager.Update();
}

So, we added a new class member variable of wxTextCtrl type which represents the text editor.
Now we need to add the text editor accessible from Document class. Document objects can’t communicate with frame directly. They can access only views associated with them. So now we have to add a new method to our View class which will return the text editor associated with this view. Let’s call this method GetEditor().

DocViewTestView.h

class DocViewTestView : public wxView
{
...
public:
	...
	wxTextCtrl * GetEditor();
...
};

DocViewTestView.cpp

wxTextCtrl * DocViewTestView::GetEditor()
{
	do
	{
		DocViewTestMainFrame * frame = 
			wxDynamicCast(GetFrame(), DocViewTestMainFrame);
		if(!frame) break;
		return frame->m_Editor;
	}
	while(false);
	return NULL;
}

Perfectly, now we have to make our document class load and save files and also we have to add methods which will allow to set/retrieve the state of the document. These methods are Modify() and IsModified().

DocViewTestDocument.h

class DocViewTestDocument : public wxDocument
{
	DECLARE_DYNAMIC_CLASS(DocViewTestDocument)
public:
	DocViewTestDocument();
	virtual bool OnSaveDocument(const wxString& filename);
	virtual bool OnOpenDocument(const wxString& filename);
	virtual bool IsModified(void) const;
	virtual void Modify(bool mod);
};

DocViewTestDocument.cpp

bool DocViewTestDocument::IsModified(void) const
{
	bool res = wxDocument::IsModified();
    DocViewTestView * view = (DocViewTestView *)GetFirstView();
    if (view)
    {
        res |= view->GetEditor()->IsModified();
    }
    return res;
}

void DocViewTestDocument::Modify(bool mod)
{
    DocViewTestView * view = (DocViewTestView *)GetFirstView();

    wxDocument::Modify(mod);

    if (!mod && view && view->GetEditor())
	{
        view->GetEditor()->DiscardEdits();
	}
}

bool DocViewTestDocument::OnSaveDocument(const wxString& filename)
{
	wxLogTrace(wxTraceMask(), wxT("DocViewTestDocument::OnSaveDocument"));
	do
	{
		DocViewTestView * view = (DocViewTestView *)GetFirstView();
		if(!view) break;
		wxTextCtrl * editor = view->GetEditor();
		if(!editor) break;

		if (!editor->SaveFile(filename))
		{
			return false;
		}
		Modify(false);
#ifdef __WXMAC__
		wxFileName fn(filename) ;
		fn.MacSetDefaultTypeAndCreator() ;
#endif
	}
	while(false);
    return true;
}

bool DocViewTestDocument::OnOpenDocument(const wxString& filename)
{
	wxLogTrace(wxTraceMask(), wxT("DocViewTestDocument::OnOpenDocument"));
    do
	{
		DocViewTestView * view = (DocViewTestView *)GetFirstView();
		if(!view) break;
		wxTextCtrl * editor = view->GetEditor();
		if(!editor) break;
		
		if (!editor->LoadFile(filename))
		{
			return false;
		}

		SetFilename(filename, true);
		Modify(false);
		UpdateAllViews();
	}
	while(false);
    return true;
}

So, let’s see how it works. When user opens the document using file selector dialog, wxDocument::OnOpenDocument() method is called automatically by Document/View framework and the file name is passed to this method. In this method we obtain the pointer to text editor and call wxTextCtrl::LoadFile() method which loads and displays a file. After that (if the file was loaded successfully) we associate the file name with our document, reset “modified” flag and update the view (it is default behavior when wxDocument::UpdateAllViews() method is called. In fact, for this sample we may avoid calling of this method because DocViewTestView::OnUpdate() method does nothing).
Now let’s build our project and see how everything works.

Simple Text Editor in wxWidgets using Document/View Framework

Simple Text Editor in wxWidgets using Document/View Framework

Download the source code for this tutorial.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: