MACINTOSH C CARBON: A Hobbyist's Guide To Programming the Macintosh in C
© 2001 K. J. Bricknell
Scope of This Chapter
On Mac OS 8/9, Mac OS 8.5 introduced a number of significant new features and associated system software functions to the Window Manager. This chapter limits itself to the Window Manager as it existed prior to Mac OS 8.5, as influenced by the Carbon API. The additional features introduced with Mac OS 8.5 and Carbon are addressed at Chapter 16.
By definition, all new functions introduced with Mac OS 8.5 and later are automatically part of Carbon.
Windows, Documents, the Window Manager and Graphics Ports
A window is an area on the screen in which the user can enter or view information. Macintosh applications use windows for most communications and interactions with the user, including editing documents and presenting and acknowledging alerts and dialogs.
Users generally enter data in windows and your application typically lets the user save this data to a file. Your application typically creates document windows for this purpose. A document window is a view into the document. If the document is larger than the window, the window is a view of a portion of the document.
The Window Manager, which, amongst other things, provides functions for managing windows, itself depends on QuickDraw. QuickDraw supports drawing into graphics ports. Each window represents a graphics port.
Standard Window Elements
In the following, Mac OS 8/9 terminology is used. Size box equates to resize control. Zoom box equates to zoom button. Close box equates to close button. Collapse box equates to minimise button.
The user can manipulate windows using a set of standard window elements supported by the Window Manager. These standard elements are as follows:
- Title Bar. The title bar is the bar at the top of a window that displays the window's name, contains the close, zoom, and collapse boxes, and indicates whether a window is active. The name of a newly created window with which no document is yet associated should be "untitled". The name of a window containing a saved document should be the document's filename.
- Close Box. The close box allows the user to close a window. The close box is sometimes called the go-away box.
- Zoom Box. The zoom box allows the user to choose between two different window sizes, one established by the user and one by the application. On Mac OS 8/9, the zoom box can be full, vertical, or horizontal.
- Collapse Box. The collapse box allows the user to collapse (minimise on Mac OS X) and uncollapse a window.
- Size Box. The size box allows the user change the size of a window.
- Draggable Area. That part of the window's "frame" less the close, zoom,collapse, and size boxes.
Scroll bars, which allow the user to view different parts of a document containing more information than can be displayed on the screen at the one time, are not an integral part of a window and must be separately created and managed. By convention, scroll bars are placed on the right and lower edges of those windows that require them.
Active and Inactive Windows
The active window is the window in which the user is currently working. It is identified by its general appearance (see Fig 1). All keyboard activity targets the active window and only the active window interacts with the user.
When the user activates one of your application's windows, the Window Manager redraws the window's title bar and frame/shadow, the close box/button, the title text, the zoom box/button, the collapse box/minimise button, and the size box/resize control as shown at Fig 1. Your application must reinstate the appearance of the rest of the window to its state prior to the deactivation, activating any controls (scroll bars, etc.), drawing the scroll box (scroller, in Mac OS X terminology) in the same position, restoring the insertion point, and highlighting the previous selection, etc.
When a window belonging to your application becomes inactive, the Window Manager redraws the title bar and frame/shadow as shown at Fig 1. Your application must deactivate any controls, remove highlighting from selections, and so on.
When the user clicks in an inactive document window, your application should make the window active but should not make any selections in response to the click. To make a selection, the user should be required to click again. This behaviour protects the user from unintentionally losing an existing selection when activating the window.
On Mac OS 8/9, all of an application's document windows are in the one layer. On Mac OS X, document windows from different applications can be interleaved, and clicking on a window to bring it forward does not affect the layering of other windows. That said, if an application has a Window menu, choosing Bring All To Front will cause all of the application's document windows to be brought in front of all of the document windows of all other applications. Clicking on the application's icon in the Dock has the same effect.
Types of Windows
The Window Manager defines a large number of window types, which may be classified as follows:
- Document types.
- Dialog and alert types.
- Floating window types.
Window types are often referred to by the constant used in 'WIND' resources, and by certain Window Manager functions, to specify the type of window required. That constant determines both the visual appearance of the window and its behaviour.
Fig 2 shows the eight available window types for documents and the constants that represent those types.
Dialog and Alert Types
Fig 3 shows the seven available window types for modal and movable modal dialogs and alerts and the constants that represent those types. (The document window type represented by the constant kWindowDocumentProc is used for modeless dialogs.)
Floating Window Types
Figs 4 and 5 show the sixteen available window types for floating windows and the constants that represent those types.
Window Definition IDs
The constants shown at Figs 2, 3, 4, and 5 each represent a specific window definition ID. A window definition ID is a 16-bit value which contains the resource ID of the window's window definition function in the upper 12 bits and a variation code in the lower 4 bits:
- Window Definition Function. The system software and various Window Manager functions call a window's window definition function (WDEF) when they need to perform certain window-related actions, such as drawing or re-sizing a window's frame.
- Variation Code. A single WDEF can support up to 16 different window types. The WDEF defines a variation code, an integer from 0 to 15, for each window type it supports.
Four WDEFs (resource IDs 64, 65, 66, and 67) are associated with the three classifications of window types.
The window definition ID is derived by multiplying the resource ID of the WDEF by 16 and adding the variation code to the result, as is shown in the following:
Window Definition ID
Window Definition ID
|| 64 * 16 + 0 = 1024
|| kWindowDocumentProc |
|| 64 * 16 + 1 = 1025
|| kWindowGrowDocumentProc |
|| 64 * 16 + 2 = 1026
|| kWindowVertZoomDocumentProc |
|| 64 * 16 + 3 = 1027
|| kWindowVertZoomGrowDocumentProc |
|| 64 * 16 + 4 = 1028
|| kWindowHorizZoomDocumentProc |
|| 64 * 16 + 5 = 1029
|| kWindowHorizZoomGrowDocumentProc |
|| 64 * 16 + 6 = 1030
|| kWindowFullZoomDocumentProc |
|| 64 * 16 + 7 = 1031
|| kWindowFullZoomGrowDocumentProc |
|| 65 * 16 + 0 = 1040
|| kWindowPlainDialogProc |
|| 65 * 16 + 1 = 1041
|| kWindowShadowDialogProc |
|| 65 * 16 + 2 = 1042
|| kWindowModalDialogProc |
|| 65 * 16 + 3 = 1043
|| kWindowMovableModalDialogProc |
|| 65 * 16 + 4 = 1044
|| kWindowAlertProc |
|| 65 * 16 + 5 = 1045
|| kWindowMovableAlertProc |
|| 66 * 16 + 1 = 1057
|| kWindowFloatProc |
|| 66 * 16 + 3 = 1059
|| kWindowFloatGrowProc |
|| 66 * 16 + 5 = 1061
|| kWindowFloatVertZoomProc |
|| 66 * 16 + 7 = 1063
|| kWindowFloatVertZoomGrowProc |
|| 66 * 16 + 9 = 1065
|| kWindowFloatHorizZoomProc |
|| 66 * 16 + 11 = 1067
|| kWindowFloatHorizZoomGrowProc |
|| 66 * 16 + 13 = 1069
|| kWindowFloatFullZoomProc |
|| 66 * 16 + 15 = 1071
|| kWindowFloatFullZoomGrowProc |
|| 67 * 16 + 1 = 1073
|| kWindowFloatSideProc |
|| 67 * 16 + 3 = 1075
|| kWindowFloatSideGrowProc |
|| 67 * 16 + 5 = 1077
|| kWindowFloatSideVertZoomProc |
|| 67 * 16 + 7 = 1079
|| kWindowFloatSideVertZoomGrowProc |
|| 67 * 16 + 9 = 1081
|| kWindowFloatSideHorizZoomProc |
|| 67 * 16 + 11 = 1083
|| kWindowFloatSideHorizZoomGrowProc |
|| 67 * 16 + 13 = 1085
|| kWindowFloatSideFullZoomProc |
|| 67 * 16 + 15 = 1087
|| kWindowFloatSideFullZoomGrowProc |
Window Type Usage
A kWindowFullZoomGrowDocumentProc window is normally used for document windows because it supports all window manipulation elements. Note that, because you can optionally suppress the close box/button when you create the window, the Window Manager does not necessarily draw that particular element. Also note that, when the related document contains more data that will fit in the window, you must add scroll bars.
For Modal Alerts and Modal Dialogs
Modal alerts and modal dialogs are simply special-purpose windows. Modal alerts generally use the window type kWindowAlertProc and modal dialogs generally use window type kWindowModalDialogProc.
The creation and handling of alerts and dialogs is addressed in detail at Chapter 8.
For Movable Modal Alerts and Movable Modal Dialogs
Movable modal alerts and movable modal dialogs are used when you want the user to be able to move the alert or dialog window or to bring another application to the foreground before the dialog is dismissed. Movable modal alerts use the window type kWindowMovableAlertProc and movable modal dialogs use the window type kWindowMovableModalDialogProc.
For Modeless Dialogs
Modeless dialogs allow the user to perform other tasks within the application without first dismissing the dialog. User interface guidelines require that the kWindowDocumentProc window type, which can be moved or closed but not resized or zoomed, be used for modeless dialogs.1
The Window Manager recognises the special-purpose regions shown at Figs 6 and 7.
A region is an arbitrary area, or set of areas, on the QuickDraw coordinate plane. The outline of a region is one or more closed loops. Regions are explained in more detail at Chapter 12.
Handles to these and two other window-related regions, which are represented by constants of type RegionWindowCode, may be obtained via a call to GetWindowRegion. The definitions of these regions, and the constants which represent them, are as follows:
Title bar region
The entire area occupied by a window's title bar, including the title text region.
Title text region
That portion of a window's title bar that is occupied by the name of the window and the window proxy icon. . (See Chapter 16.)
Close box/button region
The area occupied by a window's close box/button.
Zoom box/button region
The area occupied by a window's zoom box/button.
On Mac OS X, this equates to the title bar region. On Mac OS 8/9, this includes the window frame, including the title bar and window outline, but excluding the close box, zoom box, collapse box, and size box (if any).
Size box/resize control region
|| The area occupied by a window's size box/resize control. |
Collapse box/minimise button region
The area occupied by a window's collapse box/minimise button.
Proxy icon region
The area occupied by the window proxy icon. (See Chapter 16.)
The entire area occupied by a window, including the frame and content region. (The window may be partially off-screen but its structure region does not change.)
That part of a window in which the contents of a document, the size box, and the window's controls (including scroll bars) are displayed.
Contains all areas of a window's content region that need updating (re-drawing).
Global port region
The bounds of the window's graphics port, in global coordinates, even when the window is collapsed.
The Desktop (Gray) Region
Another region of some relevance to the Window Manager is the desktop region (sometimes known as the gray region). The desktop region is the region below the menu bar, including all screen real estate in a system equipped with multiple monitors. The Window Manager maintains a pointer to the desktop region in a low-memory global variable named GrayRgn. You can get a handle to the desktop region using the function GetGrayRgn.
Controls and Control Lists
Windows may contain controls. The most common control in a window is the scroll bar (see Fig 8), which should be included in the window when there is more data than can be shown at one time in the space available. The Control Manager is used to create, display and manipulate scroll bars.
All controls "belonging" to an individual window and are displayed within the graphics port that represents that window. Entries pointing to the descriptions of a window's controls are stored in a control list maintained for that window by the Window Manager.
The Window List
At any one time, many windows, from many applications, may be displayed on the desktop. To track all of the windows on the desktop, the Window Manager maintains a private data structure called the window list. The arrangement of the entries in this list reflects the current front-to-back ordering of the windows on the desktop, the frontmost (active) window being the first in the list.
The function GetWindowList returns a reference to the window object (see below) for the first window in the window list. The function GetNextWindow returns a reference to the window object for the next window in the window list.
The Graphics Port and the Window Object
The Graphics Port
Each window represents a QuickDraw graphics port, an opaque object of type CGrafPort which describes a drawing environment with its own coordinate system. . The Window Manager creates a graphics port when it creates the window.
The graphics port is addressed in detail at Chapter 11.
The location of a window on the screen is defined in QuickDraw's global coordinates. QuickDraw's global coordinates originate at the top left corner of the main screen and extend vertically and horizontally within the range -32768 to 32,767. The positive x-axis extends rightward and the positive y-axis extends downward (see Fig 9).
The graphics port object stores a rectangle called the port rectangle. In a graphics port representing a window, the port rectangle represents the window's content region. Within the port rectangle, the drawing area is described in local coordinates. Fig 9 illustrates the local and global coordinate systems for a window which is 100 pixels high by 200 pixels wide, and which is placed with its content region 70 pixels down and 60 pixels to the right of the upper left corner of the screen.
When the Window Manager creates a window, it places the origin of the local coordinate system at the upper-left corner of the window's port rectangle. Note, however, that the Event Manager describes mouse events in global coordinates, and that you must do most of your window manipulation in global coordinates.
The Window Object
The Window Manager stores information about individual windows in opaque data structures called window objects. The data type WindowPtr is defined as a pointer to a window object:
typedef struct OpaqueWindowPtr* WindowPtr;
Note that the data type WindowPtr is equivalent to the newer data type WindowRef:
typedef WindowPtr WindowRef;
One of the fundamental differences between the Classic API and the Carbon API is that, in the Classic API, the data type WindowPtr is defined as a pointer to a graphics port structure, not to a window structure. The first field in the Classic API's window structure is a graphics port structure, meaning that the graphics port structure has the same address as the window structure in which it resides. In the Classic API, a WindowPtr must be cast to a WindowPeek (which is defined as a pointer to a window structure) in order for the fields of the window structure to be directly accessed.
Accessor functions are provided to access the information in window objects. The accessor functions are as follows:
Gets a pointer to the specified window's graphics port object.
Gets the window kind (dialog or application) of the specified window.
Sets the window kind (dialog or application) of the specified window.
Determines whether the specified window is visible.
Makes the specified window visible.
Makes the specified window invisible.
Makes the specified window visible or invisible without affecting front-to-back ordering.
Determines whether the specifed window is highlighted.
Highlights or unhighlights a window.
Gets a handle to the specified window's structure, content, and update regions.
Note: Handles to the other regions shown at Figs 6 and 7 are not included in the window object. The WDEF determines the location of those particular regions.
Gets the specified window's title.
Sets the specified window's title.
Gets a handle to the picture stored in the window object by SetWindowPic.
Stores a handle to a picture in the window object, causing the Window Manager to draw the picture instead of generating an update event.
Gets the reference constant stored in the specified window's window object by SetWRefCon.
Sets a reference constant in the specified window's window object.
Gets the window's standard state (see below) rectangle.
Sets the window's standard state (see below) rectangle.
Gets the window's user state (see below) rectangle.
Sets the window's user state (see below) rectangle.
Events in Windows
As stated at Chapter 2, the Window Manager itself generates two types of events central to window management, namely, activate events and update events.
One of the Window Manager's main tasks is to report the location of the cursor when the application receives a mouse-down event. As was also stated at Chapter 2, the Window Manager function FindWindow may be used to determine whether the cursor is in a window when the mouse-down occurs and, if it is in a window, in exactly which window and which part of that window. FindWindow is thus the function which enables you to distinguish between those mouse-down events that affect the window itself and those that affect the document displayed in the window.
Creating Your Application's Windows
You typically create document and floating windows from resources of type 'WIND', although you can create them programmatically using the function NewCWindow. Additional methods of creating windows, including floating windows, programmatically are described at Chapter 16.
When creating resources with Resorcerer, it is advisable that you refer to a diagram and description of the structure of the resource and relate that to the various items in the Resorcerer editing windows. Accordingly, the following describes the structure of the resource associated with the creation of document and floating windows.
Structure of a Compiled 'WIND' Resource
Fig 10 shows the structure of a compiled 'WIND' resource and how it "feeds" the window object.
The following describes the main fields of the 'WIND' resource:
A rectangle that defines the initial size and placement, in global coordinates, of the window's content region. This rectangle can be changed before displaying the window, either programmatically or by using an optional positioning specification (see below).
WINDOW DEFINITION ID
The window's definition ID.
Specifies whether the window is to be visible or invisible. Note that this really means whether the Window Manager displays the window; it does not necessarily mean that the window can be seen on the screen. (A visible window might be completely covered by other windows; nevertheless its visibility status is still "visible".)
PRESENCE OF CLOSE BOX/BUTTON
Specifies whether the window is to have a close box/button.
A reference constant which your application can use for whatever data it needs to associate with the window. When it builds a new window object, the Window Manager stores in that object whatever value you specify in this field. (You can also store a reference constant in the window object programmatically via a call to SetWRefCon.).
| A string that specifies the window's title. |
An positioning specification (optional). If this field contains a positioning specification, it overrides the window position established by the rectangle in the first field.
The positioning constants (see below) which may be assigned to this field are very useful for specifying the initial position of dialogs, alerts, and windows for new documents. However, the position (and size) of a new window intended to display a previously saved document should be the same as the window position (and size) when the document was last displayed.
The constants for the positioning specification field are as follows:
(Use initial rectangle.)
(Use initial rectangle.)
Centre on main screen.
Place in alert position on main screen.
Stagger on main screen.
Center on parent window.
Place in alert position on parent window.
Stagger relative to parent window.
Center on parent window screen.
Alert position on parent window screen.
Stagger on parent window screen.
Creating a 'WIND' Resource Using Resorcerer
Fig 11 shows a 'WIND' resource being created with Resorcerer.
Creating the Window From the 'WIND' Resource
GetNewCWindow is used to create a window from a 'WIND' resource.
Adding Scroll Bars
If a window requires scroll bars, you typically create them from 'CNTL' resources at the time that you create the document window, and then display them when you make the window visible. (See Chapter 7.)
If the 'WIND' resource specifies that the new window is visible, GetNewCWindow displays the window immediately. If you are creating a document window, however, it is best to create the window in an invisible state and then make it visible when you are ready to display it. The right time to display a window depends on whether the window is associated with a new or a saved document:
- If you are creating a window because the user is creating a new document, you should display the window immediately (by calling ShowWindow).
- If you are creating a new window to display a saved document, you should retrieve the document and draw it in the window before calling ShowWindow.
Positioning a New Document Window on the DeskTop
The positioning constants previously described allow you to position new windows automatically. When used, those positioning constants concerned with staggering new window placement will ensure that the Window Manager will use any vacated position for the next new window.
Getting Available Window Positioning Bounds
Carbon introduced the function GetAvailableWindowPositioningBounds, which allows your application to determine the available window positioning bounds on a given screen. The function returns the bounds of the screen rectangle less the menu bar and, on Mac OS X, the Dock.
Positioning a Saved Document Window on the DeskTop
For windows created for the purpose of displaying a saved document, you should replicate the size and location of the document's window as it was when the document was last saved. When the user saves a document, you must therefore also save the user state rectangle and the current zoom state of the window (that is, whether the window is in the user state or the standard state).
User State, Standard State, and Zoom State
Some explanation of user state and standard state is necessary. The user state is the last size and position the user, through sizing and dragging actions, established for a window. The standard state is the size and position that your application defines as being best for the display of the data contained in the window.
The user and standard states are stored in the window object and may be set and retrieved using the functions GetWindowStandardState, SetWindowStandardState, GetWindowUserState, and SetWindowUserState (see Accessor Functions, above). In addition, the function IsWindowInStandardState allows your application to determine the current zoom state, i.e., whether the window is zoomed "out" to the standard state or zoomed "in" to the user state.
Saving the Window State
Returning to the matter of saving the user state and the current zoom state of the window, for windows with zoom boxes/buttons you typically store this data as a custom resource in the resource fork of the document file.
Drawing a Window's Contents
Your application is responsible for drawing a window's contents. It typically uses the Control Manager to draw the window's controls and then draws the user data itself.
Managing Multiple Windows and Associated Data
Your application is likely to have multiple windows open on the desktop at once, each of which will have some form of data, such as text, associated with it. This data, which is external to the window object, may be regarded as the "property" of an individual window.
As previously stated, you can store a reference constant in a window object using the function SetWRefCon. Typically, your application will use SetWRefCon to store a handle to a structure containing the window's external data. This structure, usually referred to as a document structure, might hold a handle to the text being edited, handles to the scroll bars, a file reference number and a file system specification for the document's file, plus a flag indicating whether data has changed since the last save, as shown in the following example:
typedef docStructure **docStructureHdl;
Your application can retrieve the reference constant using the function GetWRefCon.
Handling Mouse Events
Your application, when it is the active application, receives all mouse-down events in its menu bar and its windows. When it receives a mouse-down event, your application should call FindWindow to ascertain which window, and which part of that window, the mouse-down occurred in. (In this context, the menu bar is considered to be a window part). The application should then take the appropriate action based on which window, and which part of that window, the mouse-down occurred in.
Mouse-Downs in Inactive Windows
When you receive a mouse-down event in an inactive document window or modeless dialog, and if the active window is a document window or a modeless dialog, you should call SelectWindow, passing it the window reference. SelectWindow re-layers the windows as necessary, removes highlighting from the previously active window, brings the newly-activated window to the front, highlights it and generates the activate and update events necessary to tell all affected applications which windows must be redrawn.
Note that, if the active window is a modal or movable modal alert or dialog, no action is required by your application. Modal and movable modal alerts and dialogs are handled by the ModalDialog function, which does not pass the event to your application.
Handling Keyboard Events
Whenever your application is the active application, it receives all key-down events (except, of course, for the system-defined Command-Shift-number key sequences).
When you receive a key-down event, you should first check whether the user is holding down a modifier key and another key at the same time. Your application should respond to key-down events by inserting data into the document, changing the display or taking other appropriate actions. Typically, your application provides feedback for standard keystrokes by drawing the character on the screen.
Handling Update Events
Handling Update Events - Mac OS 8/9
On Mac OS 8/9, the Window Manager maintains an update region for each window. The update region represents the parts of a window's content region that have been affected by changes to the desktop and need redrawing. The Event Manager continually scans the window objects of all the windows on the desktop, looking for non-empty update regions. If it finds an update region that is not empty, it generates an update event for that window.
When your application receives an update event, it should redraw the content area. When your application redraws the content area, the Window Manager clips all screen drawing to the visible region of the window's graphics port. The visible region is that part of a graphics port that is not covered by other windows, that is, the part that is actually visible on screen. The Window Manager stores a handle to the visible region in the graphics port object.
Before redrawing the content area, your application should call BeginUpdate and, when it has completed the drawing, it should call EndUpdate. As shown at Fig 12, BeginUpdate temporarily adjusts the visible region to equate to the intersection of the visible region and the update region. Because QuickDraw limits its drawing to this temporarily modified visible region, only those parts of the window which actually need updating are drawn. BeginUpdate also clears the update region, thus ensuring that the Event Manager does not continue sending an endless stream of update events.
When the drawing is completed, and as shown at Fig 11, EndUpdate restores the visible region of the graphics port to the full visible region.
The reason for these update region/visible region machinations is that the handle to the update region is stored in the window object while the handle to the visible region is stored in the graphics port object. QuickDraw knows the graphics port object intimately, but knows nothing about the window object or its contents. QuickDraw needs something it can work with, hence the above process whereby the visible region is temporarily made the equivalent of the update region while QuickDraw does its drawing.
Manipulating the Update Region
Your application can force or suppress update events by manipulating the update region. You can call InValWindowRect or InvalWindowRgn to add a rectangle or region to the update region, thus causing an update event to be generated and, as a consequence, that area to be redrawn. You can also remove a rectangle or region from the update region by calling ValidWindowRect or ValidWindowRgn so as to decrease the time spent redrawing. For example, an unaffected text area could be removed from the update region of a window that is being resized.
Handling Update Events - Mac OS X
On Mac OS X, windows are double-buffered, meaning that your application does not draw into the window's graphics port itself but rather into a separate buffer. The Window Manager flushes the buffer to the window's graphics port when your application calls WaitNextEvent. On Mac OS X, your application does not require update events to cater for the situation where part, or all, of a window's content region has just been exposed as a result of the user moving an overlaying window.
The receipt of an update event on Mac OS X simply means that your application should draw the required contents of the window. The swapping of visible and update regions required on Mac OS 8/9 is not required, so calls to BeginUpdate and EndUpdate are irrelevant (and ignored) on Mac OS X.
As is the case on Mac OS 8/9, your application can force the generation of an update event by calling InValWindowRect or InvalWindowRgn. (Your application can also force the buffer to be flushed to the window's graphics port by calling the QuickDraw function QDFlushPortBuffer. This is required, for example, when your application is drawing periodically in a loop that does not call WaitNextEvent.)
Type-Dependent Update Functions
An update function should typically first determine whether the type of window being updated is a document window or some other type of window. If the window is a document window, a document window updating function should be called. If the window is a modeless dialog, an updating function for that modeless dialog should be called.
Handling Activate Events
Activate events are generated by the Window Manager to inform your application that a window is becoming active or is about to be made inactive. Each activate event specifies the window to be changed and whether the window is to be activated or deactivated.
Your application typically triggers activate events itself by calling SelectWindow following a mouse-down event in a non-active window. SelectWindow brings the window in which the mouse-down occurred to the front, adds highlighting to that window, and removes highlighting from the previously active window. SelectWindow then generates one activate event to tell your application to perform its part of the deactivation of the previously active window, followed by and another activate event to tell your application to perform its part of the activation of the newly activated window.
When your application receives the event for the window about to be made inactive, it should hide the window's controls and remove any highlighting of selections. When your application receives the event for the newly activated window, it should draw the controls and restore the content area as necessary. This latter might involve, for example, adding the insertion point at its former location or highlighting the previously highlighted selection.
The function for handling activate events should typically first determine whether the window being activated/deactivated is a document window or a modeless dialog. It should then perform the appropriate activation/deactivation actions. The function does not need to check for modal alert or modal dialogs because the Dialog Manager's ModalDialog function automatically handles activate events for those windows.
Dragging a Window
When a mouse-down event occurs in the title bar, your application should call DragWindow, which tracks the user's actions until the mouse button is released. DragWindow moves an image of the window on the screen as the user moves the mouse. When the user releases the mouse, DragWindow redraws the window in its new location. The window is then activated (unless the Command key was pressed during the drag).
The area within which the window may be dragged is specified by a Rect variable passed in DragWindow's boundsRect parameter.
In Carbon, assigning NULL to the boundsRect parameter has the effect of setting the the parameter to the bounding rectangle of the desktop region.
Zooming a Window
The zoom box/button allows the user to alternate between two window sizes and positions known as the user state and the standard state. To amplify the previous description of user state and standard state:
- The user state is the window size and location established by the user. (If the user has not yet moved or resized the window, the user state is the size and location of the window when it was created.)
- The standard state is the size and position that your application specifies as being the optimum for the display of the data contained in the window. In a word-processing program, for example, a window in the standard state might show the full width of a page and as much length as will fit on the screen. If the user changes the page size using the Page Setup dialog, the application might adjust the standard state width to reflect the new page width.
- The user and standard states are stored in the window object. The Window Manager sets the initial standard and user states when it fills in the window object on creation, and it updates the user state whenever the user resizes the window.
Mac OS 8.5 introduced new functions for implementing window zooming. Prior to Mac OS 8.5, when FindWindow returned either inZoomIn or inZoomOut, your application would call TrackBox to handle highlighting of the zoom box and to determine whether the cursor was inside or outside the zoom box when the button was released. If TrackBox returned true, your application would call ZoomWindow to resize the window.
The advantage of the ZoomWindowIdeal function introduced with Mac OS 8.5 is that, unlike ZoomWindow, it zooms the window in accordance with the following Apple human interface guidelines relating to a window's standard state:
- "A window should move as little as possible when zooming between the user state and standard state, to avoid distracting the user."
- "A window in its standard state should be positioned so that it is entirely on one screen."
- "If a window straddles more than one screen in the user state, when it is zoomed to the standard state it should be zoomed to the screen that contains the largest portion of the windowŐs content region."
- "If the ideal size for the standard state is larger than the destination screen, the dimensions of the standard state should be that of the destination screen, minus a few pixels of boundary. If the destination screen is the main screen, space should also be left for the menu bar."
- "When a window is zoomed from the user state to the standard state, the top left corner of the window should remain anchored in place; however, if the standard state of the window cannot fit on the screen with the top left corner anchored, the window should be "nudged" so that the parts of the window in the standard state that would fall offscreen are, instead, just onscreen."
Other advantages of ZoomWindowIdeal are that:
- It allows you to specify, in its ioIdealSize parameter, the desired height and width of the window's content region in the standard state.
- On Mac OS X, it takes account of the current height of the Dock when zooming to the standard state and constrains the zoom accordingly.
When ZoomWindowIdeal is used, another function introduced with Mac OS 8.5 (IsWindowInStandardState) must be used to determine the appropriate part code (inZoomIn or InZoomOut) to pass in ZoomWindowIdeal's partCode parameter.
Vertical or Horizontal Zoom Boxes - Mac OS 8/9
Your application should ensure that, when a vertical zoom box is clicked, only the vertical size of the associated window changes. Similarly, when a horizontal zoom box is clicked, your application should ensure that only the horizontal size of the associated window changes.
Re-Sizing a Window
Mac OS 8.5 introduced a new function for implementing window re-sizing. Prior to Mac OS 8.5, when the user pressed the mouse button in the size box, your application would call GrowWindow. This function displayed a grow image, a dotted outline of the window frame and scroll bar area which expanded and contracted as the user dragged the size box. When the user released the mouse button, GrowWindow returned a long integer which described the window's new height (in the high-order word) and width (in the low-order word). A value of zero indicated that the window size did not change. When the mouse-button was released and GrowWindow returned a non-zero value, your application called SizeWindow to draw the window in its new size.
The function introduced with Mac OS 8.5 is ResizeWindow. ResizeWindow moves a grow image around the screen, following the user's cursor movements, and handles all user interaction until the mouse button is released. Unlike the function GrowWindow, there is no need to follow this call with a call to SizeWindow. Once resizing is complete, ResizeWindow draws the window in its new size.
If you pass NULL in ResizeWindow's sizeConstraints parameter, resizing will be constrained by the default minimum size (64 by 64 pixels) and the default maximum size (the bounding rectangle of the desktop region). However, the option is available to supply custom upper and lower re-sizing limits in the fields of a Rect variable passed in the sizeConstraints parameter. The limits represented by each field are as follows:
- sizeConstraints.top represents the minimum vertical measurement.
- sizeConstraints.left represents the minimum horizontal measurement.
- sizeConstraints.bottom represents the maximum vertical measurement.
- sizeConstraints.right represents the maximum horizontal measurement.
Note that the values assigned represent window dimensions, not screen coordinates.
Following a window resize, your application should adjust its scroll bars and window contents to accommodate the new size.
In Carbon, NULL may be assigned to ResizeWindow's newContentRect parameter if the new dimension of the window's content region is not required.
Closing a Window
The user closes a window by clicking in the close box/button or by choosing Close from the File menu.
When the user clicks in the close box/button, TrackGoAway should be called to track the mouse until the user releases the mouse button. If TrackGoAway returns true, meaning that the user did not release the mouse button outside the close box/button, your application should call its function for closing down the window.
The actions taken by your window closing function depend on the type of information the window contains and whether the contents need to be saved. The function should cater for different types of windows, that is, modeless dialogs (which may be merely hidden with HideWindow rather than closed completely) and standard document windows. In the latter case, the function should check whether any changes have been made to the document since it was opened and, if so, provide the user with an opportunity to save the document to a file before closing the window. (This whole process is explained in detail at Chapter 18.)
As for the window itself,DisposeWindow removes a window from the screen, removes it from the window list, and discards all of its data storage, including the window object.
Hiding and Showing a Window
Ordinarily, when the user elects to close a window, you dispose of the window. In some circumstances, however, it may be more appropriate to simply hide the window instead of removing its data structures. It is usually more appropriate, for example, to hide, rather than dispose of, modeless dialogs. That way, when the user next chooses the relevant menu command, the dialog is already available, and in the same location, as when it was last used.
HideWindow hides a window. ShowWindow will make the window visible and SelectWindow will make it the active window.
Providing Help Balloons (Mac OS 8/9)
Help Balloons -'hrct' and 'hwin' Resources
The Mac OS 8/9 system software provides help balloons for the title bar, draggable area, close box, zoom box, and collapse box for windows created with the standard WDEFs. Where applicable, you should provide help balloons for the content area of your windows.
How you choose to provide help balloons for the content area depends mainly on whether your windows are static or dynamic. A static window does not change its title or reposition any of the objects within its content area. A dynamic window can reposition any of it objects within its content area, or its title may change. For example, any window that scrolls past areas of interest to the user is a dynamic window because the object with associated help balloons can change location as the user scrolls. The following addresses the case of static windows only.
Help balloons for static document and floating windows are defined in 'hrct' and 'hwin' resources.
Creating 'hrct' and 'hwin' Resources Using Resorcerer
The 'hrct' (rectangle help) resource is used to define hot rectangles for displaying help balloons in a static window and to specify the help messages for those balloons. All 'hrct' resources must have resource IDs equal to or greater than 128. Fig 13 shows an 'hrct' resource being created using Resorcerer.
The 'hwin' (window help) resource is used to associate the help balloons defined in an 'hrct' resource with a particular window. All 'hrct' resources must have resource IDs equal to or greater than 128. Fig 14 shows an 'hwin' resource being created using Resorcerer.
Main Window Manager Constants, Data Types, and Functions
kWindowDocumentProc = 1024 // Document windows
kWindowGrowDocumentProc = 1025
kWindowVertZoomDocumentProc = 1026
kWindowVertZoomGrowDocumentProc = 1027
kWindowHorizZoomDocumentProc = 1028
kWindowHorizZoomGrowDocumentProc = 1029
kWindowFullZoomDocumentProc = 1030
kWindowFullZoomGrowDocumentProc = 1031
kWindowPlainDialogProc = 1040 // Dialogs and Alerts
kWindowShadowDialogProc = 1041
kWindowModalDialogProc = 1042
kWindowMovableModalDialogProc = 1043
kWindowAlertProc = 1044
kWindowMovableAlertProc = 1045
kWindowMovableModalGrowProc = 1046
kWindowFloatProc = 1057 // Floating windows
kWindowFloatGrowProc = 1059
kWindowFloatVertZoomProc = 1061
kWindowFloatVertZoomGrowProc = 1063
kWindowFloatHorizZoomProc = 1065
kWindowFloatHorizZoomGrowProc = 1067
kWindowFloatFullZoomProc = 1069
kWindowFloatFullZoomGrowProc = 1071
kWindowFloatSideProc = 1073
kWindowFloatSideGrowProc = 1075
kWindowFloatSideVertZoomProc = 1077
kWindowFloatSideVertZoomGrowProc = 1079
kWindowFloatSideHorizZoomProc = 1081
kWindowFloatSideHorizZoomGrowProc = 1083
kWindowFloatSideFullZoomProc = 1085
kWindowFloatSideFullZoomGrowProc = 1087
kDialogWindowKind = 2
kApplicationWindowKind = 8
Window Part Codes Returned by FindWindow
inDesk = 0
inNoWindow = 0
inMenuBar = 1
inSysWindow = 2
inContent = 3
inDrag = 4
inGrow = 5
inGoAway = 6
inZoomIn = 7
inZoomOut = 8
inCollapseBox = 11
inProxyIcon = 12
Regions Codes Passed to GetWindowRegion
kWindowTitleBarRgn = 0
kWindowTitleTextRgn = 1
kWindowCloseBoxRgn = 2
kWindowZoomBoxRgn = 3
kWindowDragRgn = 5
kWindowGrowRgn = 6
kWindowCollapseBoxRgn = 7
kWindowStructureRgn = 32
kWindowContentRgn = 33
kWindowUpdateRgn = 34
kWindowGlobalPortRgn = 40
Options For ScrollWindowRect and ScrollWindowRegion
kScrollWindowNoOptions = 0
kScrollWindowInvalidate = (1L << 0) // Add exposed area to window's update region.
KScrollWindowEraseToPortBackground = (1L << 1) // Erase exposed area using background
// colour/pattern of the window's grafport.
typedef struct OpaqueWindowPtr* WindowPtr;
typedef WindowPtr WindowRef;
typedef SInt16 WindowPartCode;
typedef UInt16 WindowRegionCode;
typedef UInt32 OptionBits;
typedef OptionBits ScrollWindowOptions;
WindowRef GetNewCWindow(short windowID,void *wStorage,WindowRef behind);
WindowRef NewCWindow(void *wStorage,const Rect *boundsRect,ConstStr255Param title,
Boolean visible,short procID,WindowRef behind,Boolean goAwayFlag,
void GetWTitle(WindowRef theWindow,Str255 title);
void SetWTitle(WindowRef theWindow,ConstStr255Param title);
OSStatus CopyWindowTitleAsCFString(WindowRef inWindow,CFStringRef *outString);
OSStatus SetWindowTitleWithCFString(WindowRef inWindow,CFStringRef inString);
Boolean IsWindowVisible(WindowRef window);
void ShowWindow(WindowRef theWindow);
void HideWindow(WindowRef theWindow);
void ShowHide(WindowRef theWindow,Boolean showFlag);
Boolean IsWindowHilited(WindowRef window);
void HiliteWindow(WindowRef theWindow,Boolean fHilite);
void SelectWindow(WindowRef theWindow);
void BringToFront(WindowRef theWindow);
void SendBehind(WindowRef theWindow,WindowRef behindWindow);
void DrawGrowIcon(WindowRef theWindow);
void MoveWindow(WindowRef theWindow,short hGlobal,short vGlobal,Boolean front);
void DragWindow(WindowRef theWindow,Point startPt,const Rect *boundsRect);
long GrowWindow(WindowRef theWindow,Point startPt,const Rect *bBox);
void SizeWindow(WindowRef theWindow,short w,short h,Boolean fUpdate);
OSStatus ZoomWindowIdeal(WindowRef window,SInt16 partCode,Point *ioIdealSize);
Boolean IsWindowInStandardState(WindowRef window,Point *idealSize,
OSStatus SetWindowIdealUserState(WindowRef window,Rect *userState);
OSStatus GetWindowIdealUserState(WindowRef window,Rect *userState);
Boolean TrackBox(WindowRef theWindow,Point thePt,short partCode);
void ZoomWindow(WindowRef theWindow,short partCode,Boolean front);
Boolean ResizeWindow(WindowRef window,Point startPoint,const Rect *sizeConstraints,
Boolean TrackGoAway(WindowRef theWindow,Point thePt);
void DisposeWindow(WindowRef theWindow);
Boolean IsWindowCollapsable(WindowRef inWindow);
Boolean IsWindowCollapsed(WindowRef inWindow);
OSStatus CollapseWindow(WindowRef inWindow,Boolean inCollapseIt);
OSStatus CollapseAllWindows(Boolean inCollapseEm);
short GetWindowKind(WindowRef window);
void SetWindowKind(WindowRef window,short kind);
OSStatus GetWindowRegion(WindowRef inWindow,WindowRegionCode inRegionCode,
Window User State and Standard State
Rect* GetWindowStandardState(WindowRef window,Rect *rect);
Rect* GetWindowUserState(WindowRef window,Rect *rect);
void SetWindowStandardState(WindowRef window,const Rect *rect);
void SetWindowUserState(WindowRef window,const Rect *rect);
Boolean IsWindowInStandardState(WindowRef window,Point *idealSize,
Getting Available Window Positioning Bounds
OSStatus GetAvailableWindowPositioningBounds(GDHandle inDevice,Rect *availableRect);
Maintaining the Update Region
void BeginUpdate(WindowRef theWindow);
void EndUpdate(WindowRef theWindow);
Boolean IsWindowUpdatePending(WindowRef window);
OSStatus InvalWindowRgn(WindowRef window,RgnHandle region);
OSStatus InvalWindowRect(WindowRef window, const Rect *bounds);
OSStatus ValidWindowRgn(WindowRef window,RgnHandle region);
OSStatus ValidWindowRect(WindowRef window, const Rect *bounds);
Retrieving Mouse Information
short FindWindow(Point thePoint,WindowRef *theWindow);
Window Reference Constant, Variant, and Picture
long GetWRefCon(WindowRef theWindow);
void SetWRefCon(WindowRef theWindow,long data);
short GetWVariant(WindowRef theWindow);
void SetWindowPic(WindowRef theWindow,PicHandle pic);
PicHandle GetWindowPic(WindowRef theWindow);
WindowRef GetNextWindow(WindowRef window);
Window's Graphics Port
CGrafPtr GetWindowPort(WindowRef window);
void SetPortWindowPort(WindowRef window);
WindowRef GetWindowFromPort(CGrafPtr port);
Rect* GetWindowPortBounds(WindowRef window,Rect *bounds);
Scrolling Pixels in the Window Content Region
OSStatus ScrollWindowRect(WindowRef window,const Rect *scrollRect,SInt16 hPixels,
SInt16 vPixels,ScrollWindowOptions options,RgnHandle outExposedRgn);
OSStatus ScrollWindowRegion(WindowRef window,RgnHandle scrollRgn,
SInt16 hPixels,vPixels,ScrollWindowOptions options,RgnHandle outExposedRgn);