Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Programmer's Guide / Part 2 - Programming
Chapter 6 - Windows and Menus


Windows

Windows are platform-specific data structures through which documents display themselves. This section discusses how to use OpenDoc window objects (which are basically wrappers for those structures) regardless of which platform you are developing for. Some information specific to the Mac OS is also provided where appropriate. For more detailed Mac OS programming information on windows, see Inside Macintosh: Macintosh Toolbox Essentials.

Creating and Using Windows

The OpenDoc class ODWindow is a wrapper for a pointer to a platform-specific window structure. For some operations, your part editor must retrieve the window pointer from the ODWindow object and use the platform's facilities. In most cases, however, the interface to ODWindow provides the capability you need for interacting with your windows.

The Window State Object

There is a single instantiated window state object (class ODWindowState) for each OpenDoc session. The window state consists of a list of the currently existing window objects. You can access all open windows in a session through the window state.

The document shell and dispatcher use the window state to pass events to parts so that they can activate themselves, handle user input, and adjust their menus as necessary. A part may be displayed in any number of frames, in any window of a document. The dispatcher uses the window state to make sure that it passes events to the correct part, no matter what window encloses the active frame and how many other frames the part has.

Normally, your part editor calls the window state only when it creates new windows, when it needs access to a particular window, and when it needs access to the base menu bar object.

If for some reason your part needs access to all windows, you can create an ODWindowIterator object, which gives you access to all windows referenced by the window state.

Creating and Registering a Window

To receive events in a window, you generally must create an OpenDoc window object for it. (You may not be able to create an OpenDoc window for a Mac OS modal dialog box, but you can pass an event filter routine to it and therefore receive Mac OS events.) Windows in OpenDoc are created and maintained through the window-state object, which you access through the session object.

You first create a window with platform-specific calls; you then call the window state object to create an OpenDoc window object describing the platform-specific window. You call either of two methods:

A window has an is-root property. If the property is true, the window is a root window, which is the same as a document window. The root part of a root window is the root part of its document, and the document cannot close as long as the root window is open. If a window's is-root property is false, the window may be either a part window that has been opened from a source frame within a root window, or it may be a dialog box, palette, or other utility window. OpenDoc permits multiple document windows for a single document, as long as the root part provides a user interface to support this feature. The document shell closes a document when the document's last document window (root window) is closed.

Windows also have a should-save property that, if true, specifies that the state of the window is saved persistently after the window closes. Usually, only document windows should be marked as should-save.

The creator of a window can specify the view type and presentation of the root frame, the frame that displays the root part. The view type specifies whether the root part should draw itself as an icon, and the presentation specifies what kind of appearance the part content should have if not drawn as an icon. View type and presentation are suggestions to the part editor that draws within that frame. View type and presentation are described in more detail in the section "Defining General Display Characteristics".

OpenDoc assumes that each window has a single canvas, which is attached to the window's root facet, the facet created for the root frame. On the Mac OS platform, the root frame in the window has the same shape as the window's content region: it includes window scroll bars but excludes the window's resize box, if present.

Your part should create windows as invisible and then make them visible as described in "Opening a Window"

Allocating Window Memory Efficiently

On the Mac OS platform, you can save on the total amount of memory required for your part's document by allocating your windows in system memory rather than in the application heap. If you do so, however, you are responsible for disposing of the platform-specific window structure yourself. Follow these steps:

  1. Allocate the platform-specific window in temporary memory, by using calls such as this:

  WindowPtr myWindow = GetNewCWindow(kWINDID,

(Ptr)ODNewPtr(sizeof(WindowRecord)), (WindowPtr)-1L);

(ODNewPtr is a function of the ODMemory utility library, supplied with OpenDoc; when called in this manner it allocates the pointer in temporary memory.)

  1. When registering the window and retrieving the OpenDoc window object, pass a value of kODFalse for the shouldDispose parameter of RegisterWindow or RegisterWindowForFrame. This value tells OpenDoc not to dispose of the underlying platform-specific window.
  2. Add code to your DisplayFrameClosed and DisplayFrameRemoved methods to dispose of the platform-specific window if the frame being removed or closed is a root frame.

Opening a Window

After creating a window, your part editor typically makes calls to these three methods, in this order:

  1. the window's Open method, which creates the root facet
  2. the window's Show method, which makes the window visible
  3. the window's Select method, which brings the window to the front

Window IDs

Your part editor should not maintain references to ODWindow objects for accessing OpenDoc windows, because the document shell or the window state object can close a window and invalidate the reference. Instead, the window state assigns window IDs that are valid for the length of a session. Use the window's GetID method to get the ID of a window when you create it, and then pass that ID to the window state's AcquireWindow method for subsequent access to the window.

Closing a Window

If your part editor needs to close a window programmatically, it calls the window's CloseAndRemove method. That method closes the window, releases the window object and disposes of the platform-specific window structure, deletes the root facet and canvas, and removes the root frame from the document. It also makes any necessary platform-specific calls to dispose of the window itself.

Storing and Retrieving Window Characteristics

Whenever a document is saved, OpenDoc writes certain information into a storage unit referenced from the storage unit of the window's root frame. The window's bounding rectangle, title, and other characteristics are saved in a property of type kODPropWindowProperties in that storage unit.

When you create a root window, you retrieve that information from the stored frame and use it to specify the platform-specific window's characteristics. You can use functions of the WinUtils utility library (provided with OpenDoc) to extract that information, or you can access the frame's storage unit directly. The kODPropWindowProperties property contains a persistent reference to another storage unit, which (for Mac OS windows) contains the properties listed in Table 6-1.
Table 6-1 Window characteristics stored in a root frame
kODPropWindowRectkODPropWindowIsResizable
kODPropWindowTitlekODPropWindowIsFloating
kODPropWindowProcIDkODPropWindowIsRootWindow
kODPropWindowHasCloseBoxkODPropShouldShowlinks
kODPropWindowRefConkODPropSourceFrame
kODPropWindowIsVisible 

Making sure a window is onscreen
If your part is a root part that recreates a previously stored document window, you must make sure that the window is visible onscreen. Your document may have been moved from one system to another with a different monitor configuration or size. You may need to move or resize the window to fit its new environment.

The Open Method of Your Part Editor

Opening your part means creating a window for it and displaying it in the window. You have to open a window only if your part is to be the root part of that window.

Your part itself initiates the opening of a window when the user selects the View in Window command from the Edit menu (see "View in Window"), and when it creates its own dialog boxes. Otherwise, your part opens a window only when your part's Open method is called. This is the interface to the Open method:

ODID Open(in ODFrame frame);
The Open method is called in these circumstances.

In your implementation of Open, you can take steps similar to the following, depending on the circumstances under which it was called.

  1. If you are creating an initial window (frame = null), skip this step and
    go to step 2.

    • If you are opening a frame into a part window (frame = an embedded frame), check whether the window already exists. If you have created the part window previously and saved its window ID, pass that ID to the AcquireWindow method of the window state object. If the method returns a valid window, bring the window to the front and exit. If the window does not yet exist, go to step 2.
    • If you are opening a stored document into a window (frame = a root frame), read in the saved window data from the storage unit of the frame passed to the method (see "Storing and Retrieving Window Characteristics").

  2. Create a platform-specific window and register it with the window state object, as described in the section "Creating and Registering a Window".

    • If you are opening a stored document into a window, apply the stored characteristics to the platform-specific window. Call the window state object's RegisterWindowForFrame method.
    • If you are opening a frame into a window or creating an initial window, apply your default characteristics to the platform-specific window. Call the window state object's RegisterWindow method.

  3. Get the window's window ID and save it for future reference.
  4. Open and bring the window to the front, as described in "Opening a Window". If you are opening a stored document into a window, skip this step because there may be more than one window to open and OpenDoc determines which window is to be frontmost.

Note
You are not absolutely required to open a window when your Open method is called. Your part does whatever is appropriate, given its nature and the conditions under which the method is called. For example, if your part is a very simple sound player, it might simply play its sound and never create a window.

Handling Window Events

To receive events in the windows that it creates, your part must create an ODWindow object for each platform-specific window it uses, including dialog boxes (except for modal dialog boxes; see "Modal Dialog Boxes").

The document shell handles most Mac OS window events outside of the content region--for example, events in the title bar or resize box. Nevertheless, the OpenDoc dispatcher first sends a kODEvtWindow event to the root part. If the part editor of the root part wishes to override the action that the document shell would otherwise take, the root part can intercept and act on the event. In situations such as zooming, in which the window content might affect the action to be taken, the root part should intercept and handle the event.

Table 6-2 lists the window events that a root part can intercept and handle. These event types are passed in the message field of the event structure.
Table 6-2 Window event types
ConstantEvent location for Mac OS window
kODMDInDragIn the drag region (title bar)
kODMDInGrowIn the resize box (lower-right corner of window)
kODMDInGoAwayIn the close box (left edge of title bar)
kODMDInZoomInIn the zoom box (right edge of title bar)
kODMDInZoomOutIn the zoom box (right edge of title bar)

If your part handles a window event, its HandleEvent method must return true. If it does not handle an event, its HandleEvent method must return false so that the document shell can handle the event.

Zooming

On platforms that support window zooming, the root part should handle the events kODMDInZoomIn and kODMDInZoomOut and define the appropriate window size. The document shell cannot know the appropriate window-size limits for zooming in or out. Your part can handle the zoom-in and zoom-out events by using platform-specific functions to adjust the window shape appropriately, and then calling the AdjustWindowShape method of the window. (Your part will subsequently receive calls to its FrameShapeChanged and GeometryChanged methods, notifying it that its frame and facet shapes have been changed accordingly.)

Note that the new window size might trigger a frame negotiation, and it may also require the root part to create or delete facets.

Resizing

The document shell usually resizes windows, although the root part can intercept and handle the event (kODMDInGrow). The document shell relies on default size limits for windows, so if your part allows--for example--very small window sizes, it may have to intercept this event and handle the resizing itself.

When a window is resized, the active part does not change, but the part editor for the root frame is informed of the resizing through calls by OpenDoc to its s FrameShapeChanged and GeometryChanged methods. The root part can then do any necessary invalidation and subsequent redrawing, including creation of new facets if embedded parts have become visible because of the resizing.

Closing

The document shell handles a mouse click in the close box of a window or user selection of the Close menu item (or its keyboard equivalent). The document shell closes the window, after which the window cannot be reopened. How-
ever, if the part editor of the root part wishes merely to hide its window rather than close it (for example, if it is a palette), the part editor can intercept this event (kODMDInGoAway) and call the Hide method of the window.

If the window is a document window and is the only one open for that document, the document shell closes the document.

If your part editor needs to close a window programmatically, it can call the window's CloseAndRemove method. The window is closed and the window object is released.

Dragging

The document shell handles some platform-specific window-moving actions, such as dragging of a window by its title bar. No event handling is required of the window's root part, although the root part can intercept and handle this event (kODMDInDrag) if it needs to constrain the movement of the window for some reason.

Parts in other windows may need to be updated because of the window's move; they receive update events as appropriate.

Modal Dialog Boxes

When your part editor displays a Mac OS modal dialog box or alert box, it does not need to create an ODWindow object, as with a regular window. However, it should still request the modal focus (using its own display frame as the modal-focus owner), and it can still receive Mac OS events by providing an event filter.

In addition, your part must ensure that Mac OS floating windows are properly deactivated. To do so, your part must deactivate the front window before displaying a modal dialog box and it must reactivate the front window after dismissing it.

Your part can, as an alternative to handling a dialog box through the Mac OS ModalDialog or Alert function, create and register its own dialog window, request the modal focus for the window's root frame, and handle the dialog box without using either of those Dialog Manager routines.

Acquiring and Relinquishing the Modal Focus

A frame displaying a modal dialog box should own the modal focus, a focus type that exists to constrain certain events.

For example, a mouse click outside the frame that has the modal focus still goes to that frame. If your part's frame has the modal focus and the user clicks outside the frame, your part's HandleEvent method is called and passed a facet of kODNULL. The method should check for a null facet in this situation and either alert the user with a beep or dismiss the dialog box, as appropriate.

A click in a frame embedded within the frame that has the modal focus goes to the embedded frame. This behavior may facilitate the construction of dialog boxes and other controls from multiple parts.

Your part obtains and relinquishes the modal focus as it does other foci; see "Requesting Foci" and "Relinquishing Foci".

In general, your part should not be willing to relinquish the modal focus on request. If your part is displaying a modal dialog box, you probably do not want any other modal dialog box to be displayed at the same time. To make sure that your part retains the modal focus, your part editor's BeginRelinquishFocus method should return kODFalse if the requested focus is kODModalFocus and the proposed new owner of the focus is not one of your own display frames.

When you have finished displaying a modal dialog box, you can directly transfer it to its previous owner by calling the arbitrator's TransferFocus method, as noted in "Handling a Simple Modal Dialog Box"

Event Filters

With Mac OS modal dialog boxes, your part editor's dialog-box event filter controls which events you receive while a dialog box or alert box is displayed. To pass received null events, update events, and activate events on to OpenDoc or other windows for handling, your event filter can send them to the OpenDoc dispatcher by calling its Dispatch method.

Your event filter should not pass other events, such as mouse events, to the dispatcher.

Handling a Simple Modal Dialog Box

To display a simple Mac OS modal dialog box or alert box, you can take these steps:

  1. Get a reference to the frame that currently owns the modal focus by calling the arbitrator's GetFocusOwner method. Request the modal focus from the arbitrator, using its RequestFocus method. If you obtain the focus, proceed.
  2. Install your dialog event filter function.
  3. Create the dialog box, using a Dialog Manager function such as GetNewDialog or a utility function such as ODGetNewDialog (from the DlogUtil utility library provided with OpenDoc). One advantage of using ODGetNewDialog is that it positions the dialog box in relation to your part's document window, rather than to any part windows that may be open.
  4. To handle Mac OS floating windows properly, deactivate the currently active window and any associated floating windows by calling the DeactivateFrontWindows method of the window state object.
  5. Handle the dialog box with a Dialog Manager call such as ModalDialog. Act on the results and, when you finish, dispose of the dialog box with a Dialog Manager call such as DisposeDialog.
  6. Reactivate the previously active window (to restore floating windows) by calling the window state's ActivateFrontWindows method.
  7. Remove your dialog event filter function.
  8. Restore the modal focus to its previous owner by calling the arbitrator's TransferFocus method.

By always saving and restoring the owner of the modal focus, your part can use this approach for nested modal dialog boxes, such as a dialog box that is built from several embedded parts.

Handling a Movable Modal Dialog Box

In OpenDoc, to implement a full-featured Mac OS movable modal dialog box--that is, one that allows process switching--you must create a window object (ODWindow) to contain it. To display the dialog box, you can take these steps:

  1. Use platform-specific methods to create the structures for the dialog box.
  2. Create a window object, using the window state's RegisterWindow method. Give it properties appropriate for your modal dialog box, such as nonpersistent and floating.
  3. Request the modal focus for the root frame of the dialog window.
  4. Adjust menus as necessary for the presence of the dialog box.
  5. Call the Open, Show, and Select methods of the modal dialog window.
  6. Handle events in the dialog box through your normal event-handling mechanism.

To make sure you dismiss the movable modal dialog box at the right time, you can take actions such as these when you receive a mouse-down event in the dialog box:

  1. Determine whether the event applies to your dialog box and, if so, what item the user selected.
  2. If the user has chosen to close the dialog box, relinquish the modal focus and call the window's CloseAndRemove method to delete the window and its root frame.
  3. Re-enable any menus or menu items that you disabled for display of the dialog box.

Note
It is also possible to create a modal dialog box that is movable but does not support process switching. To do so, use a filter function and other functions in the utility library DlogUtil provided with OpenDoc.

Modeless Dialog Boxes

Modeless dialog boxes are more like regular windows than modal dialog boxes are. They can be activated and deactivated, and they need not be dismissed for your part to become active and editable.

Showing the Dialog Box

To display a modeless dialog box in OpenDoc, you must create a window object (ODWindow) to contain it. To display the dialog box, you can take steps such as these:

  1. In case the dialog window already exists, try to get a reference to it by passing its ID (previously stored in your part) to the window state's AcquireWindow method. If it does not yet exist, create the platform-specific structures for the dialog box and create a window object with the window state's RegisterWindow method. Call the window's Open method.
  2. Call the window's Show and Select methods to make it visible and active.
  3. If you do not already have the window ID of the dialog window, get it by calling the window's GetID method. Save it for use in step 1 the next time the user chooses the action that brings up the modeless dialog box.

Closing the Dialog Box

When the user clicks the close box of a modeless dialog box, you may hide the dialog window rather than close it, so that it is not destroyed. This is an optimization that allows you to quickly redisplay the dialog box.

In your part's HandleEvent method, you can respond in this general way to a mouse click in a window's close box:

  1. From the frame or facet passed to HandleEvent, obtain information that can identify the window. For example, get a reference to the window object in which the event occurred (by calling the facet's GetWindow method), or examine the frame's presentation or part info data for identifying characteristics.
  2. Compare that information to stored information that defines your modeless dialog box. For example, get a reference to your modeless dialog's window object (by passing its ID to the window state's AcquireWindow method), or check a stored value that defines your modeless dialog's presentation.
  3. If the two are the same, hide the window instead of closing it.

Hiding a Dialog Box When Deactivating a Frame

When your part is deactivated, it should hide any of its modeless dialog boxes.

When your part relinquishes the selection focus, it can get a reference to the dialog window (by passing its ID to the window state's AcquireWindow method), call the window's IsShown method to see if it is currently being shown, and then save that shown state and hide the window.

When your part reacquires the selection focus, it can retrieve a reference to the dialog window by passing its ID to the window state's AcquireWindow method. Then, if the dialog window had been visible at deactivation, your part can once again show it.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help