Programming Guide: Writing OpenDoc Software

# Programming Guide

## Writing OpenDoc Software

This section briefly summarizes some high-level aspects of the design and implementation of a part editor. It discusses:

- Issues related to developing with the System Object Model that underlies all OpenDoc classes

- Protocols that your part can participate in to accomplish its tasks

- Several development scenarios for creating OpenDoc software

### Developing with SOM and IDL

OpenDoc is implemented as a shared library consisting of a set of SOM classes. The interfaces to SOM classes must be written in the SOM Interface Definition Language (IDL) and compiled by the SOM compiler, usually separately from the implementations of the classes.

The implementation of OpenDoc objects as SOM objects has several advantages to its use as a shared library:

- SOM objects are dynamically bound. Dynamic binding is essential to the nature of OpenDoc, in which new parts can be added to existing documents at any time.

- All SOM objects, whether their implementations were compiled under different compilers in the same programming language or in different languages, communicate consistently and pass parameters consistently.

- Unlike with other object-oriented architectures, the modification and recompilation of a SOM class do not necessarily require the subsequent recompilation of all of its subclasses and clients.

Because OpenDoc consists of SOM classes, the class ODPart is naturally a SOM class. If you want your part editor, which is a subclass of ODPart plus any other classes that you define, to also consist of SOM classes, then you must write your interfaces in IDL, separately from your implementations, and you must compile them with the SOM compiler. The result of the compilation is a set of header and stub implementation source files, in one of the procedural or object-oriented programming languages supported by the SOM compiler. You complete your development by writing your implementations into the stub implementation files and compiling them, along with the headers, using a standard compiler for your programming language.

If you write your part-editor interfaces in IDL, you will notice that the IDL syntax is very similar to that of C and C++. It includes essentially the same character set, white space rules, comment styles, preprocessing capabilities, identifier-naming rules, and rules for literals. But there are a few differences in source-code appearance to note when declaring or calling methods of SOM-based objects:

- In IDL method declarations (function definitions), each parameter declaration is preceded by a directional attribute ("in", "out", or "inout") that notes whether the parameter is used as an input, or as a result, or as both. (For example, `void FacetAdded (in ODFacet facet);`)

- The C++ interface to any method of a SOM object includes an extra initial parameter, the environment parameter (ev), used by all SOM methods to pass exceptions. See "SOM Exception Handling" for more information.

- The C interface to any SOM method includes another extra parameter, before the environment parameter, specifying the object to which the method call is directed.

Advantages of making all your classes SOM classes include a greater ability to develop portions of your part editor (or set of editors) using different programming languages and compilers, and a lesser need for recompilation of all of your code when you change portions of it. These advantages may be compelling only if your libraries are very large, however, because they must be balanced against the disadvantages of working in both IDL and a separate programming language.

You are not required to make your part-editor classes SOM classes. If you are developing in C++, for example, you can use C++ classes instead. The generally preferred procedure is to create only one SOM class, a subclass of ODPart whose interface contains nothing but your public methods (overrides of the methods of ODPart and its superclasses). That SOM class delegates all of its method calls to a C++ wrapper class, which contains the functionality for the public methods as well as any private fields and methods. Additional classes can be subclasses of your C++ wrapper class.

Advantages of developing in C++ with a single wrapper object include a lesser need to work with interfaces in two languages (IDL and C++), a smaller memory and calling overhead for your objects, and the availability of C++ features (such as templates) that are not supported by SOM.

#### SOM Class ID and Editor ID

A SOM class ID is an ISO string whose format is "module::className". A part editor's editor ID is a SOM class ID that uniquely defines the editor; you need to specify your editor ID in your editor's IDL interfaces. For example, the editor ID for AcmeGraph 1.0 might be "Acme::AcmeGraph". Editor IDs are used for binding; see "Information Used for Binding" for more information.

For a more detailed description of the Interface Definition Language and instructions on programming with SOM, see, for example, the SOMObjects Developer Toolkit Users Guide and SOMObjects Developer Toolkit Programmers Reference

### OpenDoc Protocols

OpenDoc imposes very few restrictions on how your part editor does its job. The inner workings of your editor's core data engine are of little concern to OpenDoc. The engine should be able to free storage when requested, it should adequately handle cases where the part can be only partially read into memory, and it should handle any multiprocessing or multithreading issues that arise. Other than that, it simply needs to provide an interface to OpenDoc and use the OpenDoc interface to accomplish OpenDoc-related tasks.

The programming interfaces that your part editor uses (and provides) to perform specific tasks are called protocols. The methods of ODPart, for example, participate in approximately 12 protocols, such as part activation and undo. This section briefly describes the protocols; and Table 2 lists the methods of ODPart that you will have to override to participate in each protocol.

#### Participatation in Protocols

To implement the simplest possible part, you need to participate in only some OpenDoc protocols, and you need to override only some methods of ODPart. As a minimum, your part editor must be able to:

- Draw its part
- Retrieve its part's data
- Handle events sent to its part
- Store its part's data, if it permits editing

Unless it creates extremely simple parts, your part editor must also provide some sort of command interface to the user. It must then be able to

- Activate its part
- Handle menus, pop-ups, and help
- Handle windows, dialog boxes, and palettes

If you wish your parts to be able to contain other parts, your part editor must be able to

- Embed frames and manipulate them
- Create facets for visible frames
- Store frames

Beyond these capabilities, you may want your parts to have additional capabilities, such as drawing themselves asynchronously, providing data transfer capability, supporting scripting, or others. You can add those capabilities by overriding other methods of ODPart.

#### Overriding the Methods of ODPart

Your fundamental programming task in creating an OpenDoc part editor is to subclass the class ODPart and override its methods. The following list summarizes the OpenDoc protocols that your part editor can use, and lists the sections in this book that describe each protocol more fully. To create full-featured container parts, your editor must support all of these protocols.

The methods of ODPart involved in each protocol are shown in the table that follows this list.

- Layout. The layout and imaging protocols together make up the drawing process. Your part uses the layout protocol for frame manipulation and for facet manipulation.

  - Frame manipulation includes adding and removing display frames, setting frame characteristics and order, opening frames into windows, and performing frame negotiation (modifying embedded-frame shapes on request). Your part interacts with its containing part and with its draft object to modify your display frames.

  - Facet manipulation is the adding, altering, or removing of facets of frames that are modified or scrolled into or out of view. You interact with existing facet objects to add or manipulate embedded facets. OpenDoc uses the facet hierarchy constructed by the layout protocol for passing geometric events like mouse clicks to the appropriate part editor.

  All parts that are visible must participate in this protocol. The layout protocol is described, along with the embedding protocol, in "Frame and Facet Hierarchies".

- Imaging. Your part uses the imaging protocol after the layout protocol, to draw itself appropriately in each of its display frames. Drawing may occur asynchronously, it may occur in response to update events, and it may occur when activation or deactivation affects highlighting. Drawing can be to a video display or to a printer. You interact with frames, facets, canvases, transforms, and shapes during the imaging process.

  All parts that are visible must participate in this protocol. The imaging protocol is described in "Canvases", "Transforms and Shapes", "Drawing", and "Printing".

- Activation. Through this protocol your part activates and deactivates itself (and its frames and facets). Your part interacts with the arbitrator to change ownership of the selection, menu, and keystroke focuses, and to notify the parts and frames involved of the changes.

  Your part must participate in this protocol if it ever needs to be active or if it needs any of the other focus types. The activation protocol is described in "Focus Transfer".

- User events. OpenDoc uses this protocol to distribute events such as keystrokes and mouse clicks to the appropriate part editors. The document shell, the arbitrator, and the dispatcher use focus-ownership information and the facet hierarchy to deliver these events to your part.

  Your part must participate in this protocol if it handles events. The user-events protocol is introduced in "User Event Handling" and is described in more detail in other parts of "User Events".

- Storage. Your part uses the storage protocol to write its content and its state information persistently in its document, and subsequently to retrieve it from the document. You interact with your draft object and your part's storage unit when reading and writing your part, and you may also interact with other drafts and storage units when using the clipboard, drag-and-drop, and linking protocols.

  All parts must participate in this protocol. The part-storage protocol is described in "Storage Units, Properties, and Values" and "Documents, Drafts, and Parts".

- Binding. The OpenDoc document shell controls the runtime binding of your part editor to the parts in a document that it can edit. Your part's methods are not called during binding. The binding protocol is discussed in "Binding".

- Embedding. Your part uses this protocol to embed other parts within itself and to interact with those parts.

  Your part participates in this protocol if it is a container part (that is, if it is capable of embedding parts within itself). The embedding protocol is described in "Frame and Facet Hierarchies".

- Clipboard. The clipboard protocol allows the user to use menu commands to move content elements and embedded parts into or out of your part, from or to a system-maintained buffer. Your part interacts with the clipboard object both when copying to the clipboard and when pasting from it.

  Your part must participate in this protocol if it supports clipboard transfer. The clipboard protocol is described in "Clipboard Transfer".

- Drag and drop. The drag-and-drop protocol allows the use of direct manipulation to move content elements and embedded parts into or out of your part or within your part. Your part interacts with the drag-and-drop object.

  Your part must participate in this protocol if it supports drag and drop of its content elements or embedded parts. The drag-and-drop protocol is described in "Drag and Drop".

- Linking. Your part can use the linking protocol to export updatable data to another part or to incorporate or embed an updatable copy of another part's data into your part. If your part is the source of a link, you interact with a link-source object; if your part is the destination of a link, you interact with a link object.

  Your part must participate in this protocol if it supports linking. The linking protocol is described in "Linking".

- Undo. Your part uses this protocol to give the user the capability of reversing the effects of recently executed commands. Your part interacts with the undo object to accomplish this.

  Your part must participate in this protocol if it has an undo capability. The undo protocol is described in "Undo".

- Extensions. The extension protocol is a very general mechanism for extending your part's capabilities; it consists of an interface in the form of a specialized extension object that other part editors can access.

  Your part participates in this protocol if it provides OpenDoc extensions. The extensions protocol is described in "OpenDoc Extension Interface".

- Memory management. The OpenDoc document shell manages the memory needed for the document containing your part.

  In general, your part editor does not need to be concerned with memory management except to make sure that it deletes or releases objects that it has created or obtained. The memory-management protocol and the use of reference counting is further discussed in "Creating and Releasing Objects".

Table 2 lists the methods of ODPart that you must override to have a functioning part editor, as well as those that you can optionally override to participate in specific protocols. Note that some protocols, such as layout, imaging, and activation, are required of all part editors, and you therefore must override some or all of the methods associated with them even if you do not provide any code to implement the method. Other protocols, such as embedding or undo, are not required, and you therefore need not override any of their methods if your parts do not participate. It is, of course, strongly recommended that your parts participate in all protocols that are appropriate to their content model.

Table 2. Required and Optional ODPart Overrides

Protocol | Required overrides | Optional overrides
--- | --- | ---

Layout

    AttachSourceFrame
    ContainingPartPropertiesUpdated
    DisplayFrameAdded
    DisplayFrameClosed
    DisplayFrameConnected
    DisplayFrameRemoved
    FacetAdded
    FacetRemoved
    FrameShapeChanged
    GeometryChanged
    Open
    SequenceChanged

    AcquireContainingPartProperties*
    RevealFrame*

Imaging

    CanvasChanged
    Draw
    GetPrintResolution
    HighlightChanged
    PresentationChanged
    ViewTypeChanged

    AdjustBorderShape*
    CanvasUpdated*

Activation

    AbortRelinquishFocus
    BeginRelinquishFocus
    CommitRelinquishFocus
    FocusAcquired
    FocusLost

User events

    AdjustMenus
    HandleEvent

CreateRootMenuBar

Storage

    CloneInto**
    ClonePartInfo
    Externalize**
    ExternalizeKinds
    InitPart
    InitPartFromStorage
    ReadPartInfo
    WritePartInfo

    somInit**
    somUninit**

Binding | ChangeKind |

Memory Management

ReleaseAll**

    Acquire**
    Purge**
    Release**

Linking

LinkStatusChanged

    BreakLink
    BreakLinkSource
    CreateLink
    EditInLinkAttempted*
    FulfillPromise***
    LinkBroken
    LinkUpdated
    RevealLink
    ShowLink
    UpdateFromLinkSource

Embedding

    CreateEmbeddedFramesIterator*
    EmbeddedFrameUpdated
    RemoveEmbeddedFrame*
    RequestEmbeddedFrame*
    RequestFrameShape*
    UsedShapeChanged*

Clipboard |  | FulfillPromise***

Drag and Drop

    DragEnter
    DragLeave
    DragWithin
    Drop
    DropCompleted
    FulfillPromise***

Undo

    DisposeActionState
    ReadActionState***
    RedoAction
    UndoAction
    WriteActionState***

Extensions

    AcquireExtension**
    HasExtension**
    ReleaseExtension**

  * Required of all parts that support embedding
  ** Defined in a superclass of ODPart
  *** Optional even if you implement this protocol

Generally, you must override all of the optional methods listed for a given protocol (other than those marked with *** in Table 2) if you are to participate in that protocol. For example, to participate in the extensions protocol, you must override methods AcquireExtension, HasExtension, and ReleaseExtension. The embedding protocol has even further requirements; to support embedding, you must not only override all the optional methods listed for that protocol, but you must override several methods associated with other protocols (marked with * in Table 2).

### Development Scenarios

This section provides a high-level discussion of several possible OpenDoc development scenarios. Reading the scenarios may help you to decide what kinds of OpenDoc component software you are most interested in developing. Specifically, it discusses

- Creating a part editor for noncontainer parts
- Creating a part editor for container parts
- Converting a conventional application into a part editor
- Developing OpenDoc components that are not part editors

#### Writing an Editor for NonContainer Parts

Writing a part editor that does not support embedding is somewhat simpler than writing an editor that does. Furthermore, if you are starting from scratch (not modifying an existing application), you are free to consider all aspects of the design of your part editor before you implement anything.

- Content model. Create a content model that defines the functioning of your part editor and the OpenDoc protocols that it participates in. If your parts are to be scriptable (see the Scripting step), your part's content objects and operations must reflect that content model.

- Core engine. Design and implement your core data engine, the set of data structures and behaviors that manifest the basic purpose of your editor.

- Storage. Implement persistent storage in these situations:

  - Use the OpenDoc storage system to store your part's data. Your part editor must implement code to initialize a part and to write it back to storage.

  - Implement clipboard and drag-and-drop capabilities to transfer information between parts, using the same OpenDoc storage concepts for data transfer as for persistent storage.

  - Implement linking support, using a combination of event-handling code and storage code (similar to support for document storage, clipboard, and drag and drop).

- Drawing. Give your part the capability of drawing its content, properly transformed and clipped, in whatever combination of facets and frames the content appears, onscreen or offscreen, and for screen display or for printing.

- Event handling. Give your part editor the capability of responding to user events such as mouse clicks and keystrokes. It must also respond properly to activation and deactivation events and must use the OpenDoc user interface objects such as members, pop-ups, help, and status line.

- Scripting. To support scripting, you must first define what methods, properties, and events your part will be exposed to scripting. Modify your IDL with the correct identifiers to describe your part to the scripting environment. See "Direct Scripting".

- Extensions. If you plan to extend the capabilities of your parts to communicate with other parts or process information fast, create and attach an extension interface to your part editor. To obtain existing public extension interface designs or to propose a new public interface, contact CI Labs.

- Packaging and shipping. Once your part editor is complete, provide information for the OpenDoc binding process to use, indicating what part kinds are handled.

You ship your product as one or more part editors, plus an install program and accompanying documentation. Users install your part editor into their systems and then, using the stationery part that gets control at part registration time, create new documents of your part kind or insert new parts with your part kind into their documents. See "Stationery" for information about stationery. Rules and conventions for installing part editors and storing documents vary among platforms. For platform rules, see the recipe "Installation of OpenDoc Software".

Users themselves can create additional, customized stationery documents. Users will be able to exchange documents, including stationery, freely.

#### Writing an Editor for Container Parts

If your part editor is to support embedding (that is, if its parts are to be container parts) you need to include the following additional steps:

- Embedding. You need to add embedding support to your content model and to storage.

  - Your content model needs to include a type of content element that represents an embedded part. If your part editor supports semantic events, embedded parts must be content objects to which you can pass events.

  - Make sure your parts can store embedded parts, both in their documents and during data transfer. This is relatively simple to implement; OpenDoc takes care of most of it.

- Layout management. You need to add support for layout management during event handling and during frame negotiation.

  - Your part editor must be able to maintain updated information on embedded frames and facets and notify embedded parts of such changes. It must add facets when embedded frames become visible. It must modify or delete facets when embedded frames move or become no longer visible.

  - Your part editor must include support for layout negotiation, including updating the shapes and transforms associated with each visible embedded frame and facet.

For a summary of the issues to consider in creating a part editor for container parts, see Appendix A. "Embedding Checklist".

#### Converting a Conventional Application to a Part Editor

Creating a part editor from an existing conventional application involves maintaining its core features but repackaging them for the OpenDoc environment.

- Content model and core data engine. You should have your content model and core data engine already in place. You may need to separate your core data engine from other facilities, such as user interface and storage, if it is not already sufficiently separated.

- Storage. You must refit all file I/O and clipboard data transfers into OpenDoc terms, as described for part editors in the Storage step in "Writing an Editor for NonContainer Parts".

- Event handling. Because your part editor will receive its event information from the document shell, you need to remove or modify your event loop and event-handling code.

- Scripting. You can add scripting support, as described for part editors in the Scripting step in "Writing an Editor for NonContainer Parts".

- Extensions. You can extend the capabilities of your parts, as described for part editors in the Extensions step in "Writing an Editor for NonContainer Parts".

- Packaging and shipping. Package and ship your part editor, as described for part editors in the Packaging and Shipping step in "Writing an Editor for NonContainer Parts".

If your converted application is to be a container part, you need to follow these additional steps:

- Embedding. Add embedding support, as described for part editors in the Embedding step in "Writing an Editor for Container Parts".

- Layout management. Add layout-management code, as described for part editors in the Layout Management step in "Writing an Editor for Container Parts".

#### Writing Other Types of Component Software

This book primarily describes how to create a part editor. However, you are not limited to that alone. Development of container applications is discussed briefly in the previous section. Using the OpenDoc class libraries, you can also create other kinds of OpenDoc component software:

- Part viewers are simply part editors with their editing and saving capabilities removed. A part viewer should be able to read, display, and print its parts.

  Development issues for part viewers are a subset of those for fully functional part editors. A part viewer is usually much smaller than its corresponding editor, and it can be developed from the same code base.

  To encourage data interchange, you should always create a part viewer for every part editor you develop, and you should distribute the viewer widely and without cost or obligation to the user.

- Part extensions are objects with which you can extend the programming interface of your part editor. Extensions work in conjunction with part editors, allowing their parts to communicate with and directly manipulate other parts. Extensions are described in "Extending OpenDoc".

- Document-shell plug-ins are extensions to the capabilities of the shell. They are DLLs that you can write and install. Shell plug-ins are described in "Shell Plug-Ins".

- Part or document services are OpenDoc components that, instead of editing and saving parts in a document, provide specialized services to those parts and documents. Spelling checkers, database-retrieval engines, and network connection services are all examples.

  You develop an OpenDoc service much as you develop a part editor. Like part editors, services are subclasses of ODPart. However, services commonly use less of the embedding, layout, and imaging protocols of OpenDoc, and they usually communicate with the parts they serve through an extension interface (a subclass of ODExtension). The extension interface is described in "Extending OpenDoc".

[ Top | Previous | Next | Contents | Index | Documentation Homepage ]