wxDocview classes
In this section we will have a look at important parts of the framework and explain the task of each part.
First we will have a look at the global used classes: a2dDocumentCommandProcessor, a2dEventDistributer, and a2dDocviewGlobal. After that we introduce the a2dDocument, a2dView and their template classes and explain the scope of the a2dViewConnector.
It is possible to use this framework without some of the mentioned global objects, but this is not the scope of this document and only useful in special cases; so we won't discuss this matter here. Please have a look at the "nonmanaged" sample which is part of the docview distribution if you are interested to use this framework without the global objects.
Contents
a2dDocumentCommandProcessor
The a2dDocumentCommandProcessor is an object which acts like the wxDocManager of the standard wxWidget doc/view framework but has some properties which are unique.
This class maintains a global docview settings (i.e. how many documents should maximal be opened etc.) and controls in combination with the a2dEventDistributer how the different parts interact within the framework.
An instance of this class should be created at application start-up and added to the a2dDocviewGlobal instance, in order to get to it at any time.
The a2dDocumentCommandProcessor object receives events from child and parent frames, and from views. The class is registered to the a2dEventDistributer, and this way is informed about many events that are distributed in the application. Child and Parent frames in the application sent an event directly to this class if they don't handle the event themselfs. This is a good way to combine generic menu handlers for Opening and closing files into the central a2dDocumentCommandProcessor, instead of handling it in the parent or child frame directly. You should use the standard wxIDs for your menu / toolbar items; please have a look into the wxWidget manual for an overview. The instance of this class also takes care of enabling / disabling the menu / toolbar items (UI updates), this saves you a lot of routine work.
Events like drawing update events from a2dDocument or a2dView are distributed via the global instance of a2dEventDistributer to all registrated classes. This class here does recieve events via the a2dEvtHandler event processing mechanisms from which it is derived. Therefore you can intercept any event sent to it yourself, via a standard wxWidget static event table, and modify its behaviour. The events sent to a2dDocumentCommandProcessor are for common tasks as opening new files, closing files, printing etc. Next to that normal member functions that perform common tasks are gathered here. Think of maintaining the file history.
The most important task of this class is to maintain a record of all open documents, and which of them is the current document. The current document is the document that has a view that currently has the focus or is activated. When no view is active, the last active view its document will stay the active document. Most classes in the docview framework are reference counted. One can therefore not say that one specific class Owns an instance of another. Like here all open documents are known in the a2dDocumentCommandProcessor, and it holds a reference to ( owns ) each one of them, but the same document is also owned by a2dView. In general it is organized in such a manner that the a2dDocumentCommandProcessor is the last to release a document reference.
This class also holds references to all a2dDocumentTemplate and a2dViewTemplate objects used by the application.
In general you don't have to know what this class does in detail, because you will need only some methods of this class (i.e. attaching a file history to a menu). We will discuss some important methods in the section about the a2dDocumentTemplate and the a2dViewTemplate. Please have a look into the class documentation for an API overview.
If you want more specific features you may create your own command processor and attach an instance of the derived class to the a2dDocviewGlobals object at application start-up.
As said before the a2dDocumentCommandProcessor is the heart of the docview framework. But until now it was not clear why it is actually derived from a2dCommandProcessor. The reason is that new commands within an application are going through this class too. One derives from a2dDocumentCommandProcessor and adds functions to implement functionality in form of command like functions. Asume you want to perform a your unique special action on the current active document, clearly the way to do it is via a2dDocumentCommandProcessor, since it know what the current document is. But the action itself might be defined in a derived a2dDocument class, so the new command function calls on the current document the special action on your own type of document. It is often a good idea to add functionality which modifies a document, but which does not require any type of a2dView to do it, in the above described way. To make a well organized application, it is good to be able to execute commands via a commandline interface using command strings. Even if not used much, it makes sure that GUI is well seperated from the actual actions, and makes it easy to implement macro like features. A command function is then a sort of interface to deeper used classes, like documents and views etc. At last the a2dDocumentCommandProcessor, can be used from a2dCommandLanguageWrapper. This last class is the way to wrap scripting languages to the docview framework, and extensions you made via a derived a2dDocumentCommandProcessor.
a2dEventDistributer
As the name of this class says, this object distributes events that are sent to it to registrated classes instances. The a2dEventDistributer is for example used from the a2dDocument and the a2dView, where it distributes events created by both classes. Where possible communication from one class to another is based on this event distribution, even if a direct call would be possible. This has the advantage that the same event can be intercepted by other classes too, and that the classes do not have to know about each other. An application which only contains one a2dDocument to store some file internal, and makes no use of views or commandprocessors etc., can be handled without a problem.
You may register a wxEventHandler (typically a wxFrame) to catch distributed events. The events which are distributed are discussed later in the events overview. The a2dDocumentCommandProcessor is registered automatically, and communication between the several classes is often via the a2dEventDistributer, which can be reached via a2dDocviewGlobals->GetEventDistributer(). Any class derived from wxEvtHandler or a2dEvtHandler can be registered to the a2dEventDistributer, also any event can be sent to it. So when ever you have the need to be informed about a certain action, find out if there is an event sent to the a2dEventDistributer, register the class, and add in the static event table of your class a handler for that event. Warning
Don't forget to unregister an event handler which you've registered.
Example: Register and unregister a frame to catch events of the docview framework
1
2 // Constructor
3 MyFrame::MyFrame(wxWindow* parent, wxWindowID id, const wxString& title)
4 {
5 [...]
6 // Access the <<Dox(a2dEventDistributer)>> instance through the <<Dox(a2dDocviewGlobal)>> instance
7 // and register itself
8 a2dDocviewGlobals->GetEventDistributer()->Register(this);
9 }
10
11
12 // Destructor
13 MyFrame::~MyFrame()
14 {
15 // Access the <<Dox(a2dEventDistributer)>> instance through the <<Dox(a2dDocviewGlobal)>> instance
16 // and unregister itself
17 a2dDocviewGlobals->GetEventDistributer()->Unregister(this);
18 }
a2dDocviewGlobal
The a2dDocviewGlobal is a global object which gives access to the a2dDocumentCommandProcessor and the a2dEventDistributer. At application start-up the global available instance of the a2dDocviewGlobal may be initialized. It is already initialized by a default a2dDocumentCommandProcessor, but you can replace it with your own if needed. This object is called a2dDocviewGlobals
Example: Initialization of a2dDocviewGlobals
1 void MyApp::IntitDocView()
2 {
3 // Create the command processor
4 <<Dox(a2dDocumentCommandProcessor)>>* docMan = new <<Dox(a2dDocumentCommandProcessor)>>();
5
6 // Initialize the <<Dox(a2dDocviewGlobal)>> global object, which gives us access to the command processor and friends
7 a2dDocviewGlobals->SetDocviewCommandProcessor( docMan );
8
9 }
a2dDocviewGlobals is defined in doccom.h and doccom.cpp as a global object. a2dDocviewModule OnInit() is called before the application is starting in wxApp::OnInit(). This is where the a2dDocviewGlobal is initiated and assigned to a2dDocviewGlobals. The classes of the framework are looking for the a2dDocviewGlobals object.
After construction of the a2dDocviewGlobal object you have access to the a2dDocumentCommandProcessor-, and the a2dEventDistributer-instance, either the default or the one you have created at application start-up.
Example: Accessing the a2dDocumentCommandProcessor through the a2dDocviewGlobal instance
1 void MyFrame::MyFrame(wxWindow* parent, wxWindowID id, const wxString& title)
2 {
3 [...]
4 // We assume, that you have already created a wxMenu called fileMenu
5
6 // Access the <<Dox(a2dDocumentCommandProcessor)>> instance and attach the file history
7 // to the file menu
8 a2dDocviewGlobals->GetDocviewCommandProcessor()->FileHistoryUseMenu(fileMenu);
9 }
a2dDocument
This class is the base for modeling the application's data. This data can be read form files and stored back to files, or you can fill the document directly from wiith in the program code. A plain a2dDocument is of no use to store data, it simply does not know how your data is organized. So you need to derive your own document class, and store in there the data using your won classes.
You may want to override the member functions wxInputStream& a2dDocument::LoadObject(wxInputStream& stream) and wxOutputStream& a2dDocument::SaveObject(wxOutputStream& stream) to load and save your document data. This is necessary if you don't define your own a2dIOHandler and attach an instance of the IOhandler to the a2dDocumentTemplate at application start-up.
The rest is up to you: If you want to, you may override several "OnXXX" methods to implement your own behaviour for saving, opening etc. your documents.
a2dView
The a2dView class can be used to model the viewing and editing component of an application's file-based data (the documents).
It is used to display the data of a a2dDocument object. It is your choice how it displays the document: It may show the whole document or just parts of it. An important advantage of the framework's views is that they are completely independent of the user-interface. A view can even be used to display document data into a bitmap, or to print document data to a printer. So the device that the view uses to display/present the data is not fixed. Still, the most common use is to have a view display its data on a wxWindow. Which can be a window within a frame, but as well a wxDialog. The task of the view is to read the document, and assemble the data from the document that it wants to present. Next it will present that data on the device in some form. So in fact the view translates the data stored in the document, to some form which is displayed to the user via a window. Assuming we have a document with graphical primitives, one view will simply draw the document onto a window, while another view counts the number of colors used in the primitives, and display a table of colors and number of times the colors is used.
Updating a view, is what happens when a document is changed and an update event is sent to all views, in order to have them update their data too. Some view may indeed decide to store internal the data that they do display on a window. Like in the above case, where the color statistics are displayed onto a window. The advantage is that after a paint event ( e.g after removing an overlaping window), the view does not have to read the whole document again to find the color statistics data, instead it simply displays its internal data once more. The same is true for drawing the graphic primitives, when the view draws this into a bitmap first, it can reuse this bitmap to quickly reblit damaged parts caused by on paint events. Here the bitmap is the data that the view stores internal. The bitmap will only be changed/redrawn when the document its data has changed, and when because of that an update event is sent to the view.
One other situation does exist, where the view does not need to store its data. When the view uses a control, which itself internal already stores the data it shows. For instance a wxTextCtrl stores the text that it show internal. On paint events are directly handled by the control itself. In such a case the view only needs to make sure to update that data after the document changes.
The a2dViewConnector takes care of connecting new views to their windows. Several (different or same kind of) views can be connected to one document and display it. Default a view has a pointer to only one document, but it is also possible to have a view display data of several documents.
A view is informed of changes to a document through an update event, which is sent from the document to tell the view that they need to update themselves from the document. The Update event is a a2dDocumentEvent with id wxEVT_UPDATE_VIEWS, and you need to sent it to the a2dEventDistributer class from a a2dDocument when views need to redisplay the modified data. Only the views that use the document that is mentioned in the update event, should update themselves. The update event is always sent to the a2dEventDistributer class, and therefore to all views, since all views are registered to it by default. If a view wants to display data from two documents, it should update itself after an update event from the first one as well as from the other document.
A view is not limited to windows, you may create a view which displays only parts of a document. This might be a properties dialog which show the author, title and some other statistical data in a dialog (properties dialog). But since an update event is sent to all registered class instances of the eventdistributer, you can also directly use a wxDialog derived class. A view is best used to seperate the view its data from the application its interface in form of frames and windows.
The a2dViews are not aware and responsible of frames in the application. The only thing which connect them to the outside world is a pointer to a wxWindow (a2dDocumentViewWindow or a2dScrolledWindow). Because of this the views can be used in any type of application without modification.
a2dDocumentTemplate
One or more instances of this class should be created at application start-up. When you open a file, the extension of the file or its io handler is used to choose a a2dDocumentTemplate, which on its turn is used for creating a document that is needed to store this file in memory. The document template defines the relation between the file formats and the document types available. Often this is based on the extension of the file. In the file open dialog, you get a list of filters taken from the templates, and this way a certain template is coupled to the file which will be opened. Another way is to use the template its iohandler, which defines how the data needs to be loaded into a specific document. When no extension is available, the io handlers does test the file first, and if it can load the file, that template will be used.
A special a2dDocumentTemplateAuto can be used to automatically search for a suitable iohandler. It searches in the list of document templates in the central a2dDocumentCommandProcessor for a template which can save the document or load the file.
a2dViewTemplate
The a2dViewTemplate models the relationship of a document and a view. One or more instances of this class should be created at application start-up and made known to the a2dDocumentCommandProcessor. You may create several instances of this class to model the relationship of more than one view to a document type.
A a2dViewTemplate is used from a2dViewConnector. The connector uses it to decide which view templates fit to a document just created, and gives the user the choice for one of them. That is only the default implementation, in a derived connector class, you can create new views, or connect to existing views etc., as long as the view is okay for the document, that is not a problem.
a2dViewConnector
The key factor in having a2dViews independent of wxFrames and wxWindows is in the a2dViewConnector. Instead of having a wxView generating the windows/frames, this responsibility is shifted to a specific a2dViewConnector. This class handles the connection of a view to the view-device (typically a window).
An application will use a derived class from the a2dViewConnector class, to define how new a2dView instances are connected/coupled to the windows and frames of the application. A few predefined frames and windows, which know how to deal with views, are normally used to connect new views into the application. You use a2dDocumentFrame when the view directly uses the frame to display data from the document. For a scrolled window within a frame, use wxDocFrameScrolledWindow and a2dScrolledWindow. For a normal window within a frame, use a2dDocumentFrame and a2dDocumentViewWindow.
New Views and documents generated by the a2dDocumentTemplate and a2dViewTemplate result in events being sent to a a2dViewConnector (to which the templates have a pointer). When a new a2dDocument is generated from a a2dDocumentTemplate, the a2dViewConnector gets an event, and then decides to generate a view via a a2dViewTemplate for the new document. a2dViewTemplate sends another event to the a2dViewConnector, and this allows the connector to plug the new view into the application its windows (either existing frames/window or new created frames and window.)
The a2dViewConnector may first generate a new a2dDocumentFrame plus a a2dDocumentViewWindow, and use that window to connect the new view to.
If a wxFrame displays two views, this means that the a2dDocumentFrame has two a2dDocumentViewWindows as children, and those two windows are used by the a2dViews to display themselves.
In another application, it may use an existing a2dDocumentViewWindow. If the application has only one wxFrame containing several wxWindows used for displaying a2dViews, the connector's task will be again to receive events from the template. But now it will first disconnect an existing view from a window inside the application's only frame. Next it will connect the new view into that window. It may decide to remove the old view and its document, or keep them intact and only disconnect those old views.
a2dIOHandlerStrIn
This class serves several pure virtual methods which are used by the framework to load document's data. The goal of this class is, that you create your a2dDocument derived classes independent from loading/saving data. A a2dDocumentTemplate can have two io handlers, one for loading, and one for saving. By connecting a document to I/O handlers via a document template you don't have to model several documents and override the OnLoadObject? and OnSaveObject? methods, but create just another I/O handler.
If you've created a a2dIOHandlerStrIn derived class and create an new instance of it which you attach to a a2dDocumentTemplate, the framework will take care to inform the I/O handler to load/save document's data. Please have a look at its class documentation for further information which methods you'll have to override.
a2dIOHandlerStrOut
This class serves several pure virtual methods which are used by the framework to save document's data. The a2dIOHandlerStrOut is set to a a2dDocumentTemplate, and that is where a a2dDocument uses it from to save data to a file.
