[ Next ] [ Previous ] | Chapter 9 - II | [ Contents ] [ Chapter 8: Interfacing with OS/2 Devices ] [ Chapter 10: Window Management ]

#### Parents and Owners

Earlier we had mentioned the concept of parents and owners. These terms are used often in Presentation Manager programming. It is important to understand each one. Every window has a parent, except for the desktop window. In some cases the parent will be the desktop, HWND_DESKTOP. In the last example, the frame window had the desktop as its parent. The frame window was the parent for the client window, the title bar window, and the other windows. What is a parent window?

| A parent window performs many of the same duties that parents of human children perform. A parent window controls where the child can go. A child is "clipped' to the parent and will not be visible outside the parental boundaries. A child window can be moved outside these boundaries; however, the portion outside the parent window will not be visible. Also, a child will inherit all of the parent's styles. If a parent is visible, a child will be visible; if a parent is not visible, a child will not be visible. If a parent moves, the child moves alone with it. However, unlike a human parent, if a parent window is destroyed. all of its children are destroyed as well. If a parent window has two child windows, these children are considered siblings. When a family of windows is all visible at the same time, there is a power struggle for which window will be displayed on top. A child window always will be on top of the parent window. Some surprise, huh?

However, siblings, and the whole windowing system as well, use a concept known as 'Z-Order" to decide who gets on top. The sibling created last usually is at the top of the "Z-Order". The programmer can change the order using the function WinSetWindowPos. This function lets a window be put on top or behind its other siblings. User interaction also affects the "Z-Order." When the user clicks on one of the of the siblings, that window will become the active window, and it will move to the top of the '"Z-Order." The active window is usually the window that either is or owns lime focus window. There is only one active window in the system at any given time.
The other type of window relationship is an owner window. In the last example, hwndFrame was also the owner of the other windows. An owner shares some of the same duties a parent shares. When an owner is hidden, destroyed, or minimized, the children are also. However, an owned window is not clipped to its owner.
The other interesting features of owners is the level of communication between owners and owned, or "control" windows. When an important event happens to an owned window, the owner is sent a WM_CONTROL message. The mpParm1 and mpParm2 parameters tell the owner which control sent the message and what kind of event has occurred. A window does not have to have an owner.

#### Window Stylin'

When a window is created, various descriptors are used to describe how the window will look or act; these descriptors are known as window styles. There are many different kinds of styles, including window styles and class styles, and each type of control has its own styles as well. In this section we will concentrate on window styles, class styles, and frame styles. The other control styles will be covered in their respective chapters.

[...] | -
15 | control styles
16 | WS_GROUP
17 | WS_TABSTOP
18 | WS_MULTISELECT
19 | UNUSED
20 | UNUSED
21 | UNUSED
22 | WS_ANIMATE
23 | WS_MAXIMIZED
24 | WS_MINIMIZED
25 | WS_SYNCPAINT
26 | WS_SAVEBITS
27 | WS_PARENTCLIP
28 | WS_CLIPSIBLINGS
29 | WS_CLIPCHILDREN
30 | WS_DISABLED
31 | WS_VISIBLE
Figure 9.5 Window-style flags

Figure 9.5 shows that the first 16 bits are used for the respective control window styles; the upper 16 bits are used for window styles. Since controls are also windows, both the control window styles and the basic window styles are designed to live together harmoniously.

Table 9.6 Window Style Descriptions
Value | Description
WS_GROUP | Defines which items make up a group in a dialog box window. See Chapter 13
WS_TABSTOP | The user can use the tab key to move to this dialog item. See Chapter 13.
WS_ANIMATE | Will create "exploding windows."
WS_MAXIMIZED | Causes a window to he created fully maximized.
WS_MINIMIZED | Causes a window to be created fully minimized.
WS_SYNCPAINT | Causes a window to have paint messages generated immediately when an area of the window needs to be repainted.
WS_SAVEBITS | Will save the screen area under a window and will restore the image when covered area has been uncovered.
WS_PARENTCLIP | Will cause the parent's presentation space to be clipped to the child's presentation space, enabling the child to draw on the parent's presentation space. This can create some very interesting results, as the parent's visible presentation space usually is larger than or equal to the child's. Most often this style is not used.
WS_CLIPSIBLINGS | Will prevent siblings from redrawing on top of each other
WS_CLIPCHILDREN | Will cause the child window area to be excluded from the drawing region; in other words, the parent cannot paint over the child. Usually this style is not necessary because if both the parent and child windows need to be repainted and also overlap, the parent will be repainted first, and then the child window is repainted.
WS_DISABLED | Will cause a window to be disabled upon creation. Thus this window will not respond to user input until the window is enabled.
WS_VISIBLE | Will make a window visible at creation time. An invisible default window will be created.

Table 9.7 presents class styles that can be specified at class registration time.

Table 9.7 Class Style Description
Class Style | Description
CS_MOVENOTIFY | WM_MOVE messages will be sent whenever the window is moved.
CS_SIZEREDRAW | When a window has been sized, the window will be made completely invalid. and a WM_PAINT message will be sent. This style is useful when an application centers text on the window or sizes an image to fill the window.
CS_HITTEST | WM_HITTEST messages will be sent to the window whenever the mouse moves in the window.
CS_FRAME | Specifies a frame window class.
CS_CLIPCHILDREN | See above.
CS_CLIPSIBLINGS | See above.
CS_PARENTCLIP | See above.
CS_SAVEBITS | See above.
CS_SYNCPAINT | See above.

#### Another Window Example: WINDOW

The following example program illustrates some of the concepts we've talked about so far and includes some new ones also. The program, WINDOW, creates a list of all the windows that are children of the frame window and also queries the window style of each window. The information is displayed in the client area.

> WINDOW.C
> WINDOW.MAK
> WINDOW.DEF

Here main has one small difference from main in the previous example. WIN1.C. The class style CS_SIZEREDRAW, is used for tle client window class. With this style, Presentation Manager will invalidate the window whenever the size changes. The text on the client area is dependent on the width of the window. Because we want to ensure that all the text is nicely formatted even when the window is resized, thus we use CS_SIZEREDRAW.

#### The Presentation Manager Coordinate Space

Figure 9.6 Coordinate space.

Presentation Manager windows use a different coordinate space from the one used by Microsoft Windows. (See Figure 9.6) The bottom left corner is coordinate 0,0. Most window drawing is done by specifying two sets of x,y coordinates that form the lower left and upper right corners of a "bounding rectangle." A structure RECTL,. contains the coordinates. It is a familiar parameter in most painting functions. The structure is defined:

> `typedef struct _RECTL /* rcl */`
> `{`
> `LONG xLeft;`
> `LONG yBottom;`
> `LONG xRight;`
> `LONG yTop;`
> `} RECTL;`
> `typedef RECTL *PRECTL;`

#### More on Window Painting

In a structured program, the application controls exactly when the screen is updated; in an event-driven environment, the system tells the application when it can update the screen. This is done by sending the application the WM_PAINT message. A Presentation Manager program should update the screen within the WM_PAINT message processing.
This message is one of the most common messages to handle. A WM_PAINT message is generated whenever some part of the client window needs to be painted. If a user moves one window on top of another window, the bottom window receives a WM_PAINT message when the covered area becomes visible again. When a portion of a window needs to be repainted. that portion is said to be "invalid". Presentation Manager can invalidate a region or a programmer can invalidate a region using WinInvalidateRegion or WinInvalidateRect .

`BOOL APIENTRY WinInvalidateRect(HWND hwnd,`
`PRECTL pwrc,`
`BOOL fIncludeChildren);`

`BOOL APIENTRY WinInvalidateRegion(HWND hwnd,`
`HRGN hrgn,`
`BOOL fIncludeChildren);`

The first parameter for these functions is the window handle hwnd. The next parameter is the area that is to be invalidated. The last parameter indicates whether children are to be included in the invalid rectangle or region.
Presentation Manager is very stingy in sending WM_PAINT messages. Only that piece of the window that needs to be painted will be invalidated, not the the entire window.

#### Painting by Numbers

`hpsPaint = WinBeginPaint(hwndWnd,`
`NULLHANDLE,`
`&rclRect);`
Painting in this example starts with WinBeginPaint to obtain a presentation space.

`HPS APIENTRY WinBeginPaint(HWND hwnd,`
`HPS hps,`
`PRECTL prclPaint);`
hwndWnd is the window the presentation space belongs to. Presentation Spaces are covered in more detail later. The second parameter is used if the user already has a presentation space obtained using WinGetPS or some other means and wants to use that space for drawing If a NULLHANDLE is specified, the system will provide a presentation space to be used. The last parameter is a pointer to RECTL structure. The coordinates of the invalidated region are placed in the structure. The invalidated region is the region that needs to be painted.

> `BOOL WinEndPaint(HPS hps)`

WinEndPaint is used to terminate a paint procedure. There is only one parameter, hps, which is the presentation space returned from WinBeginPaint.

Once WinEndPaint is called, the region is validated, and any presentation space returned from WinBeginPaint is released.
`WinFillRect(hpsPaint,`
`&rclRect,`
`SYSCLR_WINDOW);`
This function paints the region designated by the second parameter with the specified color index. The first parameter is the presentation space to paint.

`BOOL APIENTRY WinFillRect(HPS hps,`
`PRECTL prcl,`
`LONG lColor);`

A program can use a value such as CLR_BLUE or a system value such as SYSCLR_WINDOW that will fill the rectangle with the system default window color.

The WINDOW example is designed to draw some text on the client window area; however, in a graphical user interface (GUI) environment, this is not just a call to printf. Instead the developer must provide the exact pixel location where the text is to be located. Before we get around actually to drawing the text, we need to find some information about the size of the font used in the client window. GpiQueryFontMetrics is the function to provide all the needed information about a font.
`BOOL APIENTRY GpiQueryFontMetrics(HPS hps,`
`LONG lMetricsLength,`
`PFONTMETRICS pfmMetrics);`
`GpiQueryFontMetrics(hpsPaint,`
`sizeof(fmMetrics),`
`&fmMetrics);`
The FONTMETRICS structure contains much data concerning the point size, face name, height, and width of the current font. The variable lMaxBaselineExt provides the height of the tallest character. We'll use this value as the height-of-line line of text.

`WinQueryWindowRect(hwndWnd,`
`&rclWindow);`
`liLineInfo.usxLeft = (USHORT)fmMetrics.lAveCharWidth;`
`liLineInfo.usxRight = rclWindow.xRight-(USHORT) fmMetrics.lAveCharWidth;`

The next task is to find the current size of the window. Remember, a window can be sized by the user at any time, and a program should be able to adjust to such changes. WinQueryWindowRect will return the size of a window in a RECTL. structure. We will define a right and left margin that is equal to the average width of one character. Very conveniently, the FONTMETRICS structure contains lAveCharWidth, which is exactly that. With all this, we now know the height of our lines, the x coordinate our lines will start at, and the x coordinate that is the end of the line.
To position the first line of text at the top of the page and create a one-line margin, the following math is done to move the bounding rectangle down one line.
`rclWindow.yTop = rclWindow.yTop-`
`liLineInfo.ulCharHeight;`
`rclWindow.yBottom = rclWindow.yTop-`
`liLineInfo.ulCharHeight;`

#### Enumerating Windows

`hwndFrame = WinQueryWindow(hwndWnd,`
`QW_PARENT);`
Remember, in the window procedure, hwndWnd is the client window, not the frame window. WinQueryWindow is used to find the parent of the client window, which in our case is the frame window. This is a very simple function that will be used many times. The first parameter is the handle of the window to query, and the second parameter indicates what information will be returned.

Table 9.8 WinQueryWindow Flags
Value | Description
QW_NEXT | Returns the window below the specified window.
QW_PREV | Returns the window above the specified window.
QW_TOP | Returns the topmost child window.
QW_BOTTOM | Returns the bottommost child window.
QW_OWNER | Returns the owner of the specified window.
QW_PARENT | Returns the parent of the specified window.
QW_NEXTTOP | Returns the next window of the owner window hierarchy.
QW_PREVTOP | Returns the previous window of the owner window hierarchy.
QW_FRAMEOWNER | Returns the owner of the specified window that also shares the same parent as the specified window.

`WriteWindowInfo(hpsPaint,`
`hwndFrame,`
`&rclWindow,`
`&liLineInfo);`

`heEnum = WinBeginEnumWindows(hwndFrame);`

`hwndEnum = WinGetNextWindow(heEnum);`

`while (hwndEnum != NULLHANDLE)`
`{`
`WriteWindowInfo(hpsPaint,`
`hwndEnum,`
`&rclWindow,`
`&liLineInfo);`
`hwndEnum = WinGetNextWindow(heEnum);`
`} /* end while hwndEnum */`
`WinEndEnumWindows(heEnum);`

Presentation Manager lets users query all the descendants of a particular window by using the functions WinBeginEnumWindows and WinGetNextWindow. The window that is the head of the window family tree is the frame window, hwndFrame.

> `HENUM WinBeginEnumWindows(HWND hwnd);`
> `HWND WinGetNextWindow(HENUM henum);`
> `BOOL WinEndEnumWindows(HENUM henum);`

This window handle is passed to WinBeginEnumWindows, which passes back an enumeration handle, heEnum. This is a place holder to keep track of the last window that was returned. WinGetNextWindow takes heEnum and returns the next window in the window family tree. As each window is found, our own function, WriteWindowInfo , is used to display information about the window. The enumeration ends with a call to WinEndEnum Windows.

#### Write WindowInofo

`WinQueryClassName(hwndPaint,`
`sizeof(achClass),`
`achClass);`

The first piece of information we'll retrieve from each window is the class name. Documentation refers to the system-defined class names as WC_FRAME and so on. However, the class name in reality, and returned by WinQueryClassName, is a string in the format "#1". Some help, huh? Public window class names are stored in powerful lookup tables known as atom tables. This format helps to cheek to see if a newly registered window class has the same name as one that is already registered. To convert from this cryptic format to something more readily deciphered, we define an array, pszClassNames, that maps the numeric class names to the documented class names. The string pszClass, returned from WinQueryClassName , is incremented by one to strip off the "#" and leave a value that can be converted to an integer index into the array.

If achClass is a nonnumeric value, we assume this to be an application-defined class, and keep the sting whole.

The second piece of information to retrieve is the parent and owner windows. WinQueryWindow is used to return the window handles of the parent and owner of the specified window. All this window detail is formatted into one string that will displayed on the client window by the user-function DrawString.

> `ulStyle = WinQueryWindowULong(hwndPaint,`
> `QWL_STYLE);`

The other information we will output to the client area is the window styles. This is a value that is stored in the window word. Presentation Manager stores a lot of window information in window words. The next example covers this concept in more detail, WinQueryWindowULong will retrieve the window styles. The first parameter is the window we're inquiring about. The next parameter is a constant used to identify which piece of the window word we're after. The value QWL_STYLE designates that the window style is the ULONG is question. The example converts these values to meaningful text string and uses the function DrawString to display the formatted string on the client area.

#### The DrawString function

Two functions will draw text on a window, WinDrawText and GpiCharString. WinDrawText is the more powerful function, that providing such features as positioning, coloured text, and word break. GpiCharString is much faster but leaves more more work for the programmer.

> `LONG APIENTRY WinDrawText(HPS hpsPaint,`
> `LONG lCount,`
> `PCH pchString,`
> `PRECTL prclRect,`
> `LONG lForeColor,`
> `LONG lBackColor,`
> `ULONG flCmd);`

We use the WinDrawText function in this example. The first parameter is the presentation space. The second parameter is the number of characters to output. A -1 indicates that the entire length of the null-terminated string is to be used. The string to write is pchString. The size of the text area is defined by passing a pointer to a RECTL structure that contains the designated coordinates. The next two parameters indicate the foreground and background color of the text. The last parameter is the formatting flag, a collection of formatting attributes that are ORed together. The attributes used in this example are DT_LEFT | DT_TEXTATTRS | DT_WORDBREAK. DTLEFT left-aligns the text horizontally, and DT_TEXTATTRS indicates that default window foreground and background colors will be used. If this flag is specified, the two previous parameters are ignored. The DT_WORDBREAK attribute will draw only the number of whole words that will fit inside the bounding rectangle. The number of characters drawn is returned. By enclosing this code in a loop and incrementing the string offset by the number of characters drawn, a very powerful routine that will be used often to print formatted text is created.

`while (!bFinished)`
`{`
`/* move down to next line */`
`usReturn = DropOneLine(prclRect,`
`pLineInfo->ulCharHeight);`
`/* if we can't move down any more, stop trying to write any more */`
`if (!usReturn)`
`return ;`

`/* set the left and right drawing coordinates */`
`prclRect->xLeft = pLineInfo->usxLeft;`
`prclRect->xRight = pLineInfo->usxRight;`

`/* draw text that will fit */`
`usNumChars = WinDrawText(hpsPaint,`
`strlen(&pString[usOffset]),`
`&pString[usOffset],`
`prclRect,`
`0,`
`0,`
`DT_LEFT|DT_TEXTATTRS|DT_WORDBREAK);`

`if (!usNumChars || (usOffset+usNumChars == usStringLength))`
`/* if no characters were printed, or we are at the end of the string, quit */`
`bFinished = TRUE;`

`else`

`/* offset string to new position */`
`usOffset += usNumChars;`
`}`
There is one last short function to explain, DropOneLine. This is a user function that will take a pointer to a RECTL structure and decrement the top and bottom y coordinates by the height of one line.

#### Presentation Spaces

A presentation space is similar so an artist's canvas, It is the space where the application draws. However, a presentation space does not have to be a window. It could also be a printer or even some piece of memory. In reality, a presentation space is a data structure, but to the programmer it is the drawing area. There are two types of presentation spaces - a normal presentation space and a micropresentation space. A micropresentation space is designed to have output to only one source. A normal presentation space can be shared between multiple devices. For instance, to print some copy of the video display, a normal presentation space would be used. A normal presentation space uses more memory than a micropresentation space and is slower; however, it is the most powerful presentation space type available.
There are two types of micropresentation spaces - standard and cached. A microcached presentation space is used for the video display and is maintained by Presentation Manager. A microcached presentation space is faster than the other presentation spaces and uses less memory. A microstandard presentation space is used to send output to a printer or any other output device. However, it cannot send output to more than one device at a time.
Presentation Manager controls how much of a window actually belongs in the presentation space. For example, if another window is covering most of a window, who should be able to draw on the intersection of the two windows ? The normal answer is the window with the highest value in the Z-order. There are a few exceptions to this rule.

- WS_CLIPCHILDREN If a window has this style, when the child window overlaps the parent, the parent window cannot draw on any part of the child's window. Normally, a child has a higher place in the Z-order than the parent, anyway, and this style is not necessary.
- WS_CLIPSIBLINGS When two windows share the same parent, this style will omit a sibling's presentation space from that of the other sibling. This style can be used to make sure one sibling always "comes out on top."
- WS_PARENTCLIP This gives a child window the ability to draw on its parent This style can be potentially dangerous, esthetically speaking, because the parent's presentation space is larger than the child's space. However somebody must have had a use for it somewhere.

[ Next ] [ Previous ] | Chapter 9 - II | [ Contents ] [ Chapter 8: Interfacing with OS/2 Devices ] [ Chapter 10: Window Management ]