Programming Guide: Drawing

# Programming Guide

## Drawing

Fundamental to OpenDoc is the responsibility of each part in a compound document to draw itself, within the limits of the frame provided by its containing part. Drawing typically occurs when your part editor is asked to draw a particular facet of a particular frame in which its part is displayed. Your part editor is responsible for examining that facet and frame and displaying the correct data, with the appropriate representation, properly transformed and clipped.

Before drawing, your part editor should, if necessary, update the used shapes of its frames and the active shapes of its facets, so that the containing part can lay itself out correctly and so that only the proper events are dispatched to your part by the document shell. See "Run-Time Environment" for information on the document shell.

This section begins by discussing how your part defines the general characteristics of its display and outlining several aspects of the basic drawing process. The section then discusses:

- Drawing with scroll bars
- Drawing directly to the window
- Asynchronous drawing
- Drawing offscreen
- Use of multiple frames and facets
- Storing cached representation of your frames' content

### Defining General Display Characteristics

OpenDoc parts can display themselves in different ways in different frames or in different ways in the same frame at different times. The part is in control of its display within the borders of its frames, but there are conventions for other parts to request that the part display itself in a particular way.

There are two kinds of display categories. The view type of a frame indicates whether the part within it is shown as one of several kinds of icons (standard icon, small icon, or thumbnail) or whether the part content itself is drawn within a frame. Some view types are standard values defined by OpenDoc; any part should be able to draw itself in any of the standard view types. You can define your own view types also.

The presentation of a frame describes, for parts whose view type is framed, how the content is to be represented within the frame. Presentations are defined by parts. You define what types of presentations your part supports and you assign their values. Examples of presentations are top view, side view, wireframe, full rendering, table, bar chart, pie chart, and tool palette.

View type and presentation are represented as tokenized ISO strings of type ODTypeToken. If you create a presentation type, you define it first as an ISO string and then use the session object's Tokenize method to convert it to a token.

#### View Type

You can get and set the view type of your own frames by calling the frame's GetViewType and SetViewType methods, respectively.

In general, a part is expected to adopt the view type specified by its containing part. However, another part (such as your part's containing part) can change your frame's view type at any time, and you can change the view type of a frame of another part (such as one of your part's embedded parts) at any time, by calling the frame's ChangeViewType method. ChangeViewType sets the view type and then notifies the owning part, by calling the ViewTypeChanged method:

    void ViewTypeChanged (in ODFrame frame);

If your part receives this call, it should display itself in the specified frame according to the indicated view type. Parts must support all standard view types. If for some reason your part receives a request for a view type that it does not support, call your display frame's SetViewType method on your display frame to change it back to a view type that you support. (Calling SetViewType does not result in a call to your ViewTypeChanged method).

A change in view type can mean a change in the optimum size for your display frame. Your ViewTypeChanged method might therefore call your display frame's RequestFrameShape method at this point to initiate frame negotiations.

#### Presentation

You can get and set the presentation of your own frames by calling the frame's GetPresentation and SetPresentation methods, respectively.

Another part can change your frame's presentation, or you can change the presentation of another part's frame, by calling the frame's ChangePresentation method. In response, ChangePresentation sets the presentation and then notifies the owning part, by calling the part's PresentationChanged method:

    void PresentationChanged (in ODFrame frame);

If your part receives this call, and if it supports the indicated presentation, it should display itself in the specified frame accordingly. If it recognizes the indicated presentation but does not support it, or if it does not recognize it, your part should instead pick a close match or a standard default. It should then call the frame's SetPresentation method to give it the correct value. fileing SetPresentation does not result in a call to your PresentationChanged method.

#### Part Info Data Field

The part info data of a frame is a convenient place for your part to store information about that particular view of itself. The information can be anything from a simple ID to a pointer to a complicated structure or a reference to a helper object. A frame's part info is stored with the frame, not the part. Thus, if the part has many frames and if only a few have been read into memory, only the part info of those frames will be taking up space in memory.

To assign information to a frame's part info field, use its SetPartInfo method; to retrieve its part info, use its GetPartInfo method. Writing a frame's part info to its storage unit and reading it back in are described in "Reading and Writing Part Info Data".

The facet object also contains a part info field, which can hold any kind of display-related information that you wish, such as transforms, color information, pen characteristics, or any other imaging-related data.

To assign information to a facet's part info field, use its SetPartInfo method; to retrieve its part info, use its GetPartInfo method. A facet's part info is not persistently stored, so there are no methods for reading and writing it.

### Basic Drawing

This section discusses how your part editor performs its drawing tasks in typical situations.

To set itself up properly for display, your part should have previously examined the following information in the given facet and its frame:

- The view type of the frame tells your part whether it should display itself as an icon or whether it should show its contents. Your part should be ready to display itself in the specified frame according to the frame's view type.

- The presentation of the frame tells your part what kind of presentation (as defined by your own part) its content is to have, if it has frame view type. If your part does not support the frame's presentation, substitute a default presentation and call the frame's SetPresentation method to give it the correct value.

- Your part may be within a selection in its containing part. If so, it may need to be highlighted appropriately for the selection model of the containing part; see "Drawing Selections" for more information. You can call the GetHighlight method of your facet at any time to see whether you should draw your content with full highlighting, dim (background) highlighting, or no highlighting.

  Alternatively, when your part's HighlightChanged method is called, it can record the style of highlighting it has been assigned. It can then in turn call the ChangeHighlight method of all facets embedded in the facet whose highlight has changed, to make sure that they reflect the same highlight style.

- Check to see if you need to draw borders around any link sources and destinations in your part's content, as described in "Link Borders".

- Both the frame and the facet may have information in their part info fields, placed there by the part itself, that can provide additional display-related information or objects. Use the GetPartInfo method to obtain it.

- Your part should also be aware of the kind of canvas on which it is being drawn. You can call the GetCanvas method of the facet to get a reference to the drawing canvas.

  If the canvas is dynamic (that is, if the result of calling the canvas's IsDynamic method is kODTrue), the part is being drawn onto an interactive device like a video display. Otherwise, it is being drawn to a static device like a printer (or perhaps a print-preview image onscreen). Your part may display its content differently in each case; for instance, it might display scroll bars only on dynamic canvases, and it might engage in frame negotiation if it is being drawn on a static canvas. For considerations about static canvas, see "Printing".

  Your part can make these adjustments from within its FacetAdded and CanvasChanged methods. When a facet is added, the static/dynamic nature of its canvas is fixed and cannot be changed unless the canvas itself is changed.

### Draw Method of Your Part Editor

Your part must be able to display its content in response to a call to its Draw method:

    void Draw(in ODFacet facet, in ODShape invalidShape);

When it receives this call, your part editor draws the part content in the given facet. Only the portion within the update shape (defined by the invalidShape parameter) needs to be drawn. The update shape is based on previous Invalidate calls that were made involving this facet or containing facets, or on updating needed because of part activation or window activation. The shape is expressed in the coordinate system of the facet's frame.

Your part should take steps such as these to draw itself:

1. Your part must make sure that any required platform-specific graphics structures are prepared for drawing and that the drawing context for the facet is set correctly. You must at least do the following:

   - Obtain a handle to the platform-specific drawing context. You do this by calling the canvas' GetPlatformCanvas method to obtain the platform canvas object for the canvas. You must downcast the pointer of the platform canvas object to the platform-specific platform canvas type to be able to access the unique drawing context methods. On the Windows platform for instance, you should downcast to the platform canvas pointer to ODWin32Canvas, ODWin32StandardCanvas or ODWin32WindowCanvas and then call the GetDC method to obtain the device context.

     On the OS/2 platform, you should downcast to the platform canvas pointer to an ODOS2Canvas, ODOS2StandardCanvas or an ODOS2WindowCanvas and then call the GetPS method to obtain the presentation space.

     On the AIX platform, you should downcast to the platform canvas pointer to an ODAIXCanvas, ODAIXStandardCanvas or an ODAIXWindowCanvas and then call the GetGC method to obtain the graphics context.

     When determining what type of platform canvas to downcast to, the GetSystemType method of the ODPlatformCanvas class can be used determine the graphic system type. Additionally, the GetPlatformCanvasType can be used to determine whether the platform canvas is a "standard" (offscreen or static or both) or a "window" (onscreen, dynamic) platform canvas.

   - Set the default viewing transform of the platform-specific drawing context. Call your facet's AcquireContentTransform method to get the proper conversion from your part's content coordinates to canvas coordinates, and set the new viewing transform as appropriate for the platform. On the OS/2 platform, convert the transform to a MATRIXLF structure and pass the MATRIXLF structure to the GpiSetDefaultViewingTransform function.

     On the Windows platform, you would use the scale and offset of the transform to set the window origin with the SetWindowOrgEx function and the extents of the window and viewport using SetWindowExtEx and SetViewportExtEx, respectively.

     Because the AIX X-Windows does not provide native support for transformations, the programmer must "manually" apply the transform by applying it to all drawing coordinates before passing them to the drawing commands.

   - On the OS/2 platform, intersect the transformed invalid shape's region with the clip region which can be acquired by calling CopyAggregrateClipRegion() of the facet. Be sure to destroy the clip region when done. Now call GpiSetClipRegion() to set the drawing region. Otherwise, use the FocusLib utility to set the presentation space region and to replace the default view matrix.

     On the Windows platform, intersect the aggregate clip shape with invalid shape and then transform the intersected shape to find out the region. Note that after transforming the shape, you need to remove the offset because the window size is the same as the facet size. Next, call SelectClipRgn() on hdc to set the drawing region. Otherwise, use the FocusLib utility to set the presentation space region and to replace the default view matrix.

   - OpenDoc provides a utility (FocusLib) that facilitates this step for the OS/2, Windows, and AIX platforms. The source code for FocusLib is provided with OpenDoc.

2. If your part has placed embedded parts on an offscreen canvas, have the embedded parts draw themselves first; see "Updating an Offscreen Canvas".

3. Your part can then draw its contents, using platform-specific drawing commands.

   Your part can alter the normal order of drawing, perhaps to combine overlapping embedded parts and content elements to achieve a specific visual effect.

   - You can force the drawing of a given embedded part at any point by calling the embedded facet's Draw method.

   - Calling the embedded facet's DrawChildren method in addition forces the embedded part's own embedded parts to draw themselves immediately.

   - Calling the embedded facet's DrawChildrenAlways method forces the embedded parts to draw all of their content immediately, whether or not the content has been invalidated.

   - From within your parts draw method, you may also call the update method of your embedded facets, specifying kODNULL for the invalid shape. This will cause the embedded facet, and any of its embedded facets, and so on, to be updated as needed based on the current invalid shape.

Note:

> Unless you are the root part drawing in the root frame, never draw outside of the used shape of your display frame. Your part's containing part may be wrapping its content to the edges of your used shape.

#### Invalidating and Validating your Content

To mark areas of your part's content that need redrawing, you modify the update shape of the canvas on which your part is imaged. To do so, you call the Invalidate method of your display frames or their facets, passing a shape (invalidShape), expressed in your own frame coordinates, that represents the area that needs to be redrawn. OpenDoc adds that shape to the canvas's update shape, and when redrawing occurs again, that area is included in the invalidShape passed to your Draw method.

Likewise, to remove previously invalid areas from the update shape of the canvas, you call the Validate method of your frame or facet, passing a shape (validShape) that represents the area that no longer needs to be redrawn.

Validating is not a common task for a part editor to perform; OpenDoc automatically validates all areas that you draw into with your Draw method, and when you draw asynchronously, you automatically validate the areas that you have redrawn when you call the DrawIn method of the facet.

#### Drawing on Opening a Window

When a window opens, the facets for all the visible parts of the document are created when the window's Open method is called. The root part's FacetAdded method is called; it builds facets for its embedded frames and invalidates them. OpenDoc, in turn, calls the embedded parts' FacetAdded methods, so that they can build facets for their own embedded parts, and so on.

Each part editor whose frame is visible in the window (that is, whose frame has an associated facet) thus receives a call to its Draw method. The method draws the contents of its frame into the facet, by either using privately cached presentations or making standard drawing calls, taking into account the frame's and facet's aggregate transforms and clip shapes.

#### Drawing When a Window Is Scrolled

When the entire contents of a window is scrolled, the scrolled position of the contents is determined by the value of the internal transform of the root part's frame. If any of its embedded frames are made newly visible or newly invisible by scrolling, the root part should create or destroy their facets by calling the root facet's CreateEmbeddedFacet or RemoveFacet method, as described in "Adding a Facet" and "Removing a Facet".

To force redrawing of those parts of the window that need to be redrawn after scrolling, the root part marks the area that needs to be redrawn as invalid, by calling the root facet's Invalidate method, so that it will be redrawn when an update event occurs.

#### Redrawing a Frame When It Changes

If your part changes an embedded frame's shape, you may need to refresh its presentation as follows:

1. Change the frame's shape, as appropriate, by calling ChangeFrameShape. OpenDoc then calls the embedded part's FrameShapeChanged method, passing it the new frame shape.

   (Your part may receive a call to its UsedShapeChanged method as a result of changing the embedded frame's shape.)

2. Change the clip shape and, if necessary, the external transform of the frame's facet to correspond to the new frame shape and, possibly, new used shape.

3. If there is an undrawn area outside of the new facet shape, invalidate the portions of your own intrinsic content that correspond to the difference between the old and new frame shapes.

When the next update event is generated, the Draw methods of the embedded part and your part, as appropriate, are called to draw the invalidated areas.

#### Drawing When a Part Is Scrolled

When a user scrolls the contents of an embedded part, the containing part takes no role in the repositioning or redrawing. The embedded part sets its frame's internal transform to the appropriate value, as discussed in "Scrolling Your Part in a Frame", and calls the facet's Invalidate method to force the redraw.

If you are the part whose contents are to be scrolled, take these steps:

1. Call your frame's SetInternalTransform method, to set the transform's offset values appropriately. How you obtain the offset values you pass to SetInternalTransform depends on what kinds of events you interpret as scrolling commands and how you handle them. For example, handling events in scroll bars within your display frames is discussed in "Scrolling".

2. If any embedded frames become visible or invisible as a result of the scrolling, create or delete facets for them as described in "Adding a Facet" and "Removing a Facet".

3. Call your facet's Invalidate method to force a redraw at the next update event. You should be able to shift the bits of your frame's image by the scrolled amount and invalidate only the portion of your facet that represents part content scrolled into view.

Drawing the contents of frames that include scroll bars is more complicated, for two reasons. First, the content must be clipped so that it won't draw over the scroll bars. Second, although the content can be scrolled, the scroll bars themselves should not move. See "Drawing with Scroll Bars" for more information.

#### Drawing Selections

You determine how selecting works and how selections are drawn inside your parts.

When a part embedded within your part is selected, the appearance you give it should be appropriate for your own selection model but it should also reflect whether the part is selected by itself or is part of a larger selection that includes your own part's intrinsic content:

- To select an embedded part alone when it is viewed in a frame, the user can perform several actions, such as activating the part and then clicking on its frame border or using a lasso tool or other selection method supported by the containing part. (Your part should support all selection methods that are appropriate for your content model. Note that when a frame is selected, its part is not active; the menus displayed are those of the containing part).

  If the part alone is selected, your part (the containing part) should draw the frame border with a standard appearance. The standard frame border is typically a gray line corresponding to the frame shape with resize handles (usually eight of them) if you permit the user to resize the frame. If you allow nonrectangular frames (such as irregular polygons or circles), you are responsible for putting an appropriate number of resize handles on the frame border.

- To select an embedded part alone when it is viewed as an icon, the user places the mouse pointer over a part's icon (that is, anywhere within the active shape of the facet displaying the icon) and clicks the mouse button. Because the view type of the facet is iconic rather than framed, OpenDoc sends the mouse event to your part.

  You should, in this case, not draw the frame border of the embedded part at all. Instead you should notify the part that it is highlighted by calling the ChangeHighlight method of the embedded part's facet, specifying a highlight value of kODFullHighlight. It is then up to the embedded part to draw its highlighted appearance, either by using a highlight color or displaying the selected version of its icon.

- When an embedded part is enclosed within a range of your selected intrinsic content, its appearance should be analogous to that of the intrinsic content itself. You should draw the frame border with a selected appearance, if appropriate, and you should call the ChangeHighlight method of the embedded part's facet, so the part will know how it should highlight itself.

  - If your part highlights selections with a highlight color or inverse video (as text processors typically do), you should not draw frame borders around embedded parts within your selection, and you should set their facets' highlight value to kODFullHighlight.

  - If your part highlights selections by drawing frame borders around individual objects (as object-based drawing programs typically do), you should draw frame borders with a selected appearance around any embedded parts, and you should set their facets' highlight value to kODNoHighlight.

  - If your part highlights selections by drawing a dynamic marquee or lasso border around the exact area of the selection (as bit-map-based drawing programs typically do), you should not draw frame borders around embedded parts, and you should set their facets' highlight value to kODNoHighlight.

Your part should allow for multiple selection, so the user can select more than one frame at a time, and it should also support Select All on its own contents. Furthermore, it should allow for selection of hot parts (that is, parts such as controls that normally perform an action such as running a script in response to a single click) without executing the action.

### Drawing with Scroll Bars

This section discusses two methods for providing scroll bars for your parts. These approaches work not only for scroll bars, but also for any nonscrolling adornments associated with a frame.

#### Placing Scroll Bars within Your Frame

If you create scroll bars for your content inside the margins of your frame, remember that your frame shape includes the areas of the scroll bars. To draw properly, you need to account for the fact that, although your content can scroll, the scroll bars themselves should remain stationary.

Note:

> The method discussed in this section does not apply to a root part where the scroll bars are part of the document shell window frame control and are not within the root parts display frame.

Clipping and scrolling your content One approach is to create an extra, private "content shape," as shown in Figure 45.

Figure 45. Using a Content Shape within a Frame Shape for Drawing

View figure.

You can define the content shape in terms of frame coordinates, and you can store it anywhere, although placing it in your frame's part info data is reasonable.

When drawing your part's contents (that is, the portion within the area of the content shape) you take the current scrolled position of your content into account. You include the frame's internal transform by using the transform returned by GetContentTransform to set the origin, and you use the content shape as the facet's clip shape when drawing. If you will draw your own scroll bars, you ignore the current scrolled position of your content when drawing them. You use the transform returned by GetFrameTransform to set the default viewing transform, and you use the frame shape as the facet's clip shape. On the OS/2 platform, if you are using the PM scroll bar control window, you need to position the scroll bar window using the frame's external transform and set the window clip region, as described in the recipes "Facet Windows" and "Using Embedded PM Controls within a Facet (OS/2)". You should call WinUpdateWindow in your Draw method, specifying the handle of the scroll bar window after drawing your content.

Clipping embedded frames One complication of this method is that OpenDoc knows nothing of your content shape and therefore cannot clip the facets of embedded frames accordingly. OpenDoc clips all embedded facets to the area of your frame's facet, but that area includes the scroll bars as well. Thus, embedded parts can draw over the area of your scroll bars.

To avoid this problem, you must intersect the clip shapes of each embedded facet with your content shape before the facet draws itself.

#### Placing Scroll Bars in a Separate Frame

To avoid having to define a separate, private "content shape," you can place scroll bars or adornments in completely separate frames from your content. This method, however, requires the overhead of defining more objects, uses more memory, and may possibly affect performance negatively.

You can request one display frame for your part that encloses both the scroll bars and the content. This frame has an internal transform of identity; it does not scroll. If you are drawing your own scroll bars, draw the scroll bars directly in this frame.

You then request a second display frame for your part, a frame that can scroll and in which you draw your content. Make the second frame a subframe of the first; that is, make the nonscrolling frame the containing frame of the scrolling frame. Because both frames are display frames of the same part (yours), you must set the isSubframe parameter to true when you first create the new frame. This setting notifies OpenDoc that the frame is a subframe, so that OpenDoc draws the active frame border in the correct location around the facet of the parent frame.

Your part's containing part, as usual, must make separate facets for both frames. Then, when a mouse event occurs in the nonscrolling frame, your part can interpret it and set the internal transform of the scrolling frame accordingly.

With this method, you do not need to take any special care to manage clip shapes of any embedded facets.

A more common use of subframes may be in creating scrollable split windows. See "Drawing with Multiple Frames or Facets".

### Drawing Directly to the Window

In some circumstances, you may want your part editor to draw interactively, providing feedback for user actions. For example, you may support rubber-banding or sweeping out a selection while the mouse button is held down. If so, your part can draw directly on the window of the document shell.

OpenDoc provides several ODFacet methods, analogous to those used for drawing to your own canvas, that you can use to draw directly to the window:

- The AcquireWindowContentTransform method, analogous to AcquireContentTransform, converts from your part's content coordinate space to window-canvas coordinate space.

- The AcquireWindowFrameTransform method, analogous to AcquireFrameTransform, converts from your part's frame coordinate space to window-canvas coordinate space.

- The AcquireWindowAggregateClipShape method, analogous to AcquireAggregateClipShape, transforms and intersects your part's facet clip shape with all enclosing clip shapes, resulting in a composite clip shape in window-canvas coordinate space.

Use the values returned by these methods to set up your drawing environment when you must draw directly to the window canvas. When there is no canvas in the facet hierarchy other than the window canvas, the results returned by each of these pairs of methods are the same.

### Asynchronous Drawing

Your part editor may need to display its part asynchronously, rather than in response to a call to your Draw method. For example, a part that displays a clock may need to redraw the clock face exactly once every second, regardless of whether an update event has resulted in a call to redraw. Asynchronous drawing is very similar to synchronous drawing, except that you should make these minor modifications:

1. Determine which of your part's frames should be drawn. Your part can have multiple display frames, and more than one may need updating. Because your part stores its display frames privately, only you can determine which frames need to be drawn in.

2. For each frame being displayed, you must draw all facets. The frame's CreateFrameFacetIterator method returns an iterator with which you can access all the facets of the frame. (Alternatively, you can keep your own list of facets). Draw the part's contents in each of these facets, using the steps listed for synchronous drawing.

3. After drawing in a facet, call its DrawnIn method to tell it that you have drawn in it asynchronously. If the facet is on an offscreen canvas, calling DrawnIn allows the drawing to be copied into the window, because the owning part's CanvasUpdated method is called.

### Offscreen Drawing

There are several situations in which you may want your part to create an offscreen canvas. For example, you may want to increase performance with double-buffering, drawing complex images offscreen before transferring them rapidly to the screen. Alternatively, you may want to perform sophisticated image manipulation, such as drawing with transparency, tinting, or using complex transfer modes.

Parts are likely to create offscreen canvases for their own facets for double-buffering, in order to draw with more efficiency and quality. Parts are likely to create offscreen canvases for their embedded parts' facets for graphic manipulation, in order to modify the imaging output of the embedded part and perhaps combine it with their own output.

Canvases are described in general in "Canvases";

#### Drawing to an Offscreen Canvas

Drawing on an offscreen canvas is essentially the same as drawing on an onscreen canvas.

- You set up the environment as usual: you obtain the canvas and its pointer to the ODPlatformCanvas object, from which you obtain the platform-specific drawing context. You use your facet's transform and clip information as usual.

- If you draw asynchronously on your facet's offscreen canvas, you must, as for any asynchronous drawing, call the facet's DrawnIn method to ensure proper updating of the offscreen canvas to the window canvas. If your part's facet is moved to an offscreen canvas while it is running, OpenDoc calls your part's CanvasChanged method, so that your part will know to call DrawnIn if it is drawing asynchronously.

- You perform invalidation the same way for onscreen and offscreen canvases.

Call the Invalidate or Validate methods of your display frames and their facets, to accumulate the invalid area in the update shape of the offscreen canvas.

If you want to bypass the offscreen canvas and draw directly to the document shell window, make sure you obtain the window canvas when setting up the environment. Then use the appropriate facet calls (such as AcquireWindowContentTransform instead of AcquireContentTransform) when setting the default viewing transform and clip region.

#### Updating an Offscreen Canvas

The part that owns an offscreen canvas is responsible for transferring its contents to the parent canvas. Only the part that created the canvas can be assumed to know how and when to transfer its contents. The canvas may have a different format from its parent (one may be a bit map and the other a display list, for example); the owner may want to transform the contents of the canvas (such as by rotation or tinting) as it transfers, or the owner may want to accumulate multiple drawing actions before transferring.

When a containing part has placed one of its embedded part's facets on an offscreen canvas, it should force the embedded part to draw before the containing part itself draws any of its own contents. This ensures that the contents of the offscreen canvas are up to date, and can be combined safely with the containing part's contents when drawn to the onscreen canvas. You can force your embedded part to draw itself by calling the embedded facet's Draw method; you can force your embedded part's own embedded parts to draw themselves by also calling your embedded facet's DrawChildren method.

If an embedded part displays asynchronously and uses Invalidate or Validate calls to modify the update shape of its offscreen canvas, the offscreen canvas calls its owning part's CanvasUpdated method to notify it of the change. The owning part can then transfer the updated content immediately to the parent canvas, or it can defer the transfer until a later time, for efficiency or for other reasons.

### Drawing with Multiple Frames or Facets

OpenDoc has built-in support for multiple display frames for every part and multiple facets for every frame. This arrangement gives you great flexibility in designing the presentation of your part and of parts embedded in it. This section summarizes a few common reasons for implementing multiple frames or multiple facets.

#### Using Multiple Frames

Several uses for multiple frames have already been noted in this book, most of which involve the need for simultaneous display of different aspects or portions of a part's content.

- A part whose frame is opened into a part window requires a second frame as the root frame of that part window.

- A part that flows text from one frame to another naturally needs more than one frame.

- Parts that allow for multiple different presentations of their content are likely to display each kind of presentation (top view versus side view, wire-frame versus rendered, and so on) in a separate frame.

- Parts that display scroll bars, adornments, or palettes in addition to their own content might use separate frames for such items.

In general, the part that is displayed in a set of frames initiates the use of multiple frames. The containing part does, however, have ultimate control over the number and size of frames for its embedded parts.

#### Using Multiple Facets

The use of multiple facets allows a containing part to duplicate, distort, and reposition the content displayed in its embedded frames. Normally, even with multiple facets, all of the facets of a frame display their content within the area of the frame shape, because the frame represents the basic space agreement between the embedded part and the containing part. However, the containing part always controls the facets to be applied to its embedded frames, and so the containing part can display the facets in any locations it wishes.

##### Drawing an Embedded Frame Across Two Display Frames

Perhaps the most common reason to use multiple facets for a frame is to show an embedded frame spanning a visual boundary between separate drawing areas in a containing frame.

In Figure 46, for example, a word-processing part uses a single display frame and facet for each of its pages. An embedded part's display frame spans the boundary between page 1 and page 2. The word-processing part then defines two facets for its embedded frame: one facet in the display for page 1, and the other in the display for page 2.

Figure 46. Using Two Facets To Draw a Frame that Spans a Page Break

View figure.

##### Multiple Views of an Embedded Frame

One simple use of multiple facets is for a containing part to provide a modified view (such as a magnification) in addition to the standard view of an embedded frame. You can also use multiple facets to tile the area of an embedded frame with multiple miniature images of the frame's contents.

A similar but more complex example is shown in Figure 47. In this case, multiple facets are used to break up the image in an embedded frame, to allow the tiled parts to be moved, as in a sliding puzzle.

Figure 47. Using Multiple Facets to Rearrange Portions of an Embedded Image

View figure.

The clip shapes for the facets in Figure 47 are smaller than the frame shape and have offset origins so that they cover different portions of the image in the frame. Initially, as shown on the left, their external transforms have offsets that reflect the offsets in the shape definitions, and the facets just fill the area of the frame. Subsequently, as shown on the right, the containing part can cause any pair of facets to change places in the frame simply by swapping their external transforms.

##### Providing Split-Frame Views

One of the most useful implementations of multiple facets may be for the construction of split-frame views, in which the user can independently view and scroll two or more portions of a single document. This use of multiple facets is somewhat more complex than the others presented here, because it also involves use of subframes, embedded frames that display the same part as their containing frame. Figure 48 shows one example of how to use two subframes to implement a split-frame view.

Figure 48. Using Subframes to Implement a Split-Frame View

View figure.

In Figure 48 the part to be displayed has one frame (frame 1) and facet (facet 1) that represent its current overall frame size and display. The part creates frames 2 and 3 as embedded frames, specifying that the containing frame for both is frame 1, and also specifying that they are to be subframes of their containing frame. The diagram in the lower right of Figure 48 shows the object relationships involved, using the object-diagram conventions described in "Run-Time Object Relationships".

The part now needs to negotiate for frames 2 and 3 only with itself. It creates facets for them, embedded within facet 1. The part can now position the contents of frame 2 and frame 3 independently, by changing their internal tranforms. It uses the external transforms to place facets 2 and 3 within facet 1 as shown. The two facets together fill the area of frame 1 and show different parts of the document. The facet of the parent frame can display one portion of the split view and the facet of the subframe can display the other.

Note:

> You can achieve the same effect with a single subframe.

You can also achieve a split-frame view with a somewhat simpler method that does not require the use of subframes. In this case, your part must perform extra work to split its content display. The method relies on multiple facets only for duplicate display of embedded parts; as Figure 49 shows, your part has only a single display frame and facet.

Figure 49. Implementing a Split-Frame View Using a Single Display Frame

View figure.

You create two facets (subfacets of your display frame's facet) for each embedded part that is visible in both halves of the split view. By your own means, you separate the display of your part's intrinsic content and, at the same time, you apply the appropriate external transforms to each embedded facet so that embedded parts appear in their proper locations in each portion of your split view. (The diagram in the lower right of Figure 49 shows the object relationships).

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