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:

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:

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:

  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.

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:

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 44.

Figure 44. 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:

                       

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.

                 

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.

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 45, 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 45. 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 46. 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 46. Using Multiple Facets to Rearrange Portions of an Embedded Image



View figure.

The clip shapes for the facets in Figure 46 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 47 shows one example of how to use two subframes to implement a split-frame view.

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



View figure.

In Figure 47 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 47 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 48 shows, your part has only a single display frame and facet.

Figure 48. 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 48 shows the object relationships).


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