by Ed Snible
Here are the basics to Doc/View, as far as I can figure them. I suspect they are pretty hard to figure out, as none of the aftermarket BC4 books explain them, except Swan’s. They seem to be discussed only in the OWL manuals, in the on-line help, and with comments in the header files and source code itself. The source code seems to be the place to look to figure out Doc/View, as the other material is a little sparse at best.
To explain where a bit of information comes from, I have used the following convention. "1)" means copied directly from the help text, "2)" means from the source code, and "3)" means my own explanation.
This document assumes that you are an OWL user and have compiled and run the DOCVIEW example on the BC4 disks and have worked through the tutorial. It was at that point that I started pulling my hair out trying to figure out TDocument and TDocument and it is there that I begin:
Document and View classes
Other Doc/View-related classes:
TDocManager
TDocTemplate
TDocTemplateT<D,V>
TOleDocViewFactory<T>
TOleDocViewAutoFactory<T>
TOleFactoryDocView<T,Auto>
TStream, TInStream, TOutStream
Adopting the Document/View model makes it easier to develop large, flexible, document-centric GUI applications. Doc/View users get a big win developing programs which either have multiple windows open on the same set of data or the ability to switch the format of the screen dramatically over the same set of data.
If you have never seen what Doc/View does, compile and run the \bc45\examples\owl\owlapi\docview.ide example program. Open up a "Draw Line File", then select Window|Add View and add two more line draw views and also add a drawlist view. Tile the windows, and start scribbling in one of the drawing views.
By using the Edit menu items in the DrawList view, each line’s properties can be edited individually. Being able to work with data more than one way can be very satisfying. Data can be viewed as both a graph or a spreadsheet, for example. The "data" would be considered the Document, and the graph and the spreadsheet both would be kinds of views. This terminology is unfortunately confusing. A word processing application wants to give the user the feeling of working directly with the document but it can feel at first as if instead the user is removed from the document and working with a ‘view’. In a well designed program, though, the user feels as if he is directly manipulating the document in each view, which is of course what is actually happening.
The DOCVIEW.IDE example is an outgrowth of the OWL tutorial. There is a lot of really good stuff in the tutorial, and if you are new to Doc/View I recommend working on the step where Doc/View is added to the tutorial.
In the scribble application, pressing down on the mouse button starts drawing, and moving the mouse with the button down adds points to the line. When the mouse button is released, this function is called:
void TDrawView::EvLButtonUp(UINT, TPoint&)
{
if (DragDC) {
ReleaseCapture();
if (Line->GetItemsInContainer() > 1)
DrawDoc->AddLine(*Line);
Line->Flush();
delete DragDC;
delete Pen;
DragDC = 0;
}
}
The only thing new about this function is the call to TDrawDocument::AddLine(). This function looks like this:
int TDrawDocument::AddLine(TLine& line)
{
int index = Lines->GetItemsInContainer();
Lines->Add(line);
SetDirty(true);
NotifyViews(vnDrawAppend, index);
UndoState = UndoAppend;
return index;
}
First, the new line is added to the documents line data structure, as the lines are kept in the document as opposed to in the window. The document is marked as "dirty," meaning it is different than it was when it was initially loaded/created. Finally, NotifyViews() is called, sending the custom vnDrawAppend message to each view of the document. To define such a custom message, three lines are needed:
const int vnDrawAppend = vnCustomBase+0;
NOTIFY_SIG(vnDrawAppend, unsigned int)
#define EV_VN_DRAWAPPEND VN_DEFINE(vnDrawAppend, VnAppend, int)
And a line of code needs to be added to the DEFINE_RESPONSE_TABLEx for the view class. The ClassExpert doesn’t know about custom messages, so you need to add it by hand, after the "//{{TMyViewRSP_TBL_END}}" comment so you don’t interfere with the ClassExpert, like this:
//{{TDrawViewRSP_TBL_END}}
EV_VN_DRAWAPPEND,
END_RESPONSE_TABLE;
Finally, you write the function that a view uses to handle getting this update from its document:
bool TDrawView::VnAppend(unsigned int index)
{
TClientDC dc(*this);
const TLine* line = DrawDoc->GetLine(index);
line->Draw(dc);
return true;
}
The view’s Paint() needs to be modified to get data from the document as well:
void TDrawView::Paint(TDC& dc, BOOL, TRect&)
{
// Iterates through the array of line objects.
int i = 0;
const TLine* line;
while ((line = DrawDoc->GetLine(i++)) != 0)
line->Draw(dc);
}
One thing that is interesting about this Paint() method is that all of the information is held in the document. The view doesn’t send a message to the document to have it call line->Draw() on the view, because the document is supposed to be 100% isolated from anything do to with the view, and it should be possible to add new views without changing the document at all. The view, on the other hand, is required to know about the document. It needs to know how to handle every message the document is going to send. It is also allowed, for efficiency reasons, to keep information about the document cached inside itself. For example, lets see what happens when a line is deleted.
TDrawView doesn’t cache anything about the document, and when it gets a notification that a line has deleted it simply invalidates itself and lets its Paint() method redraw all of the lines:
bool TDrawView::VnDelete(unsigned int /*index*/)
{
Invalidate(); // force full repaint
return true;
}
TDrawListView, the listbox of line information takes a different approach. Because it is a listbox all the text about the lines is saved inside a Windows data structure. When it sees a line has been deleted, it just deletes that particular line. The TListBox::DeleteString() can set up and do the redraw without a Paint() member at all and no calls to the document:
bool TDrawListView::VnDelete(unsigned int index)
{
DeleteString(index);
HandleMessage(WM_KEYDOWN,VK_DOWN); // force selection
return true;
}
When I first began developing with Doc/View, I had a lot of difficulty figuring out how to structure my program, and understanding the often inaccurate and incomplete help files. To aid the Doc/View programmer, I have extracted a good deal of the Doc/View help from the OWL help file and added my comments, and I have written a FAQ on how to do certain things with Doc/View.
1) TDocument is an abstract base class that serves as an interface between the document, its views, and the document manager (the TDocManager class). TDocument creates, destroys, and sends messages about the view. For example, if the user changes a document, TDocument tells the view that the document has been updated. In order to send messages to its associated views, the document maintains a list of all the views existing for that document and communicates with the views using ObjectWindows event-handling mechanism. Rather than using the function SendMessage, the document accesses the view's event table. The views can update the document's data by calling the member functions of the particular document. Views can also request streams, which are constructed by the document. Both documents and views have lists of properties for their applications to use. When documents and views are created or destroyed, messages are sent to the application, which can then query the properties to determine how to process the document or view. It is the document manager's responsibility to determine if a particular view is appropriate for the given document.
Because the property attribute functions are virtual, a derived class (which is called first) might override the properties defined in a base class. Each derived class must implement its own property attribute types of either string or binary data. If the derived class duplicates the property names of the parent class, it should provide the same behavior and data type as the parent.
Although documents are usually associated with files, they do not necessarily have to be files; they can also consist of database tables, mail systems, fax or modem transmissions, disk directories, and so on.
3) This constructor causes the document to inherit the parent's document manager, if there is a parent. Otherwise it just sets the document manager member
3) This deletes all children (if there are any) and all views.
1) Generic input for the particular storage medium, InStream returns a pointer to a TInStream. mode is a combination of the ios bits defined in iostream.h. See the document open mode constants for a list of the open modes. Used for documents that support named streams, strmId is a pointer to the name of a stream. Override this function to provide streaming for your document class.
3) The InStream(), OutStream, and Open() members aren't abstract, so that classes that don't use TIn/OutStreams don't have to redefine them, but it does nothing in TDocument. This function expects a 'mode', this is where you tell the stream if it is to be binary or text, etc.
3) Again does nothing (just a stub).
3) also a stub, return TRUE.
2) close document, does not delete or detach
3) This member Close()s all children, but doesn't do anything to the view itself.
1) Saves the current data to storage. When a file is closed, the document manager calls either Commit or Revert. If force is TRUE, all data is written to storage. TDocument's Commit checks any child documents and commits their changes to storage also. Before the current data is saved, all child documents must return TRUE. If all child documents return TRUE, Commit flushes the views for operations that occurred since the last time the view was checked. Once all data for the document is updated and saved, Commit returns TRUE.
2) save current data, force write
3) Commit()s all children, then notifies all the views that a Commit is taking place. If force is FALSE, data will only be written if the doc is 'dirty' (has changed since it was loaded.) Interestingly, the code contains the comment: // should we test here for DocPath==0 , or should caller have checked? Which leads us to suspect that the caller should have checked... This is where you put the code in your TDocument descendent to write out your changes, unless you have having the view hold the data (more on this later.)
1) Performs the reverse of Commit and cancels any changes made to the document since the last commit. If clear is TRUE, data is not reloaded for views. Revert also checks all child documents and cancels any changes if all children return TRUE. When a file is closed, the document manager calls either Commit or Revert. Returns TRUE if the operation is successful.
2) abort changes, no reload if TRUE
3) Revert()s all children, doesn't do anything to itself.
1) Returns the this pointer as the root document. [INCORRECT]
3) This member searches up the parent list to find the root of the document hierarchy for this document. (perhaps it was supposed to be "of the root document.")
1) Returns a pointer to the current document manager.
3) Not quite sure why you would want more than one document manager?
1) Gets the template used for document creation. The template can be changed during a SaveAs operation.
3) A template is a pair <doc, view>. This was created by a DEFINE_DOC_TEMPLATE most likely.
3) Filename of the document. This can be 0 in the case of File|New.
3) This is usually used to put the title into the views.
1) Returns TRUE if the document or one of its views has changed but has not been saved.
2) also queries doc and view hierarchy
3) When a document changes, it is supposed to be set to being 'dirty' so that when the document is closed without saving the user can be prompted if saving is to be done. When IsDirty() is false, the File|Save menu choice is disabled by the document manager.
1) Checks to see if the document has any streams in its stream list. Returns FALSE if no streams are open; otherwise, returns TRUE.
3) This can be redefined to check something besides the streams list, the line drawing example just checks a pointer to see if any lines are around.
2) returns FALSE if unable to close
1) Used by the document manager, HasFocus returns true if this document's view has focus. hwnd is a handle to the document. to determine if the document contains a view with a focus.
3) Uses the vnIsWindow message. This is the reason you need to have a VnIsWindow() member and response table entry.
1) Notifies the views of the current document and the views of any child documents of a change, In contrast to QueryViews, NotifyViews sends notification of an event to all views and returns TRUE if any views returned a TRUE result. The event, EV_OWLNOTIFY, is sent with an event code, which is private to the particular document and view class, and a long argument, which can be cast appropriately to the actual type passed in the argument of the response function.
3) The event is something that begins with an EV_, like EV_NEW_VIEW. The item is any 32 bit value, the views have a special mechanism to get the data into the correct type, so you can pass pointers around and stuff if you cast them here. The exclude view doesn't get the information, so for example (although the example doesn't do this) you can have the line drawing program send the notification of a new line to all the views except the one that just drew the line, that way the one that submitted the line doesn't have to check for a callback and ignore it.)
1) Queries the views of the current document and the views of any child documents about a specified event, but stops at the first view that returns TRUE, In contrast to NotifyViews, QueryViews returns a pointer to the first view that responded to an event with a TRUE result. The event, EV_OWLNOTIFY, is sent with an event code (which is private to the particular document and view class) and a long argument
3) 'sid' is supposed to be the # for some message in the resource file, and 'choice' are the button flags. This function simply calls the document manager's PostDocError(). (The default document manager TDocManager calls MessageBox() with the string in the resource file. If you need something else to happen, say a BWCCMessageBox(), this is where subclassing the document manager comes into play. Note that if you take the MB_OK default, you automatically get MB_ICONQUESTION for some reason too... it's part of TDocManager)
2) return property index
2) pfXxxxx bit array
2) locale invariant name
3) If you look at the comment in the header file, you might get confused. "locale invariant" means that the name of the property doesn't change along with the national language stuff on 32-bit windows.
2) native type
==== TStream NextStream(const TStream strm);
1) Gets the next entry in the stream. Holds 0 if none exists.
3) TDocuments seem to maintain a linked list of streams which they disguise as a circular list. None of the examples use these streams, and neither does TFileDocument, so I have not a clue as to what they were supposed to be for...
1) Gets the next view in the list of views. Holds 0 if none exists.
3) Again, it seems to be emulating a circular list of views, so 0 will return the first view. Anyway, this member lets other classes go through the view list in much the same way as NotifyViews() does.
1) Gets the mode and protection flag values for the current document.
3) Private in OWL 2.0. Undocumented. If this view is valid, post the event dnCreate and reindex the frames.
1) Returns true if the document is embedded in an OLE 2 container.
3) Not in OWL 2.0.
1) Marks the document as being embedded in an OLE 2 container. Typically, this happens when the server is created and when the factory template class creates the component.
3) Not in OWL 2.0.
1) A virtual method that is overridden by TOleDocument::InitDoc. You can use this function to prepare the document before the view is constructed and before the dnCreate event, which indicates that the document has been created and is posted.
3) Not in OWL 2.0.
3) Not in OWL 2.0. Undocumented.
1) Tag holds a pointer to the application-defined data. Typically, you can use Tag to install a pointer to your own application's associated data structure. Tag, which is initialized to 0 at the time a TDocument is constructed, is not used otherwise by the document view classes.
2) application hook, not used internally
3) I suspect Tag is for doing tricks with TDocuments and TFileDocuments when you are too lazy to subclass them.
2) linked child document chain
1) Indicates that unsaved changes have been made to the document. Views can also independently maintain their local disk status.
2) document changed, might not represent views
3) (I am pretty sure that the comment in the source code is incorrect, I think that when a document is dirty it doesn't represent the FILES.)
2)document is an embedding
3) Not in OWL 2.0.
2) last used index for Untitled document
2) called from TStream constructor
2) called from TStream destructor
1) Derived virtually from both TEventHandler and TStreamableBase, TView is the interface presented to a document so it can access its client views. Views then call the document functions to request input and output streams. Views own the streams and are responsible for attaching and deleting them. Instead of creating an instance of TView, you create a derived class that has access to TView’s virtual functions. The derived class must have a way of knowing the associated window (provided by GetWindow), of describing the view (provided by GetViewName), and of displaying a range of data (GetSelection). The view must also be able to restore the view later (SetSelection) and to display the document title in its window (SetDocTitle). TView uses several event handler functions to query views, commit, and close views. For example, to query views to find the one with the focus, you would use the vnIsWindow function, passing a handle to the current window.
View classes can take various forms. For example, a view class can be a window (through inheritance), can contain a window (an embedded object), can reference a window, or can be contained within a window object. A view class might not even have a window, as in the case of a voice mail or a format converter. Some remote views (for example, those displayed by OLE 2.0 or DDE servers) might not have local windows.
1) Constructs a TView object of the document associated with the view. Sets ViewId to NextViewId. Calls TDocument::AttachView to attach the view to the associated document.
3) Also sets Tag and ViewMenu (publicly known as GetViewMenu()/SetViewMenu(). TDocument's private AttachView() takes care of advancing NextViewId.
1) Frees a TView object and calls DetachView to detach the view from the associated document.
1) Sets the menu descriptor for this view. This can be any existing TMenuDescr object. If no descriptor exists, ViewMenu is 0.
3) Look at the on-line help for TMenuDescr on this one. Basically, each view is expected to have a piece of menu-bar, which gets merged with the apps menu bar deep in the bowels of OWL.
1) Returns nonzero if the view is successfully constructed.
2) TRUE if successfully created
2) next ID to assign
3) Undocumented. Not in OWL 2.0.
2) must implement, used by template manager for selection.
3) This member doesn't actually exist, but is very important. C++ has no way of specifying an abstract static member, so Borland thoughtfully commented it out in the header. The help text for TListBox::StaticName() is: Overrides TView's function and returns a constant string, "ListView." This information is displayed in the user interface selection box. If you don't specify this, you don't get your view's name in the "Window|Add View" pick menu generated by the document manager.
2) return static name of view
3) This is supposed to contain EXACTLY the same thing as StaticName(). In fact, if you look at the code you will have a hard time finding calls to StaticName(), it looks like this is to be used exclusively, but no, you MUST define both and make them the same.
3) This member returns 0. It is supposed to be subclassed in TView/TWindow mix classes.
1) Stores the document title.
3) Actually, it does no such thing. It just calls the parent's SetDocTitle(), if there is a parent. Actually setting the window caption is done by TFrameWindow. To get your window to have a title, you have to override SetDocTitle() in your view class to call TWindow::SetDocTitle() and you should call TView::SetDocTitle() was well in case you will be dealing with child documents. TFrameWindow::SetDocTitle() appends its original title to your document's name and number.
3) This member points to the document. However, the type is TDocument so if you plan on using this member to call your own document's functions you had better downcast Doc to your own type.
1) Sets the view to an invalid state, thus causing IsOK to return 0.
2) to flag errors in creation
3) Neither TView, TDocument, or any of the document manager classes use this. This is for TView descendants to call if their Create() member fails for some reason.
1) Derived from both TWindow and TView, TWindowView is a streamable base class that can be used for deriving window-based views. TWindowView's functions override TView's virtual function to provide their own implementation. By deriving a window-view class from TWindow and TView, you add window functionality to the view of your document.
3) This class simply mixes TWindow and TView. It is a good base class to use if you aren't making an edit window (TEditView) or a list box (TListView) and sometimes is a good base to use even if you are. It defines the necessary stuff to make a view act like a window, and has a single response function, VnIsWindow()
3) This class does everything that TWindowView does, and LOTS more things. We will look only at the extra functionality.
Would you ever use TListView? You really don't want to use it unless you are doing something similar to what it does, scrolling over text. For example, the TDrawListView in the drawing example doesn't use TListView, it goes right to TListBox and TView directly, because TListView wants to do too much.
1) Creates a TListView object associated with the specified document and parent window. Sets Attr.AccelTable to IDA_LISTVIEW to identify the edit view. Sets Attr.Style to WS_HSCROLL | LBS_NOINTEGRALHEIGHT. Sets TView::ViewMenu to the new TMenuDescr for this view.
3) Sets Origin, MaxWidth, and DirtyFlag to 0. FNote that DirtyFlag is not the same as TDocument::DirtyFlag. Note that this constructor loads a menu "IDM_LISTVIEW" from the resource file for merging with the menu bar along with accelerators.
1) Overrides TWindow::Create and calls TEditSearch::Create to create the view's window. Calls GetDocPath to determine if the file is new or already has data. If there is data, calls LoadData to add the data to the view. If the view's window can't be created, Create throws a TXInvalidWindow exception.
3) (Actually, Calls TListBox::Create which throws the exception.) FNote that LoadData() is not virtual and is getting called here. Thus, you get the LoadData() for TListView if you are subclassing instead of your own LoadData(). A good reason not to use this class.
1) Is nonzero if the data in the list view has been changed; otherwise, is 0.
3) TListView sometimes sets this to 2 to do 'tricks' with saving and reloading from disk. Don't expect the value to be TRUE or FALSE.
1) Holds the file position at the beginning of the display.
3) Unlike the line drawing sample program, a TListView caches data locally, because that is what listboxes do. When it is time to VnCommit, the list box writes all of its data out to a stream instead of letting the document take care of it. Before it writes this data it seeks to Origin. Now, what is supposed to be happening is that the list box is holding one line for each line of text in the file. It might be possible to set up Origin to point to elsewhere in the file, then a different part of the file could be scrolled over, except that TListBox DOESN'T seek to Origin when it reloads it's data...
1) Holds the maximum horizontal extent (the number of pixels by which the view can be scrolled horizontally).
3) This is set to 0 by Clear() and to the length of the longest string ever sent to SetExtent(). SetExtent is called whenever data in the list box changes. This lets the list box have a scroll bar big enough to scroll over the longest string in the listbox.
1) Reads the view from the stream and closes the file. Returns TRUE if the view was successfully loaded. Throws an xmsg exception and displays the error message "TListView initial read error" if the file can't be read. Returns FALSE if the view can't be loaded.
3) The parameters, top and sel, have to do with the TListBox members SetTopIndex() and SetSelIndex(), this calls them. This function interprets the file as being lines of text 100 characters or shorter, and it loads each line into a line in the listbox where the file can be scrolled over.
The following members respond to choices on the Edit menu that was loaded in the constructor.
1) [This is OWL 2.0 documentation entry. This help item was removed from OWL 2.5] Automatically responds to a menu selection with a menu ID of CM_EDITUNDO by calling TListBox::Undo().
3) INCORRECT. Displays "Feature not implemented" in a message box.
3) These do what they are expected, using text and the clipboard. They set the DirtyFlag if necessary.
3) These members bring up a little dialog where the user can enter a line of text. They use a static function, so don't be expect to be able to do much with the appearance of the dialog. The dialog prompt comes from the resource file.
1) Overrides TWindow's response to a WM_GETDLGCODE message (an input procedure associated with a control that isn't a check box) by calling DefaultProcessing.
1) VnDocClosed indicates that the document has been closed. mode is one of the ofxxxx document open constants.
3) This member calls LoadData() to rebuild the list box, unless it's own VnCommit() decided to set the dirty flag to 2 and wrote the data itself. This member is called by TFileDocument::CloseThisFile() when an I/O completes. (Huh? OK, I don't quite understand it, but when the document is closed, TListView wants to re-read it from disk if anyone besides itself did the writing...)
1) VnCommit commits changes made in the view to the document. If force is nonzero, all data, even if it's unchanged, is saved to the document.
3) DirtyFlag is checked to see if the data is unchanged.
1) VnRevert indicates if changes made to the view should be erased, and the data from the document should be restored to the view. If clear is nonzero, the data is cleared instead of restored to the view.
3) That way VnRevert() can handle both File|Revert and Edit|Clear, though why those two functions are combined is unclear...
1) Returns a nonzero value if the window's handle passed in hWnd is the same as that of the view's display window.
3) OWL 2.5 only (private in 2.0?) @@@
1) Returns a nonzero value if changes made to the data in the view have not been saved to the document; otherwise, returns 0.
1) Automatically responds to a LBN_SELCHANGE message (which indicates that the contents of the list view have changed) by calling DefaultProcessing.
2) to prevent interpreting as unprocessed accelerator
3) This class does basically the same thing that TListView does, except it does it with TEditSearches instead of TListBoxes, so you get nice Find/Next dialogs and stuff. Use this only if you are writing an editor or something...
1) Derived from TWindowView [not! from TOleWindow] and TView, TOleView supports the View half of the Doc/View pair and creates a window with a view that can display an associated document. Documents use views to display themselves to a user. Regardless of whether a view belongs to a server or a container, TOleView sets up a corresponding TOcDocument object (an entire compound document).
In the case of an OLE-enabled container application, view refers to the window where the container application draws the compound document, which may consist of one or more linked and embedded objects. To display these objects in different formats, a container can be associated with more than one view. Similarly, to display the data properly, each embedded object can also have its own view. Each container view creates a corresponding ObjectComponents TOcView object.
If the view belongs to an OLE-enabled server application, TOleView creates a remote view on the server's document (a TOcRemView object). TOleView takes care of transmitting messages from the server to the container, specifically in the case of merging menus and redrawing embedded objects, and supports merging the server's and the container's pop-up menu items to form a composite menu. Because it knows the dimensions of the server's view, TOleView is responsible for telling the container how to redraw the embedded object.
Similarly to TView, TOleView supports the creation of views and provides several event handling functions that allow the view to query, commit, and close views. TOleView also manages the writing to storage of documents that belong to a container or a server.
3) I haven’t used this class enough to be able to say much about it, except that OLE apps don’t need TOleView, they only need TOleWindow. TOleView only adds the Doc/View view stuff to TOleWindow, so if you are having trouble with the user interface and such you need to look at TOleWindow. For example, if you look at the STEP14 step in the tutorial, you see a simple Doc/View OLE container. When you Edit|Insert Object, your new object shows up about 1/2" from the upper left hand corner. The code that puts it exactly there, drags it around etc. is entirely in TOleWindow. Your code just does the same old line stuff you have been doing since the early examples.
TOleView apps can also use the header file OLEVIEW.RH and pick up the following constants:
#define CM_EDITPASTESPECIAL 24311
#define CM_EDITPASTELINK 24312
#define CM_EDITINSERTOBJECT 24313
#define CM_EDITLINKS 24314
#define CM_EDITOBJECT 24370
#define CM_EDITFIRSTVERB 24371 // 20 verbs at most
#define CM_EDITLASTVERB 24390
#define CM_EDITCONVERT 24391
#define CM_EDITSHOWOBJECTS 24392
//
// Menu ID
//
#define IDM_OLEPOPUP 32405
#define IDM_OLEVIEW 32406
#define IDM_OLEVIEWEMBED 32407
//
// String ID
//
#define IDS_EDITOBJECT 32600
#define IDS_EDITCONVERT 32601
#define IDS_CLOSESERVER 32602
#define IDS_EXITSERVER 32603
//
// Accelerator IDs
//
#define IDA_OLEVIEW 32551
There is also an OLEVIEW.RC which defines these items.
1) Overrides TView's virtual GetViewName function and returns the name of the class (TOleView).
3) Wrong. Returns "Ole View" same as StaticName(). Also, the ‘n’ should be capitalized in the TOleView help page.
2) Shut down the associated OCF partners if possible
3) This function is undocumented. Calls TOleWindow::OleShutDown() and then if no other view exists tries to close the document. Always returns true. Inherited from TOleWindow where it isn’t documented either.
1) Creates an ObjectComponents view associated with the embedded object. Associates the view with the document template specified in tpl. The isEmbedded parameter is true if the view is an embedded object. The outer parameter refers to the IUnknown interface with which the view will aggregate itself.
2) Perform normal CleanupWindow, plus let the OcView object know we have closed
3) Not documented in the on-line help.
2) Check if other TOleView already exists
3) Downcasts all of the other views to see if they are TOleLinkViews. If it finds any, other than itself (which makes no sense to me because it isn’t a TOleLinkView), returns true else returns false. Not documented in on-line help.
1) Attaches this view to its ObjectWindows parent window so the embedded object can be either opened and edited or deactivated. To attach a view to an embedded object, set the attach parameter to true. To detach the embedded object, set the attach parameter to false.
1) Responds to an OC_VIEWBREAKLINK message that TOcLinkView sends when the server document that provides the link shuts down. EvOcViewBreakLink breaks the link with a server document or a selection by deleting the TOleLinkView associated with the TOcLinkView (view). After the link is broken, the container application is left holding a static representation (that is, a metafile) of the linked document. Returns false if unsuccessful.
1) Inserts the server's menu into the composite menu. Determines the number of groups and the number of pop-up menu items to insert within each group. The shared menu (sharedMenu) is the container's menu merged with the server's menu groups.
3) sharedMenu is called ‘part’ in the header file.
1) Asks the server to close the view associated with this document. Tests to see if the document has been changed since it was last saved. Returns true if the document and its associated view are closed.
1) Asks the server to load itself from storage. Loads the document and its associated view.
3) According to the TOleWindow::EvOcViewLoadPart() help, If the ... object is unable to handle the message, EvOcViewLoadPart returns false.
1) Asks the container application to open an existing document so the document can receive embedded and linked objects. (Actually, TOleView calls on the TOleDocument object to read the document from storage, using the standard OLE IStorage and IStream interfaces). Assigns a unique string identifier to the document and returns true if successful.
1) Notifies the active view of any changes made to the embedded object's data (changeInfo). Also, notifies any other views associated with this document that the bounding rectangle for the document is invalid and needs to be repainted. EvOcViewPartInvalid always returns true.
1) Asks the server to save the embedded object's data to storage. To save the object, EvOcViewSavePart calls upon the TOleDocument object, which creates storage as necessary for each embedded object. Saves the dimensions of the server's view, which the server uses to tell the container how to redraw the embedded object in the container's window.
3) According to the TOleWindow::EvOcViewSavePart() help, If the ... object is unable to handle the message, EvOcViewSavePart returns false. TOcSaveLoad is an undocumented class that is declared in the ocf\ocview.h header file. It has no members functions, some public data members, and the comment "Use when doing parts save and load"
1) Responds to an OC_VIEWSETLINK message TOcLinkView sends when the server document provides a link to a container document. EvOcViewSetLink establishes the link between a TOleLinkView and a TOcLinkView. The view parameter references the view with which the document or selection is associated. Returns false if unsuccessful.
2) Invalidate the view region specified by rect
3) Undocumented function. See VnInvalidateRect().
1) Ensures that TOleView's data members, such as DragPart, Pos, and Scale, [Inherited from TOleWindow] are initialized properly after a revert operation, which cancels any changes made to the document since the last time the document was saved to storage.
3) Undocumented function. Sets OcDoc to 0.
2) Override TView's GetViewMenu to make an on-the-fly decision about which menu to use: normal, or embedded.
3) Undocumented function. If a view menu has been explicitly assigned with SetViewMenu() that menu will be returned. Otherwise, it is going to load from the module the menu IDM_OLEVIEWEMBED or IDM_OLEVIEW and use that. SetViewMenu will keep it around.
1) Invalidates the view region specified by p. Use this function to invalidate the bounding rectangle surrounding an embedded object if the object has been changed, usually as a result of in-place editing. If successful, returns true.
3) This documented function doesn’t exist. See VnInvalidate(TRect&).
1) Derived from TView, TOleLinkView provides embedding and linking support for a portion of a document instead of an entire document. With the added functionality of TOleLinkView, a container gains the ability to embed or link to a selection within the server document.
The main purpose of a class derived from TOleLinkView is to attach a view to a portion of a document whenever a link is created to a selection within a server document. After this link is established, any changes made to the linked selection in the server document are sent to the container via the following sequence of steps:
1. When a user changes the server document, TOleLinkView receives a notification message
2. TOleLinkView checks to see if the selection it represents has changed. If the selection has changed, TOleLinkView notifies TOcLinkView about the change.
3. When TOcLinkView receives the change message, it notifies the container that the selection has changed.
Non-Doc/View servers need to maintain a list of the TOleLinkViews attached to the document so that change notifications can be sent to each one of the views.
3) This class does not appear in the help file’s ‘ObjectWindows hierarchy chart’.
1) Returns the moniker for the selection in a server document associated with this TOleLinkView container's view. By looking at the moniker, the application can find the corresponding objects in its document.
3) Documentation typo: The OWL help gives the return type as TString but it is TString& in the header file. This function just returns OcLinkView.GetMoniker(), which returns the moniker (the source file's path name and the object hierarchy) for the selection in a container document associated with this TOleLinkView server's view. See TOcLinkView::GetMoniker() in the OCF help for more details.
1) Returns the constant string "Link View" that is displayed in the user interface selection box.
1) The TOcLinkView connector object associated with this view.
3) Note that TOcLinkView is not a Doc/View class. It’s an OCF class derived from TUnknown.
1) Derived from TDocument, TFileDocument opens and closes views and provides stream support for views. Streams are created on top of DOS files using Windows file services. TFileDocument has member functions that continue to process FileNew and FileOpen messages after a view is constructed. You can add support for specialized file types by deriving classes from TFileDocument. TFileDocument makes this process easy by hiding the actual process of storing file types.
3) Initializes FHdl and InfoPresent
3) Does nothing
1) Overrides TDocument_Open and opens the file using the specified path. If the file is already open, returns 0. Calls TDocument::SetDocPath to set the directory path. If omode isn't 0, sets TDocument::OpenMode to omode. If the file can't be opened, returns 0.
3) This function set up a few things and calls OpenThisFile(). As OpenThisFile() isn't virtual, if you want to be doing your own stuff at opening time, you don’t call the old open() to set the file handle. (See the line drawing program for an example of this.) FHdl is used to keep track of the state of the document, open or closed.
1) Closes the document but does not delete or detach any associated views. Before closing the document, Close calls TDocument's Close to make sure all child documents are closed. If any children are open, Close returns 0 and doesn't close the document. If all children are closed, checks to see if any associated streams are open, and if so, returns 0 and doesn't close the document. If there are no open streams, closes the file.
1) Overrides Tdocument::InStream and provides generic input for the particular storage medium. InStream returns a pointer to a TInStream. mode is a combination of the ios bits defined in iostream.h. strmId is not used for file documents. The view reads data from the document as a stream or through stream functions.
3) The stream gets it's file handle from FHdl.
1) Calls TDocument::Commit and clears TDocument's DirtyFlag data member, thus indicating that there are no unsaved changes made to the document.
3) Commit doesn't write any data to the disk. I think it is intended to be used with views like TListView which carry all of their data in their windows. The line drawing example program doesn't even call the base Commit().
3) Same as commit.
2) { return FHdl != HFILE_ERROR || TDocument::IsOpen(); }
1) Opens a file document using an existing file handle. Sets TDocument::OpenMode to PREV_OPEN and read/write. Sets the document path to 0. Sets FHd to fhdl. Always returns nonzero.
3) While I have not seen this in use, I suspect that it is for compound documents where a document is embedded in another document. When the parent document gets to a certain point in this situation it creates a child document which reads bytes from its file.
3) You can call this if you redefine Open() and don't want to call the original Open(). THIS IS NOT VIRTUAL, SO DON'T SUBCLASS IT!
3) You can call this if you redefine Close() and don't want to call the original Close(). THIS IS NOT VIRTUAL, SO DON'T SUBCLASS IT!
1) Derived from TDocument, TStorageDocument supplies functionality that supports OLE's compound file structure. A compound file structure is a file-management system that stores files in a hierarchical structure within a root file. This storage structure is analagous [sic] to the directory or folder and file scheme used on a disk, except that a directory is called a storage and a file is called a stream.
In addition, TStorageDocument provides support for OLE's compound document mechanism. A compound document can store many different kinds of embedded objects, for example, spreadsheets as well as bitmaps.
Basically, TStorageDocument supports having a document read and write its own storage. In order to provide this functionality, TStorageDocument overrides several virtual methods from TDocument and, following TDocument's strategy, also applies property lists both to documents and to their views. In this way, documents can use these attributes to read files in from storage and write files out to storage
Messages are sent to the application, which queries the properties in order to determine how to process the document or view. Each derived class must implement its own property attribute types - either string or binary data.
1) Constructs a TStorageDocument object with the specified TDocument parent window. Sets the data members StorageI and OpenCount to 0. Sets CanRelease to false. Later, the member function ReleaseDoc sets this flag to true so that the document can be closed.
1) Releases the storage for the document and closes the document.
1) Opens or creates a document based on IStorage. The name parameter specifies the name of the document, if any, to open. The omode parameter contains a combination of the document open and sharing modes (for example, ofReadWrite) defined in docview.h.
1) Releases the IStorage if CanRelease is true. (CanRelease is set to true when ReleaseDoc is called.) Before closing the document, Close checks any child documents and tries to close them.
1) Saves the current data to storage. When a file is closed, the document manager calls either Commit or Revert. If force is true, all data is written to storage. TDocument's Commit checks any child documents and commits their changes to storage also. Before the current data is saved, all child documents must return true. If all child documents return true, Commit flushes the views for any operations that occurred since the last time the view was checked. Once all data for the document object is updated and saved, Commit returns true.
1) If a file is opened or created in transacted mode, call CommitTransactedStorage to commit a document to permanent storage. By default, a document uses transacted instead of direct storage. With transacted storage, a document written to IStorage is only temporary until it is committed permanently. If a compound file is opened or created in direct mode, then CommitTransactedStorage does not need to be called.
1) Performs the reverse of Commit and cancels any changes made to the storage document since the last commit. If clear is true, data is not reloaded for views. Revert also checks all child documents and cancels any changes if all children return true. When a file is closed, the document manager calls either Commit or Revert. Revert returns true if the revert operation is successful.
1) Sets the document path for the Open and Save file operations.
1) Checks to see if the storage document has any IStorage created. If there is no storage, IsOpen returns false; otherwise, it returns true.
3) These are the standard Property functions, same as TDocument.
1) OpenHandle writes data to a memory block. OpenHandle first creates an ILockBytes interface on the global handle and then creates an IStorage based on the ILockBytes interface. The parameter omode contains a combination of the document open and sharing modes (for example, ofReadWrite) defined in docview.h.
1) Derived from TStorageDocument, TOleDocument implements the document half of the Doc/View pair. It manages the document's data while the corresponding TOleView object determines how the data is displayed on the screen. Basically, TOleDocument is a TStorageDocument with a knowledge of TOcDocument through its pointer to TOcDocument.
TOleDocument is responsible for creating compound documents, closing documents, reading documents from storage, and writing documents to storage. In the case of a server, the document consists of a single object. In the case of a container, the document can consist of one or more embedded objects (also referred to as parts).
To accomplish these tasks, TOleDocument talks to the underlying ObjectComponents classes through the use of functions such as GetOcApp, GetOcDoc, and SetOcDoc.
3) TOleDocument adds two private members to TStorageDocument, TOcDocument* OcDoc and bool Closing. This is a relatively small class that manages those variables and calls TStorageDocument.
1) Commits the current document's data to storage. If force is true and the data is not dirty, all data is written to storage and Commit returns true. If force is false, the data is written only if it is dirty.
3) Undocumented function. Does nothing. Called by TOleView::EvOcViewSavePart() only.
1) Before the document is actually opened, PreOpen gives the derived class a chance to perform a particular operation; for example, setting a different open mode for the compound document.
1) Checks to see if the current document's path is the same as the TOcDocument's path. If the paths are not the same, PathChanged returns true.
2) Shut down the TOleView's
3) Undocumented function. All views of this document which can be downcasted to TOleView get OleShutDown()’d.
1) TDocManager creates a document manager object that manages the list of current documents and registered templates, handles standard file menu commands, and displays the user-interface for file and view selection boxes. To provide support for documents and views, an instance of TDocManager must be created by the application and attached to the application.
The document manager normally handles events on behalf of the documents by using a response table to process the standard CM_FILENEW, CM_FILEOPEN, CM_FILECLOSE, CM_FILESAVE, CM_FILESAVEAS, and CM_VIEWCREATE File menu commands. In response to a CM_FILENEW or a CM_FILEOPEN command, the document manager creates the appropriate document based on the user's selections. In response to the other commands, the document manager determines which of the open documents contains the view associated with the window that has focus. The menu commands are first sent to the window that is in focus and then through the parent window chain to the main window and finally to the application, which forwards the commands to the document manager.
When you create a TDocManager or a derived class, you must specify that it has either a multi-document (dmMDI) or single-document (dmSDI) interface. In addition, if you want the document manager to handle the standard file commands, you must OR these with dmMenu.
You can also enable or disable the document manager menu options by passing dmSaveEnable or dmNoRevert in the constructor. If you want to enable the File|Save menu option if the document is unmodified, pass the dmSaveEnable flag in the constructor. To disable the "Revert to Saved" menu option, pass dmNoRevert in the constructor.
When the application directly creates a new document and view, it can attach the view to its frame window, create MDI children, float the window, or create a splitter. However, when the document manager creates a new document and view from the File|Open or File|New menu selection, the application doesn't control the process. To give the application control, the document manager sends messages after the new document and view are successfully created. Then, the application can use the information contained in the template to determine how to install the new document or view object.
3) It shouldn't be too hard to subclass the manager to change some small thing about it, such as using BWCC message boxes for errors and such, but I wouldn't want to rewrite it...
1) Constructs a TDocManager object that supports either single (SDI) or multiple (MDI) open documents depending on the application. mode is set to either dmMenu, dmMDI, dmSDI, dmSaveEnable, or dmNoRevert. To install the standard TDocManager File menu commands, you must OR dmMDI or dmSDI with dmMenu. For example, DocManager = new TDocManager(DocMode | dmMenu); The document manager can then use its menu and response table to handle these events. If you do not specify the dmMenu parameter, you must provide the menu and functions to handle these commands. However, you can still use your application object's DocManager data member to access the document manager's functions.
3) The help doesn't document the second parameter, templateHead. The first TDocManager created gets all of the already existing "static" templates, and sets templateHead to 1, so that if a second document manager is created it DOESN'T GET ANY TO OWN ANY TEMPLATES UNLESS THEY ARE IN A LIST AND THE HEAD IS EXPLICITLY PASSED IN. All of the templates have their document manager set to this document manager.
1) Creates a document based on the directory path and the specified template. flags, one of the document view constants, determines how the document template is created. If path is 0 and this is not a new document (the flag dtNewDoc is not set), it displays a dialog box. If path is 0, dtNewDoc is not set, and more than one template exists, it displays a dialog box and a list of templates.
3) If flags is dtNewDoc, bring up the menu of visible document types using SelectDocType(), otherwise brings up a file open dialog with SelectDocPath(). After it has figured out which template to use, returns the template's CreateDoc(filepath, flags).
1) Creates a document view based on the directory path and specified template. flags, one of the document view constants, determines how the document template is created.
2) selects from registered templates supporting this doc.
3) Calls SelectViewType() from the templates supporting this doc, if a template is selected calls the template's CreateView(). The flags are just passed in to CreateView() which ignores them, they are supposed to be the dtxxxx flags.
1) CreateDoc creates a document based on the directory path and the specified template. The flags parameter contains one of the document template constants that determines how the document is created.
3) Not in OWL 2.0. This function creates a document by calling a document’s constructor (via TDocTemplateT<D,V>::ConstructDoc(), passing in a temporary document for the parent?!? It then sets the template and calls InitDoc(). CreateDoc() can be called directly from the application to open up files much as if they were opened with the File|Open. It is at a lower level, though, and it won’t catch multiple openings of the same document, so you may call FindDocument() first to check.
1) Creates a view of the specified document.
3) Not in OWL 2.0. Calls the template’s ConstructDoc() and then calls InitDoc().
1) Initializes the documents, the directory path for the document, and the dtxxxx document flag values (such as dtNewDoc ) used to create document templates.
2) prompts for pathname if none supplied and not creating a new document
3) Not in OWL 2.0. Note: The flags will be xor’ed with the flags in the template for this document, so you pass in CHANGES not the flags you want the document to be initialized with.
1) Prompts the user to select a file name for the document. Filters out read-only files.
3) Not in OWL 2.0.
1) Selects a registered template to save with this document.
1) Returns the first registered template whose pattern matches the given file name. If no template is compatible with the supplied file name, or if the template is open already, it returns 0.
1) Calls TWindow::GetFocus to determine the window with the focus. Searches the list of documents and returns the document that contains the view with the focus. Returns 0 if no document has a view with focus.
3) If this function returns 0 and one of your documents has the focus, then you will not be able to save your document, etc., from the menu. Probably HasFocus() is failing in that case because of a problem with VnIsWindow().
1) Updates the document with any changes and prompts the user for confirmation of updates.
1) Returns the first document whose pattern matches the given file name. If no document is compatible with the supplied file name, or if the document is open already, it returns 0.
3) Borland help file incorrectly gives the return type as
TDocTemplate* FindDocument(const char far* path);
1) Returns true if the dtxxxx document template constant specified in Flag is set.
1) Adds a template to the list of templates attached to the document.
1) Removes a template from the list of templates attached to the document.
1) Removes a template from the list of templates attached to the document.
1) Inserts a template into the chain of templates.
{return tpl ? tpl->NextTemplate : TemplateList;}
1) Returns the next document template.
3) If tpl is 0, returns the first template.
3) Just calls CreateAnyDoc(0, 0);
3) Just calls CreateAnyDoc(0, dtNewDoc);
1) Responds to a file close message. Tests to see if the document has been changed since it was last saved, and if not, prompts the user to confirm the save operation.
3) Calls the current document's CanClose(), it the document can close calls Close(), if Close() returns TRUE deletes the document
1) Responds to a file save message. Set doc to the current document. Calls IsDirty() and returns IDS_NOTCHANGED if the document hasn't been changed since the last time it was saved.
3) If the document doesn't have a path (created with CmFileNew()), calls CmFileSaveAs(). If the document has a path, does the checking described above and saved the document with it's Commit(). NOTES: The "doc" referred to in the help is a local variable of CmFileSave() that isn't accessible or meaningful. The IDS_NOTCHANGED isn't returned, as the function is void. It is submitted to PostDocError(), but you should never encounter this, as the private command enablers keep options that will trigger errors dim on the menu.
1) Prompts the user to enter a new name for the document.
3) Finds the current document, calls SelectAnySave() to figure out the file name and template to use for saving, then call's the document's Commit() with TRUE to force writing.
1) Reverts to the previously saved document.
3) Call's the document's Revert() method if the document IsDirty(), if the document isn't it will trigger a PostDocError() message.
1) Responds to a view create message by creating a document view based on the specified directory path.
3) Calls CreateAnyView(*doc), to create a new view for this document.
1) Displays a message box with the error message passed as a string resource ID in sid. By default, the message box contains either an OK push button or a question mark icon. If an error message can't be found, PostDocError() displays a "Message not found" message. choice can be one or more of the Windows MB_Xxxx style constants. See the Windows API on-line Help for a description of the values. This function can be overridden.
1) If the current document changes, calls ::SendMessage and passes a WM_OWLDOCUMENT message to indicate a change in the status of the document.
1) If the current view changes, calls ::SendMessage and passes a WM_OWLVIEW message to indicate a change in the status of the view.
3) The ids appear to be dnxxxx message constants.
1) TDocManager uses the dnxxxx message constants to indicate that a document or view has been created or closed. You can set up response table entries for these messages using the EV_OWLVIEW or EV_OWLDOCUMENT macros.
Constant Meaning
------------ -------------------------------------------------------
dnCreate A new document or view has been created.
dnClose A document or view has been closed.
3) An application uses receives these dnxxxx events by putting in a response table something like this, from the docviewx example:
EV_OWLVIEW(dnCreate, EvNewView),
EV_OWLVIEW(dnClose, EvCloseView),
Now, EvNewView() and EvCloseView() are not defined in the header file for TApplication, the names are just place holders, but these are the names generated by the AppExpert for Doc/View programs. In a standard Doc/View program, the EvNewView() either (mdi) creates a new mdi child and puts the view as the child's client or (sdi) puts the new view as the main window's client. The docviewx example program uses this to modify the menu, as well. EvCloseView() does nothing under mdi, as mdi takes care of it, under sdi it should set the main window's client to 0, and can alter the menu, title, etc.
1) Called from MainWindow, EvPreProcessMenu loads and deletes a menu at the position specified by MF_POSITION or MF_POPUP. Your application can call EvPreProcessMenu to process the main window's menu before it is displayed.
3) Called by TApplication::PreProcessMenu(HMENU fmenu) if there is a document manager, using WM_OWLPREPROCMENU. TApplication::PreProcessMenu is called by TFrameWindow::MergeMenu(const TMenuDescr& childMenuDescr) and TFrameWindow::AssignMenu(TResId menuResId).
1) Checks to see if all child documents can be closed before closing the current document. If any child returns FALSE, returns FALSE and aborts the process. If all children return TRUE, EvCanClose calls TDocManager::FlushDoc for each document. If FlushDoc finds that the document is dirty, it displays a message asking the user to save the document, discard any changes, or cancel the operation. If the document is not dirty and EvCanClose returns TRUE, EvCanClose returns TRUE.
3) Called by TApplication::CanClose(), using WM_OWLCANCLOSE.
3) This undocumented member calls ReindexFrames() for each document. This member is bound to the message WM_OWLWAKEUP which is send by OWL to the application during TDocManager::Streamer::Read(), so that document managers can be streamed in from disk.
1) Prompts the user to select one of the templates to use for the file to be opened. Returns the template index used for the selection or 0 if unsuccessful. For a file open operation, save is FALSE. For a file save operation, save is TRUE. This function can be overridden to provide a customized user-interface.
3) Uses the file common dialogs directly, not the OWL versions.
1) SelectDocType, which can be overridden, lets the user select a document type from a list of document templates. Returns the template index used for the selection or 0 if unsuccessful.
3) Uses a private TPickList class for this which makes a popup menu below the mouse cursor.
1) SelectViewType, which can be overridden, lets the user select a view name for a new view from a list of view names. Returns the template index used for the selection or 0 if unsuccessful.
3) (Same as SelectDocType)
Note:
3) There are private members for all of the Cmxxx members:
virtual void CmEnableNew(TCommandEnabler& hndlr);
virtual void CmEnableOpen(TCommandEnabler& hndlr);
virtual void CmEnableSave(TCommandEnabler& hndlr);
virtual void CmEnableSaveAs(TCommandEnabler& hndlr);
virtual void CmEnableRevert(TCommandEnabler& hndlr);
virtual void CmEnableClose(TCommandEnabler& hndlr);
virtual void CmEnableCreate(TCommandEnabler& hndlr);
These members dim illegal menu choices.
1) TDocTemplate is an abstract base class that contains document template functionality. TDocTemplate classes create documents and views from resources and handle document naming and browsing. The document manager maintains a list of the current template objects. Each document type requires a separate document template.
3) When AppExpert made your program, it set up something like this:
DEFINE_DOC_TEMPLATE_CLASS(TFileDocument, TWindowView, DocType1);
DocType1 __dvt1("All Files (*.*)", "*.*", 0, "TXT", dtAutoDelete | dtUpdateDir);
This is called a document template.
FNOTE: Document Templates have nothing to do with C++ templates! Document templates are data items that contain a view and a three letter file name extension for each document.
DEFINE_DOC_TEMPLATE has three parameters: docClass, viewClass, and tplClass. If you are used to the container classes, this is kind of like an association, it is saying that there is a type "DocType1". It might be more readable like this:
// (This code won’t compile, it is imaginary.)
DEFINE_A_TYPE_OF_VIEW(TfooDocument, TbarWindowView, TfoobarDocViewPair);
TfoobarDocViewPair dummy("All Files (*.*)", "*.*", 0, "TXT", dtAutoDelete | dtUpdateDir);
Where 'foo' is your own special I/O routines for saving whatever kind of objects your document holds, and 'bar' is your viewer class. Sometimes you want more than one viewer class, like in the line drawing example there was a graphical viewer and a text viewer, so there were two DEFINE_DOC_TEMPLATE_CLASSes and __dvts. You can have as many as you want. When you select "Window|Add View" you get a menu of the StaticName()s of all views which have the same document class as the current view.
DEFINE_DOC_TEMPLATE_CLASS is supposed to hide from you a bunch of code that it writes that creates a TDocTemplateT<D,V>. While most of the Doc/View stuff becomes clearer when you look at the code, TDocTemplateT<D,V> doesn't, so just use the macro.
The strings in the template define what will appear in the common dialog when a file is being opened. The dtxxxx flags are discussed below.
1) A pure virtual function that must be defined in a derived class, ConstructDoc creates a document specified by the document template class. Use this function in place of CreateDoc.
3) This function is much lower level than
1) A pure virtual function that must be defined in a derived class, ConstructView creates the view specified by the document template class.
3) Use this function in place of CreateView().
1) An obsolete pure virtual function that must be defined in a derived class, CreateDoc creates a document based on the directory path (path) and the specified template and flags value. If the path is 0 and the new flag (dtNewDoc) is not set, the dialog box is displayed. This function is obsolete: use ConstructDoc instead.
3) Although Borland recommends the use of ConstructDoc() instead of this function, ConstructDoc() does have the parameters expected. To use ConstructDoc(), do what CreateDoc() does: call InitDoc()@@@
1) A pure virtual function that must be defined in a derived class, CreateView creates the view specified by the document template class. This function is obsolete: use ConstructView instead.
1) InitDoc is called only from the subclass so that CreateDoc can continue its document processing.
2) Backward compatibility
2) Backward compatibility
1) A pure virtual function that must be defined in a derived class, IsMyKindOfDoc tests if the template belongs to the same class as the document or to a derived class.
3) See TDocTemplateT<D,V>::IsMyKindOfDoc().
1) Prompts the user to select a file name for the document. Filters out read-only files.
2) Backward compatibility
1) Indicates whether the document can be displayed in the file selection dialog box. A document is visible if dtHidden isn't set and Description isn't 0.
2) Backward compatibility
1) Sets the valid document matching pattern to use when searching for files.
2) Backward compatibility
3) Does nothing, unless you are running Diagnostic Expert in which case calling this causes the warning "Obsolete function called" to appear.
2) Backward compatibility
2) Backward compatibility
3) Undocumented function. Does nothing, unless you are running Diagnostic Expert in which case calling this causes the warning "Obsolete function called" to appear.
2) Backward compatibility
1) Sets the default extension to use if the user has entered the name of a file without any extension. If there is no default extension, SetDefaultExt contains 0.
2) Backward compatibility
3) Does nothing, unless you are running Diagnostic Expert in which case calling this causes the warning "Obsolete function called" to appear.
1) Constructs a TDocTemplate with the specified file description (desc), file filter pattern (filt), search path for viewing the directory (dir), default file extension (ext), and flags representing the view and creation options (flags). Sets Description to desc, FileFilter to filt, Directory to dir, DefaultExt to ext, and Flags to flags. Then, adds this template to the document manager's template list. If the document manager is not yet constructed, adds the template to a static list, which the document manager will later add to its template list.
3) This constructor is private because it is expected that TDocTemplateT<D, V> will be used to construct the template. In fact, it is TDocTemplateT<D, V>'s constructor that passes around the static list, TDocTemplate doesn't know about the static list.
1) Destroys a TDocTemplate object and frees the data members (FileFilter, Description, Directory, and DefaultExt). The Destructor is called only when no views or documents are associated with the template. Instead of calling this Destructor directly, use the Delete member function.
1) InitDoc is called only from the subclass so that CreateDoc can continue its document processing.
1) Called only from the subclass to continue CreateView processing.
==== TDocTemplateT(const char far filt, const char far desc, const char far dir, const char far ext, long flags = 0, TModule& module = ::Module, TDocTemplate& phead = DocTemplateStaticHead); ====
3) Just calls TDocTemplate's constructor.
3) Undocumented function, but replaces the documented CreateDoc(). See TDocTemplate. Called by the document manager’s CreateDoc() and CreateAnyDoc() functions.
3) Undocumented function, but replaces the documented CreateView(). See TDocTemplate. Called by the document manager’s InitDoc(), CreateView(), and CreateAnyView() members. Also called by TOleFactoryDocView<T, Auto>::CreateObject().
1) Creates a document of type D based on the directory path (path) and flags value.
3) Obsolete. See ConstructDoc(). No one calls this function in OWL 2.5. The flags are the dtxxxx flags.
1) CreateView creates the view specified by the document template class.
3) Obsolete. See ConstructView(). No one calls this function in OWL 2.5. The flags are the dtxxxx flags, but are ignored. The code is:
return (V*)InitView(new V((D&)doc));
which basically is a type safe encapsulation of calling InitView() with the result of calling the view's constructor.
1) Tests to see if the document (doc) is either the same class as the template's document class or a derived class. If the template can't use the document, IsMyKindOfDoc returns 0.
3) If your class was created by the ClassExpert, it set up a DEFINE_DOC_TEMPLATE_CLASS for you which writes an IsMyKindOfDoc member for you. That member simply does a TYPESAFE_DOWNCAST of the passed in doc to your document type. Note: This works well if each file extension in your program uses a TDocument subclass you wrote yourself, but it can cause problems if you use the stock TFileDocument. Take the DOCVIEW.IDE example provided by Borland. A .pts file can be saved as a dump-view as well as a line-view because the TDrawDocument can be downcast from a TDocument to a TFileDocument as well as to a TDrawDocument, and the dump-view is using the TFileDocument.
1) An abstract base class, TStream provides links between streams and documents, views, and document files.
3) This class isn’t abstract in the usual C++ sense with =0 pure virtual functions, it is made abstract because it’s only constructor is protected.
3)
TInStream and TOutStream simply add public constructors and mix in istream and ostream.
Related classes TStorageInStream, TStorageOutStream, TStorageStreamBase, and TStorageBuf are used by TStorageDocument and TOleDocument but are declared in the .cpp files and so cannot be used.
TDocManager* GetDocManager() returns the document manager. This is used when you want to programmatically cause Doc/View things to happen, such as loading a document on startup. From any TWindow, you can call GetApplication()->GetDocManager() to get the document manager.
In addition, your application will need an EvNewView() and possibly an EvCloseView() member in your application class.
This class is used to alter an applications menu. It is normally used with Doc/View to merge menu items specific to a view with menu items which are always available. See the on-line help and the docview.ide sample program.
Events and how to make them
Let's take a look at how the standard events are defined. They are defined in two pieces: First, the argument types to the various functions are defined, then the EV_VN_ messages are defined, like this:
NOTIFY_SIG(vnViewOpened,TView*)
NOTIFY_SIG(vnViewClosed,TView*)
NOTIFY_SIG(vnDocOpened, int)
NOTIFY_SIG(vnDocClosed, int)
NOTIFY_SIG(vnCommit, BOOL)
NOTIFY_SIG(vnRevert, BOOL)
NOTIFY_SIG(vnIsDirty, void)
NOTIFY_SIG(vnIsWindow, HWND)
#define EV_VN_VIEWOPENED VN_DEFINE(vnViewOpened,VnViewOpened,pointer)
#define EV_VN_VIEWCLOSED VN_DEFINE(vnViewClosed,VnViewClosed,pointer)
#define EV_VN_DOCOPENED VN_DEFINE(vnDocOpened, VnDocOpened, int)
#define EV_VN_DOCCLOSED VN_DEFINE(vnDocClosed, VnDocClosed, int)
#define EV_VN_COMMIT VN_DEFINE(vnCommit, VnCommit, int)
#define EV_VN_REVERT VN_DEFINE(vnRevert, VnRevert, int)
#define EV_VN_ISDIRTY VN_DEFINE(vnIsDirty, VnIsDirty, void)
#define EV_VN_ISWINDOW VN_DEFINE(vnIsWindow, VnIsWindow, int)
Here is a handy table of the meanings of the constants:
Constant Meaning
1) Changes are committed to the document.
2) document is committing, flush cached changes
3) The document is about to write changes to the file. If the view is keeping changes from the document for efficiency reasons, it has to tell the document about them all when it gets this notification.
1) Base event for document notifications.
2) base of document class specific notifications
3) Has the value of 100. Document classes you write are supposed to use vnCustomBase+x for each of their custom notifications. Documents don't send events to views that are not their own, so you don't need to guarantee unique #s among documents. Define these in your document and not your views so they can be shared.
1&2) Document has been opened.
1&2) Document has been closed.
1&2) Is TRUE if uncommitted changes are present.
1&2) Is TRUE if the HWND passed belongs to this view.
1) Document's previous data is reloaded and overwrites the view's current data.
2) document has reverted, reload data from doc
1) A new view has been constructed.
2) A view is about to be destroyed.
Now, in the Borland supplied line drawing example, three additional events were defined: Append a line, Delete a line, and Modify a line. Each of these worked with an unsigned int which was the # of the line. These events were defined thusly:
const int vnDrawAppend = vnCustomBase+0;
const int vnDrawDelete = vnCustomBase+1;
const int vnDrawModify = vnCustomBase+2;
NOTIFY_SIG(vnDrawAppend, unsigned int)
NOTIFY_SIG(vnDrawDelete, unsigned int)
NOTIFY_SIG(vnDrawModify, unsigned int)
#define EV_VN_DRAWAPPEND VN_DEFINE(vnDrawAppend, VnAppend, int)
#define EV_VN_DRAWDELETE VN_DEFINE(vnDrawDelete, VnDelete, int)
#define EV_VN_DRAWMODIFY VN_DEFINE(vnDrawModify, VnModify, int)
When I first started trying to make Doc/View apps, I didn't realize that I could put any old type I wanted into NOTIFY_SIG, because I didn't understand how it figured out what to do. But you can, provided it fits into 32 bits. Just follow the examples from DOCVIEW.H above. (DOCVIEW.H has a bunch of secret stuff in it to make this all work, it is quite hard to read...)
The document manager is lazy; it doesn't open your document. Why? Well lets say you are writing the Borland C++ IDE with Doc/View. Now, you are going to need the .IDE, the .EXE (for debugging and browsing) and the .APX (for Class Expert) when a project is opened up. But loading in the whole .EXE might be a waste, so the system waits until it needs it. If you look at the line drawing example it does this to show you how to do it, although you don't get an actual benefit out of it in this case. (I'm not sure though if you wouldn't model a system like the C++ IDE with the .IDE file being a parent document, and the .EXE and .APX being children though...)
What the DOCVIEW.IDE line drawing example does is have the following code at the top of its GetLine() method:
if (!IsOpen() && !Open(ofRead | ofWrite))
return 0;
Paint() calls GetLine() which will call Open() the first time through. If you don’t like this you can add the bit dtAutoOpen in your DocTypeN declaration.
If you decide to add dtAutoOpen to an existing project, be warned that your Open() will not be called for File|New operations as it was when Open() was called in the document.
You copied the CanClose() member from the line drawing example:
bool CanClose() {return TListBox::CanClose() && Doc->CanClose();}
This bit of code asks the document if it can close when the view closes, even though the document is not going to close. If your view doesn't hold any data, you don't want to call TDocument::CanClose() unless you are the last view on the screen:
bool CanClose()
{ return TListBox::CanClose() &&
(Doc->NextView(this) || Doc->NextView(0) != this ||
Doc->CanClose()); }
SetDocTitle doesn't do anything except call the parent document, and there is rarely one of those. The only SetDocTitle() in OWL that does anything interesting is the one in TFrameWindow (which wasn’t in the BC 4.0x help). What it does is takes the frame's title, the document name, and the index number, and make a window caption. If you want to set the frame's title, you can do it in TMyApp::EvNewView by replacing the call to your MDI child constructor with something like this:
TMyMDIChild* child = new TMyMDIChild(*mdiClient,
view.GetViewName(), // caption
view.GetWindow());
You have to write a VnIsWindow() member AND you must put a EV_VN_ISWINDOW in the response table. If you don't the document manager thinks that your view isn't the active view, even when it has the focus. See TListBox::VnIsWindow() for an example.
If your view has controls on it (for example if you are deriving from TView and TDialog), the example VnIsWindow() will not work for you, because the document manager is passing around the window with the focus, and you have given the focus to a control. Try this:
bool TMyDialog::VnIsWindow(HWND hWnd)
{
return ((HWindow == hWnd) || IsChild(hWnd));
}
This is frustrating for projects created by the "Dialog main window" option of BC 4.5x, because the AppExpert will generate a VnIsWindow() like this:
bool VnIsWindow(HWND hWnd) {return HWindow == hWnd;}
which will NEVER work for a dialog.
I haven't found a clean way to do this, although a property could probably take care of it. In your EvNewView(), set the icon based upon the view class:
if (TYPESAFE_DOWNCAST(&view, TAlphaView))
child->SetIcon(this, IDI_ALPHA);
else if (TYPESAFE_DOWNCAST(&view, TBetaView))
child->SetIcon(this, IDI_BETA);
else if (TYPESAFE_DOWNCAST(&view, TGammaView))
child->SetIcon(this, IDI_GAMMA);
(When I suggested this method on the OWL mailing list, I was blasted for the non-object oriented nature of this solution. Some people think that all the views should inherit from a single view with a GetDesiredIcon() method, and the view should be TYPESAFE_DOWNCAST into that type and its GetDesiredIcon() method called. This is a good solution, although it would be better if the stock TView had something like this or even better if the ClassExpert could derive classes from your own base classes.)
Changing the menus is your code's job, not the document manager's. In your EvNewView() member you want to add the following code before the call to setting the icon:
if (view.GetViewMenu())
child->SetMenuDescr(*view.GetViewMenu());
This of course assumes that you are setting the view menu up correctly in the constructor for your view. Once this has been done, TMDIChild::EvMDIActivate() will call TFrameWindow::MergeMenu() when the active view changes.
I believe if you want to change the speed bar for different window types as the Borland IDE does that you would need to do this in EvMDIActivate for your TMDIChild descendent but I have not tried this yet.
This is done in your InitInstance() member of your application class AFTER the call to TApplication::InitInstance(), like this:
// If a command line argument was given, try to open it as a file
if (_argc == 2)
GetDocManager()->CreateDoc(&__dvt1, _argv[1], 0, 0);
__dvt1 is a doc/view template given at the top of your application .cpp. If you have more than one document type, you need to get the file extension from _argv[1] and use that to determine which __dvtx variable to use. If you have more than one view, you need to specify the one you want to open by default. F Note that this method doesn’t work with OWL 2.0.
There is no notification when a Document closes, but you can monitor for view closings and if you see the last view for a document closing add that item to the menu. Here is a code sample for an EvCloseView() method. Two variables, bool fAddedSeparator and the array char szFileCache[5][MAXPATH] were added to the application class, as well as changes in InitInstance() and ~TApplication() to read and write the cache list for the next time the program was started:
void myApp::EvCloseView (TView& view)
{
// Only update menu if this is the last view. TDocument::GetViewList() and
// TView::GetNextView() are undocumented public inline functions.
if (view.GetDocument().GetViewList()->GetNextView())
return;
const char *psz = view.GetDocument().GetDocPath();
// Don't update if there is no filename (After File|New...)
if (!psz)
return;
AddCacheTop(psz);
// Erase the existing menu items
TMenu menu(GetSubMenu(GetMainWindow()->GetMenu(), 0));
for (i = 0; i
I'll put this page here for a while, but most of the information belongs to the new Help file --[[User:Jogybl|Jogybl]] 08:24, 9 March 2010 (UTC)
I fully agree. The summaries are fine here but the reference material makes it insurmountable as an article. (I must admit I didn't get through it all myself.) Someone struggling with Doc/View coming here for some simple answers would probably run away screaming. Hopefully it is not a repeat of the huge amount of formatting work, that you obviously have put in here, to get the good parts into the Doxygen documentation. [[User:Vattila|Vattila]] 01:02, 14 March 2010 (UTC)