We've now seen the basic architecture, interfaces, and API functions involved in OLE Documents and have alluded to a number of user interface elements. Once again, a container has to provide a site object for each content object in the compound document, and each site object implements IOleClientSite and IAdviseSink. This is the easy part of a container. Much of the rest of the implementation involves elements of the user interface:
Shading the site when an object is active; unshading it when it reverts to the running or loaded state.
Invoking and handling the Insert Object dialog box. (One view of this is shown in Figure 17-1 on page 818.)
Asking the object to draw and print its view when necessary, watching for view changes.
Creating an object verb menu in both the container's main menu and a pop-up context menu, which is displayed when the user clicks the right mouse button on the content object. When the user selects a verb, the container activates the object with that verb. The container also activates the object with the primary verb (OLEIVERB_PRIMARY) when the user double-clicks the object's rectangle.
Pasting content objects, including content object formats in the Paste Special dialog box, from the clipboard and from drag-and-drop operations.
Copying the necessary content object formats back into a data object for use with the clipboard or with drag and drop. This lets the user copy objects from within the container.
Handling conversion and emulation of objects through the Convert dialog box. This includes changes between content and iconic presentations.
Optionally installing a message filter to invoke the Busy dialog box when a local server is occupied, as discussed in Chapter 6.
This list includes only those elements of the user interface relevant to embedded objects or to all content objects. In later chapters, we'll see additional requirements for linked objects as well as in-place activation. I won't include screen shots of all the various dialog boxes because they are fully documented in the OLE Programmer's Reference as well as in other Microsoft Windows documentation.
With these things in mind, you should think through the structure of an application before attempting to turn it into a container. Doing so will help you understand where these user interface elements, as well as other container requirements, affect that application. To understand the role of the container, let me indulge in what I call "The Allegory of the Cookie Jar."3
A content object is like a batch of cookies. Different cookies have their own form and taste, just as different objects have different classes and behavior. Cookies are great by themselves right out of the oven, but eventually we need to store the rest of the batch somewhere or the cookies will quickly get stale or be devoured by a pack of ravenous hounds (other members of the household). Where we decide to store these cookies depends on how we later want to get at them. Embedded Cookies are always stored inside a cookie jar; Linked Cookies are stored elsewhere, and what you put in the cookie jar is a treasure map (that is, a moniker) that describes where elsewhere is.
A container site is like a cookie jar because it holds (in both its variables and the storage it manages) whatever information makes up the content object. On the outside of this cookie jar is generally some sort of representation of the cookies inside. For our purposes, let's imagine a high-tech cookie jar with a video camera on the inside that transmits a picture of the current cookies to a small screen on the outside. This lets us see at any time exactly what's in the jar. This is equivalent to an always up-to-date graphical presentation for an object, and the video camera represents the availability of code that we can run to obtain new presentations for the object. However, such code is not always available—users take compound documents to other machines on which, perhaps, only the default handler is available. In this case, we have a broken video camera, so as a backup, the cache saves a photograph of the cookies taken at some time in the past (which might be out-of-date). Thus, we always have some reminder—perhaps a lower-quality one—of what is stored in the cookie jar.
What is now important for a container application to decide is where this cookie jar lives: the site must have a home in the rest of the application. Just as a cookie jar needs a shelf or a countertop, a site needs a page, a sheet, a document, or whatever. This is vitally important to decide before embarking on the task of implementing a container because the location will likely determine how the site provides storage to its object.
For example, the Patron sample that we'll make a container in the next section has internal objects called tenants. Each tenant lives on a page, each page lives in a document, and the document works with a compound file. The document owns the root storage for the compound file and gives each page a substorage. Each page then creates a deeper substorage for each tenant. In this way, Patron already has a substorage assigned to each tenant, and this is exactly the type of thing we can give to a content object. In fact, back in Chapter 12, we added this sort of support to enable tenants to contain static metafiles and bitmaps, using OLE's data cache as the object. In this chapter, we're ready to store active content objects in these tenants, so the tenants will become our container sites.
3 With apologies to Plato and his allegory of the cave. |