Programming Guide: Working with Your Display Frames and Facets

# Programming Guide

## Working with Your Display Frames and Facets

Your part's display frames are the frames in which you draw your part's contents. This section discusses how to request or respond to changes and additions to those frames.

You do not directly create your own part's display frames; your part's containing part does that. When your part is first created or first read into memory from storage, the containing part of your part will in most cases have already provided at least one frame for your part to display itself in. However, it is possible that your part can be instantiated without already having a display frame. (see "Adding an Embedded Part"). Your part should allow for that possibility.

Your part should also allow for the possibility of multiple display frames. Your part must keep a list of its display frames, with enough information so that your part knows how to display itself in each and synchronize their displays when necessary. OpenDoc does not specify the format of that list. It is your responsibility to keep the list and to update each display frame that has been affected by any change to your part.

### Responding to Reconnected and Closed Display Frames

When the document containing your part opens and your previously stored display frames are read in and instantiated, OpenDoc calls your part's DisplayFrameConnected method. Here is its interface (this and all other method prototypes in this book are written in IDL):

    void DisplayFrameConnected(in ODFrame frame);

Your DisplayFrameConnected method should update your part's internal list of display frames and other related structures to reflect the addition of the frame. It should assign the frame's used shape, if different from the frame shape itself. The method should check the frame's content extent and update it if necessary, as described in "Content Extent". The method should also call the frame's SetDroppable method if you wish the frame to be able to accept drops.

Unlike for DisplayFrameAdded (see "Responding to an Added Display Frame"), your DisplayFrameConnected method does not normally have to create new part info data or a new set of embedded frames for the display frame, because they will have been created earlier.

When your part's document is closed, OpenDoc calls your part's DisplayFrameClosed method when it closes your display frame:

    void DisplayFrameClosed(in ODFrame frame);

Your DisplayFrameClosed method should

1. Update your part's internal list of display frames and other related structures to reflect the closing of the frame

2. Relinquish any focus owned by the frame (see "Relinquishing Focuses")

3. Call the Close method of all your embedded frames

If this frame is the root frame of your window and you have previously instructed OpenDoc not to dispose of the platform-specific window structure, you must dispose of the platform window yourself at this time.

Closing and reconnecting can happen at other times as well:

- When a part's editor is changed at run-time, OpenDoc calls the DisplayFrameClosed method of the editor losing the part and the DisplayFrameConnected method of the editor receiving the input.

- For efficient memory use, a containing part may not always keep frame objects in memory for all of its embedded frames. Instead, as described in "Reconnecting and Releasing Embedded Frames", it might release frames as they become invisible through scrolling, and recreate them as they become visible. Released frames can then be closed by OpenDoc.

Do not update your persistently stored display frames when your DisplayFrameConnected or DisplayFrameClosed methods are called; these methods should have no effect on stored data. In general, you should write your part content and frames to storage only when your Externalize or ExternalizeKinds methods are called.

### Responding to Added or Removed Facets

When your part's containing part has added a facet to one of your part's display frames, the display frame notifies your part of the addition by calling your part's FacetAdded method. The interface is:

    void FacetAdded (in ODFacet facet);

Your FacetAdded method must perform certain actions to handle the addition of the new facet to one of your frames. Some actions depend on the nature and implementation of your part itself, but others are standard:

- The method should store any private information that it needs as part info data in the facet that is being added. Although a facet's part info is not persistent, it can hold information that the facet needs for display.

- The method should assign an active shape to the facet, if needed. If you do not explicitly assign an active shape, OpenDoc uses the frame shape of the facet's associated frame for the active shape.

- The method should create facets for all embedded frames that are visible within the area of the added facet. See "Adding a Facet".

When a containing part removes a facet from one of your part's display frames, the frame notifies your part by calling your part's FacetRemoved method:

    void FacetRemoved (in ODFacet facet);

Your FacetRemoved method must perform certain actions to handle the removal of the facet. In general, this method reverses the actions performed by FacetAdded. Typically, the method should at least:

- Remove the facets for all embedded frames that were visible in the area of the removed facet

- Delete any part info data that was referenced in the facet

Facets are intended to be ephemeral objects. Do not hold references to them when they are no longer needed. Your FacetRemoved method should delete all references to the facets that it removes.

Note:

> If your part is the root part in a window, it receives a FacetAdded call from the root frame when the window is opened and a FacetRemoved call from the root frame when the window is closed.

### Resizing a Display Frame

Because of editing operations in your part, or because of an undesirable frame size imposed by your part's containing part, you may wish to change the size or shape of your display frame. You must negotiate this change with the containing part. (For an example of frame negotiation, showing the point of view of both the containing part and the embedded part, see "Frame Negotiation").

You start by requesting a new frame size from your part's containing part. Depending on its current contents and other constraints such as gridding, the containing part may grant the requested size, return a different size, or refuse the request by returning a size identical to your current frame size.

To request a new frame size, take these steps:

- Call the RequestFrameShape method of your display frame. The frame, in turn, calls the containing part's RequestFrameShape method to forward the request.

- The containing part may honor the request, or it may decide on a different (usually smaller) shape. It returns the shape it will let your display frame have. The frame stores it as its frame shape and returns it to you.

- Use the returned frame shape to update the used shape for that frame. At this time, you can also update the active shape for your frame's facet.

- If your part does not wish to accept the new shape, it can call the frame's RequestFrameShape method again, but with a different shape to avoid endless repetition of these steps. Alternatively, it can request an additional frame, as described in "Requesting an Additional Display Frame" (next).

### Requesting an Additional Display Frame

Your part may need a display frame in addition to the frame or frames already created by your part's containing part. For example, you may need an extra frame to flow content into another frame (as when you add another column or page of text), or you may need another frame to display your part with a new presentation.

To request another frame, you can call the RequestEmbeddedFrame method of the containing part. You must specify one of your current display frames as the base frame, the frame that defines certain characteristics of the new frame.

- The new frame is a sibling of the base frame; that is, it is embedded at the same level as the base frame.

- The new frame is in the same group as the base frame.

- The new frame has a sequence number within the base frame's frame group, assigned by the containing part. (By convention, the containing part adds the new frame to the end of the sequence in that group.)

You also pass additional information, such as the new frame's view type, presentation, and overlay status (that is, whether it should overlay, or float over, the content of the containing part).

The shape you request for the new frame is understood to be expressed in the frame coordinates of the base frame (see "Frame Coordinate Space"). Thus you can request a position for the new frame that is relative to the base frame (such as above, below, to the side, or overlapping), by specifying an origin that is offset from the base frame's origin. The containing part has ultimate control over frame positioning, however, and is not required to size or place the new frame exactly as you request. Furthermore, the frame shape returned by the containing part is by convention normalized; that is, the relative-positioning information has been stripped from it and its origin is at (0, 0).

The containing part responds to this call as described in "Adding an Embedded Frame on Request". Your part then responds as described in "Responding to an Added Display Frame" (next).

### Responding to an Added Display Frame

When an additional display frame is created, OpenDoc connects it to the part it displays. This connection ensures that frames are always valid and usable; the object that creates the new frame need do nothing beyond creating the frame itself.

To achieve that connection, the part displayed in the frame must respond to this method call, which informs the part that it has a new frame:

    void DisplayFrameAdded(in ODFrame frame);

Your part receives this call when it is first created and has no previously stored display frame, and also when additional display frames are created for it. In response to this call, your part's DisplayFrameAdded method performs the appropriate tasks. Most of them depend on the nature and implementation of your part itself; however, here are some general actions it should take:

- The method should add the new display frame to your part's list of display frames. This list, like other internal structures, is completely hidden from OpenDoc. You can represent the list any way you choose.

- The method should validate the view type and presentation of the new frame. Your part should accept any view types that are in the required set of view types, plus any other view types or presentations that you support. The DisplayFrameAdded method should correct these values, if necessary, as described in "Defining General Display Characteristics".

- The method should assign your part's current content extent to the frame using the frame's ChangeContentExtent method; see "Content Extent".

- The method should assign a used shape for the frame. If you do not specifically assign a used shape, the frame shape is used; a containing part that calls the frame's AcquireUsedShape method receives the frame shape in that case.

- The method should add any needed part info to the frame, by calling the frame's SetPartInfo method. See "Part Info Data Field" for more information.

- If the frame being added is the root frame of its window, your part may want to activate itself. Part activation is described in "How Part Activation Occurs". (This situation can occur only when your part is first opened into a window, such as a part window; it cannot happen when an additional display frame for the current window is created.)

- If the frame being added can accept dropped data, the method should call the frame's SetDroppable method, passing it a value of kODTrue. Otherwise, the frame will not be able to receive data through drag and drop. See "Drag and Drop" for more information.

Your part should not perform any layout or imaging tasks as a result of a display frame being added; specifically, it should not at this point negotiate with its containing part for a different frame shape. It should wait until its FacetAdded method is called, by which time the containing part has stored the frame shape and the frame has become visible.

Note:

> OpenDoc calls DisplayFrameAdded only when a frame is newly created. When your part opens and its stored display frame is recreated, OpenDoc calls your part's DisplayFrameConnected method; see "Responding to Reconnected and Closed Display Frames".

### Removing a Display Frame

To remove a frame in which your part is displayed, you call the RemoveEmbeddedFrame method of your part's containing part (see "Removing an Embedded Frame"). You can remove only those frames that you have previously requested through calls to your containing part's RequestEmbeddedFrame method. Your other display frames can be removed only by your containing part.

When the containing part receives the RemoveEmbeddedFrame call, it permanently severs all connection between your part and the frame (and the draft object may delete the frame from storage if its reference count becomes 0).

### Responding to a Removed Display Frame

Your part's containing part can also initiate removal of one of your display frames, as described in "Removing an Embedded Frame". The removal may occur, for example, if your frame is part of a selection that is cut or deleted. Regardless of whether your part or the containing part initiates the removal, OpenDoc calls your part's DisplayFrameRemoved method.

This is its interface:

    void DisplayFrameRemoved(in ODFrame frame);

In your DisplayFrameRemoved method, take steps such as these:

1. Relinquish any focus owned by the frame (see "Relinquishing Focuses" ).

2. Delete any part info that your part had associated with the frame. Set the frame's part info data to null by calling the frame's SetPartInfo method.

3. Update your part's internal list of display frames and other related structures to reflect the removal of the frame.

4. Remove in turn any embedded frames that your part had been displaying in the removed frame. Check the identity of the embedded frame's containing frame to determine which call to make:

   - If your (removed) display frame is designated as the embedded frame's containing frame, the display frame has not been moved from your part to another object. In this case, call the embedded frame's Remove method.

   - If your (removed) display frame is not designated as the embedded frame's containing frame, the display frame has been moved from your part to another object. In this case, call the embedded frame's Release method.

If this frame is the root frame of your window and you have previously instructed OpenDoc not to dispose of the platform-specific window structure, you must dispose of the platform window yourself at this time.

Note:

> OpenDoc calls DisplayFrameRemoved only when a frame is permanently removed from your part. When your part closes and OpenDoc stores its display frame, OpenDoc calls your part's DisplayFrameClosed method; see "Responding to Reconnected and Closed Display Frames".

### Grouping Display Frames

As an embedded part, your part does not directly control the frames in which it is displayed. However, if you need to flow text or other content through a sequence of separate display frames of your part (as for page-layout purposes) then you can request that the containing part create and assign the needed frames as a sequence in a single frame group. See "Creating Frame Groups". If your part's containing part adds a new frame to your display frame's group or reorders the sequence of one of your display frames within its group (by calling the frame's ChangeSequenceNumber method), OpenDoc calls your part's SequenceChanged method. This is its interface:

    void SequenceChanged(in ODFrame frame);

Your part can then retrieve the new sequence number of the frame by calling its GetSequenceNumber method.

### Synchronizing Display Frames

Sometimes, views of your part in two or more separate frames must be synchronized, meaning that any editing or other changes to the contents of one (the source frame) must force updating of the other. In some cases, such as when you open one of your display frames into a window, you can determine these dependencies internally. In other cases, you cannot. For example, if your part is an embedded part and your containing part creates multiple views of your part, your containing part asks you to synchronize those views by calling your part's AttachSourceFrame method. This is its interface:

    void AttachSourceFrame(in ODFrame frame,
                           in ODFrame sourceFrame);

Your AttachSourceFrame method should take whatever action is necessary to synchronize the frames; make sure that the result of any editing in one frame appears in the other. At a minimum, if the two frames have the same presentation, the method should duplicate all embedded frames in one frame into the other (and attach them to their source frames as well). It is the containing frame that determines when embedded frames must be synchronized. See "Synchronizing Embedded Frames".

### Adopting Container Properties

As described in "Transmitting Your Container Properties to Embedded Parts", a containing part can notify its embedded parts of the container properties, the characteristics of its content that it expects the embedded parts to adopt. For example, if your part is a text part and the user embeds it into another text part, the containing part may expect your part to adopt the general text appearance (font, size, stylistic variation, and so on) of the containing part.

Your part can, of course, adopt only those container properties whose format and meaning it understands. You obtain the set of container properties that your containing part makes available for adoption by calling the AcquireContainingPartProperties method of your containing part.

A containing part calls your part's ContainingPartPropertiesUpdated method when it changes any of the container properties available for your part to adopt. This is its interface:

    void ContainingPartPropertiesUpdated(in ODFrame frame,
                                         in ODStorageUnit propertyUnit);

Your ContainingPartPropertiesUpdated method should read and adopt any container properties that it understands from the provided storage unit. Then, it should in turn call the ContainingPartPropertiesUpdated method of any of your part's own embedded frames (other than bundled frames) that are displayed within the frame passed to ContainingPartPropertiesUpdated.

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