One of the major benefits of using ODF is in the area of embedding. ODF makes embedding much easier to implement and also makes your part behave correctly by following the OpenDoc guidelines.
How you implement embedding is very dependent on the intrinsic content of your part (text, spreadsheet, drawing, ...). You cannot implement embedding without thinking about your data, how you will present it, and how the user is going to interact with it.
ODF provides several examples of embedding part editors, each with a different content model. For example the ODFDraw part editor shows one way of implementing embedding in the context of a drawing part. We think this is a very interesting case because of the direct interaction between intrinsic content (rectangles, oval objects...) and embedded frames (z-ordering, direct user interaction to resize or move objects...).
Note that ODFDraw is a relatively complex part editor. Your part editor may not have to implement everything that it implements. For example, embedded frames in ODFDraw can be partially hidden behind other frames or drawing objects. For this reason the ODFDraw part editor has a CDrawFacetClipper object that calculates the clipping shape of embedded frames. Your part editor may not have overlapping embedded frames. In that case you will not have to use a facet clipper. An example of a part editor without overlapping embedded frames is ODFTable.
FW_MProxy & Data Structures
The first thing you have to do in order to support embedding is to find a way to incorporate the FW_MProxy class defined by ODF into your data structure classes (as a mix in or simply as a field in another class)
For example, the ODFDraw class hierarchy (without embedding) looks like this:
This is a very simple class hierarchy. Draw objects (rectangles, ovals, lines...) are maintained in a z-ordered collection. Every shape object has the following protocol:
- draw
- set and Get Properties (foreground or background color, pen size, pattern etc.)
- flatten (persistence)
- resize
- move
- and more...
Supporting embedding required us to add a new type of shape (CProxyShape).
CProxyShape is a subclass of CRectShape. CProxyShape is also a subclass of FW_MProxy.
It is easier to explain what an FW_MProxy is by using an example. The following figure shows the ODFDraw part editor embedded in a root part (it doesn’t matter what this root part is). Notice that the ODFDraw part editor is active, and has some intrinsic content (a rectangle and an oval) and another part embedded in it (a clock part).
The user selects View in Window.
The Draw part now has two display frames (frame1 and frame2) and two embedded frames (one in frame1 and one in frame2).
If the user decides to move a graphic object, like the rectangle, it is obvious that the rectangle object in frame1 and frame2 should move the same way. After all, both frames display the same document. Now if the user decides to move the clock it should also move the same way in both frames. There should not be any difference for the user between a rectangle object and the clock. The problem is that the clock in frame1 and the clock in frame2 are displayed in two different embedded frames. You need a way to synchronize those two embedded frames so that moving one will also move the other one. This is what FW_MProxy provides. It is a list of the embedded frames that represent the same object in your data structure.
The ProxyShape move protocol, for example, iterates through all the embedded frames in the proxy list, moving them one by one.
Your subclass of FW_MProxy will usually have to override the following methods:
- UsedShapeChanged: One of your embedded frames has changed its used shape. Your part editor, depending on its content model, may have to recalculate its embedded facet’s clip shape.
- FrameShapeRequested: One of your embedded frames is requesting a new frame shape. Depending on your content model you may grant or refuse this request. By default ODF always grants the request. For this reason, ODFTable has to override FrameShapeRequested because embedded frames always have the size of their containing cell.
- AdjustBorderShape: this method is called in order to adjust one of your embedded facet’s active border shape.
You may also want to call the following methods:
- FW_MProxy::ChangeExternalTransforms: call ChangeExternalTransforms when you want to move an embedded facet in each one of your display frames.
- FW_MProxy::ChangeFrameShapes: call ChangeFrameShapes when you want to change the size of one of your embedded frames in each one of your display frames.
Content Objects
Three methods of your Content Objects (derived from of FW_CEmbeddingContent) are directly affected by embedding:
- Internalize
- Externalize
- SingleEmbeddedFrameInternalized
Remember that it is your responsibility to internalize/externalize your embedded frames along with your data in the kODPropContents property. ODF does not provide any default behavior because ODF does not know anything about your content model. It is your responsibility to call FW_MProxy::Internalize/Externalize in your override of FW_CEmbeddingContent::Internalize/Externalize.
Your selection's content object (the subclass of FW_CEmbeddingContent associated with your FW_CSelection object) must override SingleEmbeddedFrameInternalized. This method is called when a single embedded frame is copied or dragged into one of your frame. If more than one embedded frames needs to be added to your content, then the Internalize method of your selection's content object will be called instead.
Along with the embedded frame, a suggested frame shape is also provided. This suggested frame shape can be NULL; in this case you should provide a default frame shape consistent with your content model. The suggested frame shape could also be completely ignored (see ODFTable).
Your frame object, in the case where you want to support embedding, must be a subclass of FW_CEmbeddingFrame. The minimum API you need to support is:
- CreateEmbeddedFacet: is called to let you create your embedded facets. Don’t forget that as an embedding part, you are in charge of creating your embedded frame’s facets. The embedded facet’s external transform describes the location of embedded frames.
- UpdateSelectionOnMouseDown is called as a result of a mouse down event. Although UpdateSelectionOnMouseDown is not specific to embedding, one of its parameters is ‘inEmbeddedFrameBorder’. This means that the user has clicked in the active border of one of your embedded facets.