Overview:

The Bonobo Compound Document Interfaces were originally modeled after the associated COM interfaces. It has become clear that they do not provide much utility in their current form. They have been largely unused to this point, so the Gnome 2 platform shift gives us an opportunity to revamp the interfaces and set them on a path that is more consistent with the rest of Bonobo.

This document is an attempt to identify the requirements for a compound document architecture and propose a solution which is more in line with the rest of the bonobo interfaces.

What are compound documents?

A compound document is obviously an aggregation of multiple sub-documents. The sub-documents are implemented as standalone components. A set of interfaces is necessary to allow the components to expose both their model characteristics to container documents and their display characteristics to container views. With these generalized interfaces, it is possible to write simple container applications that can unleash the power of specialized components while remaining ignorant of the components' purpose. The container need only know how to perform the following operations on subcomponents.

> Activation:
> A container must be able to query for subcomponents which provide the container's preferred rendering model (canvas vs. widget). A Control implementation should be provided for ease of identifying embeddable subcomponents. The control should perform component activation and provide a reference to the selected subcomponent via an event. Linking of subcomponents via the Clipboard/Drag'nDrop is also necessary. Using a moniker based scheme is the clear choice for this capability. The design and implementation of this new capability is being performed in parallel to this compound document effort.

> WYSIWYG Display/Print rendering:
> A subcomponent must be capable of rendering itself in a WYSIWYG fashion using GNOME rendering technology. Components should expose factories to obtain a canvas or widget based "view" reference which can be embedded into a document view. Similarly, an interface to provide print rendering is necessary.

> Uniform zooming of subcomponents is accomplished through the size request/size allocation cycle on the plug/socket interfaces. Components request their size in points based on their full extent at 100% zoom. The container can then factor in manual resizing of the component and scale the allocation by the current zoom factor of the document. The component then scales itself to utilize the allocated space. If the component's unscaled dimensional requirements change due to editing, for example if a new line of text is added to a text control, the component view should emit a new size request (again in points and based on 100% zoom) so that the container can reallocate space and perform any additional layout tasks it deems necessary.

> The above zooming mechanism is made more complicated because the current size request mechanism is integer/pixel based, and we need point based granularity using floating point values. We already have a mechanism to perform this transformation, GnomeCanvasWidget. The canvas also incorporates an easy to use zooming mechanism.

> Another consideration is whether user resizing of a subcomponent should scale the component view, or crop it. Word, for example, uses a cropping paradigm, where the zoom of the component is not impacted, and only the visible extent of the document changes as the user resizes the component. While it is somewhat counterintuitive to show only a portion of a subcomponent, we must recognize that some users may be used to this sort of behavior and consider providing an alternative mechanism to perform this operation.

> Perhaps subcomponents could expose a standard property to identify if they support cropping. A zoom_factor property would also be necessary so that when the container sets a new size allocation, the component could render its visible extent at a given zoom factor for the specified size allocation.

> The subcomponent must support at least a subset of its editing capabilities via "in-place" activation of its view controls. The subcomponent should merge UI elements into the container's UI to provide an embedded editing capability. It is not necessary to provide all of the editing functionality that the subcomponent's native application provides, however.

> Where possible, a subcomponent should support both canvas and widget based view controls. Until such time as the widget infrastructure becomes more canvas-like, it is anticipated that canvas based subcomponents will be more useful. It is hard to imagine an acceptable document layout implementation using tables and boxes. The canvas is currently better suited to the fine positioning and sizing requirements of a document application.

> Persistence:
> A subcomponent must be able to save and restore its state via a predictable interface. A compound document container must be able to serialize the state information for all of its subcomponents. The subcomponent must also have a mechanism to notify the container when the component has changed since its state was saved last.

The Original Compound Document interfaces:

The following interfaces represent the compound document architecture as of bonobo-1.0. They are primarily unused as of GNOME 1.4 and have very little utility. Clearly, wholesale restructuring is required.

- ClientSite: A client helper object which "binds" to the remote Embeddable object. The interface exposes 3 methods, none of which provide any utility to a remote component.:

- getContainer(): There is nothing useful that a remote component can do with the ClientSite's parent container.
- showWindow(): Is to notify the client if a containee wants to show its own separate editing window.
- saveObject(): This should be accomplished by QI for the component's persist interfaces.

- View: A Control subclass that exposes one addition zooming method. Zooming is a complex issue as described above, but View::setZoomFactor is not a desirable way to manage this. A zoom_factor property would be better.
- ViewFrame: A ControlFrame subclass with the dubious method, getClientSite. As displayed above, there's not much point to obtaining a ClientSite ref.
- Embeddable: The sub-document model object interface. It exposes the following methods:

- setClientSite: If the ClientSite is worthless to the Embeddable, why would it care?
- getClientSite: Ditto.
- setHostName: Documented as being useful for labeling separate editing window. Since an Embeddable need never open such a beast, there's no need to label it.
- setURI: the URI this component represents. If this is really necessary, a property would be more appropriate.
- close: The normal ref counting mechanism should cause clean shutdown of Embeddables. Besides, there is no way a container could know if it is the sole user of a component and is allowed to shut it down.
- advise: This is COM/OLE cruft that if it were needed would be better provided in Bonobo by an EventSource.
- unadvise: Ditto.
- getMiscStatus: Undocumented, and it's hard to imagine what need this could possible fill.
- createView: Useful, but misplaced here. This should be in its own queryable interface so that containers can determine the view capabilities of potential embeddables prior to activation.
- createCanvasItem: Also useful, and similarly misplaced.

The above interfaces are inconsistent with the current bonobo direction of minimizing client-side helper objects. In the View/ViewFrame case, they are so thin a wrapper around the Control interfaces as to be virtually indistinguishable from them, save by name. The ClientSite, View, and ViewFrame interfaces can be eliminated outright. The useful bits of Embeddable need to be split out into queryable interfaces, so it too can be eliminated.

Proposed Compound Document Interfaces:

The two useful methods from Embeddable will be split out into their own interfaces. Since the View Interface was so thin, the comparable interface will instead return a control and the ControlFrame implementation can be easily used to provide a proper Socket subclass.

i`nterface ControlFactory {`
`Control createControl (in ControlFrame frame,`
`in UIContainer uic);`
`};```

`interface CanvasComponentFactory {`
`Canvas::Component createCanvasComponent (in boolean aa,`
`in Canvas::ComponentProxy proxy);`
`};```

Dirty status is obtainable via the persist interfaces, but is currently duplicated in all the Persist subclasses. This method will be moved to Bonobo::Persist to eliminate duplication. Parallel discussions are occuring regarding Cut/Paste Drag/Drop related enhancements to the persist interfaces. These enhancements will provide even more utility to the container for managing subcomponents, and will form the basis for linking in the compound document environment. Persist and its subclasses are the workhorses for the model side of compound documents.

Any component wishing to be considered compound document ready would need to expose at a minimum, one of the above "view" interface factories, and either PersistStream or PersistStorage for save/load. Additionally, The existing Print interface should be exposed to provide a print rendering capability. The above interfaces should be sufficient for containers to layout and render subcomponents and manage their persistence.